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.
Related
I have this variadic struct for determining the sum of the sizeof all the types passed in:
template <typename U, typename... T> struct TotalSizeOf
: std::integral_constant<size_t, sizeof(U) + TotalSizeOf<T...>::value> {};
template <typename U> struct TotalSizeOf<U>
: std::integral_constant<size_t, sizeof(U)> {};
Usage:
TotalSizeOf<double, int, char>::value
The question is, how do I modify this to allow it to work on an empty parameter pack, to return 0;
e.g. TotalSizeOf<>::value
Currently, I get the error error: wrong number of template arguments (0, should be at least 1)
I only have C++14 available.
You simply must specialize also for <>
Example:
template < typename... T> struct TotalSizeOf;
template < typename U, typename... T> struct TotalSizeOf<U, T...>
: std::integral_constant<size_t, sizeof(U) + TotalSizeOf<T...>::value> {};
template <> struct TotalSizeOf<> :
std::integral_constant<size_t, 0 > { };
int main()
{
std::cout << TotalSizeOf< int, char>::value << std::endl;
std::cout << TotalSizeOf< char>::value << std::endl;
std::cout << TotalSizeOf< >::value << std::endl;
}
With C++17 you can get this without elaborate template metaprogramming, using fold expressions:
#include <iostream>
#include <type_traits>
template<class... T>
struct TotalSizeOf: std::integral_constant<std::size_t, (0 + ... + sizeof(T))> {};
int main()
{
std::cout << TotalSizeOf< int, char>::value << std::endl;
std::cout << TotalSizeOf< char>::value << std::endl;
std::cout << TotalSizeOf< >::value << std::endl;
}
This should also be more efficient when compiling (of course at runtime, these are the same).
PS: Just read, that you only have C++14, but will let this stand here, since I think it is nice to see, that we are less forced to do awkward TMP in newer C++ versions.
Addendum: Less elegant than C++17, but C++14 and pretty much tmp-free
#include <iostream>
#include <type_traits>
#include <initializer_list>
constexpr size_t sum(std::initializer_list<size_t> arr) {
// Accumulate is sadly not constexpr in C++14
auto ret = 0ul;
for(auto i: arr) {
ret += i;
}
return ret;
}
template<class... T>
struct TotalSizeOf: std::integral_constant<std::size_t, sum({sizeof(T)...})> {};
int main()
{
std::cout << TotalSizeOf< int, char>::value << std::endl;
std::cout << TotalSizeOf< char>::value << std::endl;
std::cout << TotalSizeOf< >::value << std::endl;
}
I want to check at compile-time if user literal _name is defined for type Ret and argument Arg. While I have half-solution, it requires the literal operator to be defined at least once:
#include <iostream>
#include <type_traits>
struct one { };
struct two { };
// we need at least one of these definitions for template below to compile
one operator"" _x(char const*) {return {};}
two operator"" _x(unsigned long long int) {return {};}
template<class T, class S, class = void>
struct has_literal_x : std::false_type
{ };
template<class T, class S>
struct has_literal_x <T, S,
std::void_t<decltype((T(*)(S))(operator"" _x))>
> : std::true_type
{ };
int main()
{
std::cout << has_literal_x<one, char const*>::value << std::endl;
std::cout << has_literal_x<two, unsigned long long int>::value << std::endl;
std::cout << has_literal_x<one, unsigned long long int>::value << std::endl;
std::cout << has_literal_x<two, char const*>::value << std::endl;
std::cout << has_literal_x<int, char const*>::value << std::endl;
}
Output:
1
1
0
0
0
But if there isn't at least one definition of possibly overloaded user literal, this solution will not work. Is there any way to check it even for non-existing literals (possibly the same way we can check if class X has member member, but I don't know if it's viable in this case)?
Is it possible to check if an user literal is defined for given type and argument?
The (short) answer is yes.
As an example, you can use the following specialization in your example code:
template<class T, class S>
struct has_literal_x <T, S,
std::enable_if_t<std::is_same<decltype(operator""_x(std::declval<S>())), T>::value>
> : std::true_type
{ };
That quickly becomes:
#include <iostream>
#include <type_traits>
#include <utility>
struct one { };
struct two { };
//one operator"" _x(char const*) { return {}; }
//two operator"" _x(unsigned long long int) { return {}; }
template<class T, class S, class = void>
struct has_literal_x : std::false_type
{ };
template<class T, class S>
struct has_literal_x <T, S,
std::enable_if_t<std::is_same<decltype(operator""_x(std::declval<S>())), T>::value>
> : std::true_type
{ };
int main()
{
std::cout << has_literal_x<one, char const*>::value << std::endl;
std::cout << has_literal_x<two, unsigned long long int>::value << std::endl;
std::cout << has_literal_x<one, unsigned long long int>::value << std::endl;
std::cout << has_literal_x<two, char const*>::value << std::endl;
std::cout << has_literal_x<int, char const*>::value << std::endl;
}
The output is the expected one: 0 for all of them.
Another way to do that in C++14 (mostly inspired by this answer of #Jarod42) is by means of a template variable.
As an example:
template<typename T, typename S, typename = void>
constexpr bool has_literal_v = false;
template<typename T, typename S>
constexpr bool has_literal_v<T, S, std::enable_if_t<std::is_same<decltype(operator""_x(std::declval<S>())), T>::value>> = true;
The main would become instead:
int main()
{
std::cout << has_literal_v<one, char const*> << std::endl;
std::cout << has_literal_v<two, unsigned long long int> << std::endl;
std::cout << has_literal_v<one, unsigned long long int> << std::endl;
std::cout << has_literal_v<two, char const*> << std::endl;
std::cout << has_literal_v<int, char const*> << std::endl;
}
I find it easy to read and that's a constexpr variable. What else?
With is_detected functions family, you may just do
template <typename T>
using has_literal_x_type = decltype(operator"" _x(std::declval<T>()));
template <typename Ret, typename T>
using has_literal_x = std::is_same<Ret, detected_t<has_literal_x_type, T>>;
And test it with
static_assert(!has_literal_x<one, char const*>::value, "unexpected");
static_assert(!has_literal_x<one, unsigned long long int>::value, "unexpected");
static_assert(!has_literal_x<two, char const*>::value, "unexpected");
static_assert(!has_literal_x<two, unsigned long long int>::value, "unexpected");
static_assert(!has_literal_x<int, char const*>::value, "unexpected");
Demo
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 have written a function to apply a function to a std::tuple as below (based on "unpacking" a tuple to call a matching function pointer).
I am concerned that the tuples might be copied around. I have a very basic idea of what move semantics does, and understand concepts like && and rvalue in the string examples commonly found. But I don't know much about how std::forward() and the likes work. And I am not sure how to handle it when there is also packing and variadic programming. (I added a few std::forward and &&'s around and soon get compilation errors.)
Can someone please explain how to make move semantics work for the tuples here? One additional question is, how can I verify (except for visual inspection of code) that move semantic indeed works for the tuples in the code?
Thanks in advance.
#include <tuple>
#include <iostream>
#include <functional>
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
template <typename R, typename Tp, typename ...FArgs>
struct t_app_aux {
template<int ...S>
R static callFunc(std::function<R (FArgs...)> f,Tp t,seq<S...>) {
return f(std::get<S>(t) ...);
}
};
template <typename R, typename Tp, typename ...FArgs>
R t_app(std::function<R (FArgs...)> f, Tp t) {
static_assert(std::tuple_size<Tp>::value == sizeof...(FArgs), "type error: t_app wrong arity");
return t_app_aux<R, Tp, FArgs...>::callFunc(f,t,typename gens<sizeof...(FArgs)>::type());
}
int main(void)
{
std::tuple<int, float, double> t = std::make_tuple(1, 1.2, 5);
std::function<double (int,float,double)> foo1 = [](int x, float y, double z) {
return x + y + z;
};
std::cout << t_app(foo1,t) << std::endl;
}
There are copies with your current implementation: http://ideone.com/cAlorb
I added a type with some log:
struct foo
{
foo() : _value(0) { std::cout << "default foo" << std::endl; }
foo(int value) : _value(value) { std::cout << "int foo" << std::endl; }
foo(const foo& other) : _value(other._value) { std::cout << "copy foo" << std::endl; }
foo(foo&& other) : _value(other._value) { std::cout << "move foo" << std::endl; }
int _value;
};
And also before/after your application:
std::cout << "Function created" << std::endl;
std::cout << t_app(foo1,t) << std::endl;
std::cout << "Function applied" << std::endl;
It gives:
Function created
copy foo
copy foo
7.2
Function applied
So then, to fix this adding forward is done like this:
template <typename R, typename Tp, typename ...FArgs>
struct t_app_aux {
template<int ...S>
R static callFunc(std::function<R (FArgs...)> f, Tp&& t, seq<S...>) {
return f(std::get<S>(std::forward<Tp>(t)) ...);
}
};
template <typename R, typename Tp, typename ...FArgs>
R t_app(std::function<R (FArgs...)> f, Tp&& t)
{
static_assert(std::tuple_size<typename std::remove_reference<Tp>::type>::value == sizeof...(FArgs),
"type error: t_app wrong arity");
return t_app_aux<R, Tp, FArgs...>::callFunc(f, std::forward<Tp>(t), typename gens<sizeof...(FArgs)>::type());
}
As you can see it removes unwanted copies: http://ideone.com/S3wF6x
Function created
7.2
Function applied
The only problem was to handle the static_assert because std::tuple_size was called on a std::tuple<>& and it did not work. I used typename std::remove_reference<Tp>::type but maybe there is a clever and more universal way ?
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;