Another day, another experiment with template meta-programming that's gone awry. I'm attempting to make an is_greater_than template that will take in two integral values N and M of type T.
template<typename T, T N, T M>
struct is_greater_than<void, N, M>;
template<typename T = std::enable_if<std::is_integral<T>::value, T>::value, T N, T M>
struct is_greater_than<T, N, M>
{
static const bool value = N > M;
};
Try as I might I can't seem to get this to work. Attempting to compile this yields 112 compiler errors. I have an ideone fiddle here: http://ideone.com/ch1j7b.
What am I doing wrong here? Any help would be appreciated!
Your usage of std::enable_if is wrong, it should be a separate template parameter (possibly unnamed):
#include <iostream>
#include <type_traits>
template<typename T, T N, T M,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
struct is_greater_than:
public std::integral_constant<bool, (N > M)>::type
{
};
int main()
{
std::cout << is_greater_than<int, 1, 2>::value
<< is_greater_than<int, 1, 1>::value
<< is_greater_than<int, 2, 1>::value;
}
Note that I'm inheriting fromstd::integral_constant here, it'll define the value and type members for us based in its second argument (parenthesis around N > M are required).
Try this:
template<typename T, T N, T M, typename enable = void>
struct is_greater_than;
template<typename T, T N, T M>
struct is_greater_than<T,N,M,
typename std::enable_if<std::is_integral<T>::value>::type>
{
static const bool value = N > M;
};
int main()
{
bool a = is_greater_than<int, 11, 10>::value;
cout << boolalpha << a << endl;
}
Output:
true
Live code
This compiles fine in VS2010:
template<typename T, T M, T N>
struct is_greater_than;
template<typename T = std::enable_if< std::is_integral<T>::value, T >::value, T M = T(), T N = T()>
struct is_greater_than {
static const bool value = M > N;
};
You can test it with:
std::cout << is_greater_than<int,4,2>::value << std::endl;
std::cout << is_greater_than<std::string,"a","B">::value << std::endl;
Related
Is there a way (in C++17) to achieve something similar to a forward declare, in a template?
What I want to achieve is something like this:
template<typename T, SizeType D, typename SizeType = int>
Obviously here D depends on SizeType, so it must come before it. But in that case I cannot set a default parameter unless D also has a default parameter (which I do not want). Basically I want to be able to "declare" SizeType before D, but "define" it after it.
Edit: Here is an example of how I would like to use it:
template<typename T, SizeType D, typename SizeType = int>
class StaticArray{};
//...
StaticArray<float, 5> s; // = StaticArray<float, 5, int>
StaticArray<float, (1<<40), size_t>; // 1<<40 doesn't fit in int
You could do something like this
template<typename T, auto DArg, typename SizeType = int, SizeType D = DArg>
Now the argument for D is provided first, then the type of D, and finally the argument is converted to D of the correct type.
I came up with something else considering StoryTeller's answer and the comments:
template<typename T, auto D>
Minimal solution example for future readers:
#include <iostream>
template<typename T, auto D>
class StaticArray
{
public:
using size_type = decltype(D);
};
int main()
{
StaticArray<float, 5ul> s;
std::cout << typeid(decltype(s)::size_type).name() << "\n";
return 0;
}
Here is also an example illustrating "forced conversion", and what it entails:
#include <iostream>
template<typename T, auto D, typename SizeType = decltype(D)>
class StaticArray
{
public:
using size_type = SizeType;
static const size_type convertedDim = static_cast<size_type>(D);
static const decltype(D) Dim = D;
};
int main()
{
StaticArray<float, 1ull<<40, int> s;
std::cout << typeid(decltype(s.Dim)).name() << "\n";
std::cout << s.Dim << "\n";
std::cout << typeid(decltype(s.convertedDim)).name() << "\n";
std::cout << s.convertedDim << "\n";
return 0;
}
I'm now learning a little about templates and templates in C++11, C++14 and C++1z. I'm trying to write a variadic class template with an inside class that will associate an int to every template argument - and have a constexpr method that returns its array representation.
Let's say that I have ensured that the template cannot receive two of the same type as an argument. I was thinking about doing it somewhat like this:
template <typename... Types>
struct MyVariadicTemplate {
//we know that all types in Types... are different
template <int... Values>
struct MyInnerTemplate {
//I need to make sure that sizeof...(Values) == sizeof...(Types)
constexpr std::array<int, sizeof...(Values)> to_array() {
std::array<int, sizeof...(Values)> result = {Values...};
return result;
// this is only valid since C++14, as far as I know
}
};
};
this code should be valid (if it's not, I'd love to know why). Now, I'd like to add another inner template:
template <typedef Type>
struct AnotherInnerTemplate {};
that has a public typedef, which represents MyInnerTemplate with one on the position of Type in Types... and zeros elsewhere - and here I'm lost. I don't know how to proceed
I would appreciate any hint on how that can be done - and if I'm heading towards the wrong direction, I hope somebody can give me a hint on how to do that.
I think what you're looking for is something like this.
#include <array>
#include <cstddef>
#include <iostream>
#include <type_traits>
template <typename NeedleT, typename... HaystackTs>
constexpr auto get_type_index_mask() noexcept
{
constexpr auto N = sizeof...(HaystackTs);
return std::array<bool, N> {
(std::is_same<NeedleT, HaystackTs>::value)...
};
}
template <typename T, std::size_t N>
constexpr std::size_t ffs(const std::array<T, N>& array) noexcept
{
for (auto i = std::size_t {}; i < N; ++i)
{
if (array[i])
return i;
}
return N;
}
int
main()
{
const auto mask = get_type_index_mask<float, bool, int, float, double, char>();
for (const auto& bit : mask)
std::cout << bit;
std::cout << "\n";
std::cout << "float has index " << ffs(mask) << "\n";
}
Output:
00100
float has index 2
The magic happens in the parameter pack expansion
(std::is_same<NeedleT, HaystackTs>::value)...
where you test each type in HaystackTs against NeedleT. You might want to apply std::decay to either type if you want to consider, say, const int and int the same type.
template <int size, int... Values> struct AnotherImpl {
using Type = typename AnotherImpl<size - 1, Values..., 0>::Type;
};
template <int... Values> struct AnotherImpl<0, Values...> {
using Type = Inner<Values...>;
};
template <class T> struct Another {
using Type = typename AnotherImpl<sizeof...(Types) - 1, 1>::Type;
};
Full:
template <class... Types> struct My {
template <int... Values> struct Inner {
constexpr std::array<int, sizeof...(Values)> to_array() {
return std::array<int, sizeof...(Values)>{Values...};
}
};
template <int size, int... Values> struct AnotherImpl {
using Type = typename AnotherImpl<size - 1, Values..., 0>::Type;
};
template <int... Values> struct AnotherImpl<0, Values...> {
using Type = Inner<Values...>;
};
template <class T> struct Another {
using Type = typename AnotherImpl<sizeof...(Types) - 1, 1>::Type;
};
};
auto main() -> int {
My<int, float, char>::Another<int>::Type s;
auto a = s.to_array();
for (auto e : a) {
cout << e << " ";
}
cout << endl;
return 0;
}
prints:
1 0 0
Is this what you want?
Consider this illegal code:
template <int... Is>
struct Object {
void foo() const;
};
template <int... Js>
void Object<0, Js...>::foo() {/*Do whatever*/}
We want to specialize foo() when the first template parameter is 0, and let's say we want to specialize foo() as well if the second parameter is 3, and the third int is 1. So the solution I found (not sure if its the best approach) is the following:
#include <iostream>
template <int...> struct Foo;
template <int... Is>
struct Object {
int ID; // This member is just to illustrate the case when 'this' is needed in foo().
friend struct Foo<Is...>;
void foo() const {Foo<Is...>::execute(this);} // Pass 'this' in case it is needed.
};
template <int... Is>
struct Foo<0, Is...> {
static void execute (const Object<0, Is...>* object) {std::cout << "First int = 0, ID = " << object->ID << ".\n";}
};
template <int N, int... Is>
struct Foo<N, 3, Is...> {
static void execute (const Object<N, 3, Is...>* object) {std::cout << "Second int = 3, ID = " << object->ID << ".\n";}
};
template <int M, int N, int... Is>
struct Foo<M, N, 1, Is...> {
static void execute (const Object<M, N, 1, Is...>* object) {std::cout << "Third int = 1, ID = " << object->ID << ".\n";}
};
int main() {
Object<0,5,8,2>{4}.foo();
Object<4,3,2,5,3>{2}.foo();
Object<4,2,1>{0}.foo();
}
First of all, is this solution any good? Next, the problem now arises if we try Object<0,3,1,4>{8}.foo(); because the spec was not complete. So let's say that the earliest matched specialized int will always take precedence. So in this case Object<0,3,1,4>{8}.foo(); should run the first specialization because of the 0, while Object<9,3,1,4>{8}.foo(); shall run the second specialization because of the 3, and so forth. How to enforce that rule?
I suggest just using if statements. The compiler will probably optimize them away anyway (assuming you have optimization enabled).
In other words, just do something like this:
template <int... Js>
void Object::foo() {
std::array<int, sizeof...(Js)> args = {Js...}; // I _think_ this is the correct syntax to dump the parameter pack into an std::array.
if(args.size() > 0 && args[0] == 0) {
// First argument is 0, do whatever.
} else {
// It's not 0, do your other thing.
}
}
You'll get pretty much the same effect, and your code will be quite a bit clearer.
A comment and a hint.
The approach for me is OK. Since we do not have partial template specialization for functions that's all we have.
Then regarding Object<0,3,1,4>{8}.foo() this gives ambiguous partial specializations (on Clang 3.6). To solver this problem I ended up adding another partial specialization
template <int... Is>
struct Foo<0, 3, Is...> {
static void execute (const Object<0, 3, Is...>* object) {std::cout << "First int = 0, second = 3, ID = " << object->ID << ".\n";}
};
Another possibility is mess with std::integer_sequence. I have to give up now, the following is not a solution, just an appetizer...
#include <utility>
#include <iostream>
template <class S1, class S2>
struct seq_lt
{
enum {value = 0} ;
} ;
template <int I1, int ...S1, int I2, int ...S2>
struct seq_lt<std::integer_sequence<int, I1, S1...>,
std::integer_sequence<int, I2, S2...>>
{
enum {value = (I1 < I2 ? 1 : 0)} ;
} ;
int main(int argc, char *argv[])
{
std::integer_sequence<int, 1, 2, 3> seq1 ;
std::integer_sequence<int, 2, 3> seq2 ;
std::cout << "seq_lt " << seq_lt<decltype(seq1), decltype(seq2)>::value << std::endl ;
std::cout << "seq_lt " << seq_lt<decltype(seq2), decltype(seq1)>::value << std::endl ;
}
This solution was inspired by Marom's second suggestion, and also inspired partly by celticminstrel's solution too.
#include <iostream>
#include <type_traits>
template <std::size_t, typename T, T...> struct NthValue;
template <typename T, T First, T... Rest>
struct NthValue<0, T, First, Rest...> : std::integral_constant<T, First> {};
template <std::size_t N, typename T, T First, T... Rest>
struct NthValue<N, T, First, Rest...> : NthValue<N - 1, T, Rest...> {};
template <int... Is>
struct Object {
void foo() const {fooHelper (typename Map<Is...>::type{});}
private:
template <int...> struct Map;
template <int, int> struct MappedType {};
struct Default {};
void fooHelper (const MappedType<0,0>&) const {std::cout << "First int = 0.\n";}
void fooHelper (const MappedType<1,3>&) const {std::cout << "Second int = 3.\n";}
void fooHelper (const MappedType<2,1>&) const {std::cout << "Third int = 1.\n";}
void fooHelper (const Default&) const {std::cout << "Default case.\n";}
};
template <int... Ns>
template <int... Is>
struct Object<Ns...>::Map {
using type = typename std::conditional<NthValue<0, int, Is...>::value == 0,
MappedType<0,0>,
typename std::conditional<NthValue<1, int, Is...>::value == 3,
MappedType<1,3>,
typename std::conditional<NthValue<2, int, Is...>::value == 1,
MappedType<2,1>,
Default
>::type
>::type
>::type;
};
int main() {
Object<0,5,8,2>().foo(); // First int = 0.
Object<4,3,2,5,3>().foo(); // Second int = 3.
Object<4,2,1>().foo(); // Third int = 1.
Object<0,3,1,4>().foo(); // First int = 0.
Object<9,3,1,4>().foo(); // Second int = 3.
Object<9,9,9>().foo(); // Default case.
}
There is also no run-time overhead.
I'm attempting to make an index_type_<N> type that will return the type of the index based on N where N is the maximum number to the indices can go to.
So for example:
index_type_<32>::type index1; //uint8_t
index_type_<64000>::type index2; //uint16_t
index_type_<18446744073709551>::type index3; //uint64_t
I have the following code that refuses to compile and I can't determine the cause despite searching for Google on the error messages, none of them seem relevant to my situation.
#include <iostream>
template<size_t N, typename T = void>
struct index_type_;
template<size_t N>
struct index_type_<N, typename std::enable_if<N <= 256, uint8_t>::value>
{
typedef uint8_t type;
};
template<size_t N, typename T = void>
struct index_type_;
template<size_t N>
struct index_type_<N, typename std::enable_if<N <= 65536, uint16_t>::value>
{
typedef uint16_t type;
};
template<size_t N>
struct index_type_<N, typename std::enable_if<N <= 4294967296, uint32_t>::value>
{
typedef uint32_t type;
};
template<size_t N>
struct index_type_<N, typename std::enable_if<N <= 18446744073709551616ULL, uint64_t>::value>
{
typedef uint64_t type;
};
int main()
{
index_type_<32>::type index1;
index_type_<232122>::type index2;
index_type_<992532523>::type index3;
index_type_<4213662352328854>::type index4;
std::cout << "index1:" << sizeof(index1) << std::endl;
std::cout << "index2:" << sizeof(index2) << std::endl;
std::cout << "index3:" << sizeof(index3) << std::endl;
std::cout << "index4:" << sizeof(index4) << std::endl;
}
The errors and the sample code can be found here:
http://ideone.com/SJdKjr
Any help would be greatly appreciated, I feel I'm missing something obvious.
All your specializations are ambiguous. E.g. which one is the best choice of the following?
template <std::size_t N>
struct Foo {
// Specialization 1
};
template <std::size_t N>
struct Foo<N> {
// Specialization 2
};
int main() {
Foo<1> foo; // Error: partial specialization 'Foo<N>' does not
// specialize any template arguments.
}
Try something like this instead:
template <std::uintmax_t N>
struct index_type {
using type = std::conditional_t<N <= 255, std::uint8_t,
std::conditional_t<N <= 63535, std::uint16_t,
std::conditional_t<N <= 4294967295, std::uint32_t,
std::conditional_t<N <= 18446744073709551615ULL, std::uint64_t,
std::uintmax_t>>>>;
};
template <std::uintmax_t N>
using index_type_t = typename index_type<N>::type;
Usage:
index_type_t<64000> test; // unsigned int
Using a type list and recursive tests via numeric_limits:
#include <cstdint>
#include <limits>
#include <type_traits>
namespace detail
{
template<class T>
struct delay { using type = T; };
template<class T, T t, class... IntTypes>
struct select_int_type
{
static_assert(sizeof...(IntTypes) > 0,
"No integer type sufficiently big found.");
};
template<class T, T t, class Head, class... Tail>
struct select_int_type<T, t, Head, Tail...>
{
using type = typename std::conditional<
t <= std::numeric_limits<Head>::max()
, delay<Head>
, select_int_type<T, t, Tail...>
>::type::type;
};
}
template<std::uintmax_t N>
using select_uint_type =
typename detail::select_int_type<decltype(N), N,
std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t
, std::uintmax_t
>::type;
Usage example:
#include <iostream>
int main()
{
select_uint_type<32> index1;
select_uint_type<232122> index2;
select_uint_type<992532523> index3;
select_uint_type<4213662352328854> index4;
std::cout << "index1:" << sizeof(index1) << std::endl;
std::cout << "index2:" << sizeof(index2) << std::endl;
std::cout << "index3:" << sizeof(index3) << std::endl;
std::cout << "index4:" << sizeof(index4) << std::endl;
}
You can easily add signed types either via another alias template:
template<std::intmax_t N>
using select_int_type =
typename detail::select_int_type<decltype(N), N,
std::int8_t, std::int16_t, std::int32_t, std::int64_t
, std::intmax_t
>::type;
(I think make_signed is problematic here because of the selection based on a value)
I am trying to figure out which is the most idiomatic way implement a function over a variadic type list. For example, computing the maximum size of all the types. I understand there exist several approaches to accomplish such a task, but I would like to know when to choose which strategy.
These are the mechanisms I would consider (there may exist more, please mention if so):
Type traits (ideally succinctly with using declarations):
template <typename Head>
using max_size = typename std::integral_constant<size_t, sizeof(Head)>::type;
template <typename Head, typename... Tail>
using max_size = ?;
constexpr functions:
template <typename Head>
constexpr size_t max_size() { return sizeof(Head); }
template <typename Head, typename... Tail>
constexpr size_t max_size() { ? }
My question is twofold:
What features of the computation determine what strategy to choose?
In each case, how would an example implementation for the maximum size example described above look like?
I personally prefer functions rather than traits, I find them easier to manipulate and more natural. But that's certainly subjective ;)
#include <iostream>
template <typename Head>
constexpr size_t max_size() { return sizeof(Head); }
template <typename Head, typename Next, typename... Tail>
constexpr size_t max_size() {
return max_size<Head>() > max_size<Next, Tail...>() ?
max_size<Head>() : max_size<Next, Tail...>();
}
int main() {
std::cout << "int: " << max_size<int>() << "\n";
std::cout << "char, short, int: " << max_size<char, short, int>() << "\n";
std::cout << "char, double, int: " << max_size<char, double, int>() << "\n";
}
In action at liveworkspace:
int: 4
char, short, int: 4
char, double, int: 8
I would stay away from strictly using constexpr as they are harder to compose. For instance, I'm not even sure high-order metafunctions are possible with constexpr, if so they use function pointers as template parameters which is ugly.
In general, I start with a metafunction class:
struct max_size {
template<typename... Ts>
struct apply : parampack_foldl::apply<boost::mpl::quote2<boost::mpl::max>, typename boost::mpl::sizeof_<Ts>::type...>;
};
Then create an alias to lessen typing:
template<typename... Ts>
using MaxSize = typename max_size::apply<Ts>::type;
Or, create a constexpr function:
template <typename... Ts>
constexpr size_t max_size() { return max_size::apply<Ts...>::type::value; }
The second step is really just a matter of style, what really matters is that you have the first which gives you the most to work with.
For completeness, there is also the technique of inheritance:
#include <cstddef>
using std::size_t;
template<size_t ...S> struct max_size_t;
template<size_t S1, size_t S2, size_t ...Rest>
struct max_size_t<S1, S2, Rest...>
: max_size_t<(S2 < S1) ? S1 : S2, Rest...> {
};
template<size_t S>
struct max_size_t<S> {
static const int value = S;
};
template<> struct max_size_t<> {
static const int value = 0;
};
template<typename ...T> struct max_size : max_size_t<sizeof(T)...> {};
// Using the same test-harness as Matthieu M:
#include <iostream>
int main() {
std::cout << "int: " << max_size<int>::value << "\n";
std::cout << "char, short, int: " << max_size<char, short, int>::value << "\n";
std::cout << "char, double, int: " << max_size<char, double, int>::value << "\n";
return 0;
}
Also at liveworkspace.
Although this is not a way I'd choose to implement max_size, it's very handy when the desired function is one which returns a type (highly contrived example follows):
template<typename T1, typename T2, bool B=(sizeof(T1)>sizeof(T2))> struct selector;
template<typename T1, typename T2> struct selector<T1, T2, true> { using type = T1; };
template<typename T1, typename T2> struct selector<T1, T2, false> { using type = T2; };
template<typename T1, typename ...Rest> struct largest_type;
template<typename T1, typename T2, typename ...Rest>
struct largest_type<T1, T2, Rest...>
: largest_type<typename selector<T1, T2>::type, Rest...> {};
template<typename T1> struct largest_type<T1> { using type = T1; };
#include <iostream>
int main() {
static const unsigned long long u = 1ULL << 63;
std::cout << "int: " << typename largest_type<int>::type(u) << "\n";
std::cout << "int, double: " << typename largest_type<int, double>::type(u) << "\n";
std::cout << "short, float, long long: " << typename largest_type<short, float, long long>::type(u) << "\n";
return 0;
}
See it here.