Is there a way to automatically select between multiple non-template functions based on a template parameter?
Example:
class Aggregate
{
public:
std::string asString();
uint32_t asInt();
private:
// some conglomerate data
};
template <typename T>
T get(Aggregate& aggregate)
{
// possible map between types and functions?
return bind(aggregate, typeConvert[T])(); ??
// or
return aggregate.APPROPRIATE_TYPE_CONVERSION();
}
The solution would be nice to throw a compiler error if there is no good conversion available, i.e.
get<double>(aggregate); // compile error
I do not want to use template specialization, i.e
template<>
int get(Aggregate& aggregate)
{
return aggregate.asInt();
}
because it leads to code duplication when your get() function has more then one line of code
The pedestrian way is to define each possible option separately:
template <typename T> T get(Aggregate &); // leave undefined
template <> uint32_t get(Aggregate & a) { return a.asInt(); }
// ...
Absent any more systematic structure that encodes which function serves which conversion, I think this is the best you can do. It may be worth redefining Aggregate, though, to be more introspectible.
You may do something like (require C++11) : (https://ideone.com/UXrQFm)
template <typename T, typename... Ts> struct get_index;
template <typename T, typename... Ts>
struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename Tail, typename... Ts>
struct get_index<T, Tail, Ts...> :
std::integral_constant<std::size_t, 1 + get_index<T, Ts...>::value> {};
template <typename T, typename Tuple> struct get_index_in_tuple;
template <typename T, typename ... Ts>
struct get_index_in_tuple<T, std::tuple<Ts...>> : get_index<T, Ts...> {};
class Aggregate
{
public:
std::string asString();
uint32_t asInt();
private:
// some conglomerate data
};
template <typename T>
T get(Aggregate& aggregate)
{
using types = std::tuple<uint32_t, std::string>;
auto funcs = std::make_tuple(&Aggregate::asInt, &Aggregate::asString);
return (aggregate.* (std::get<get_index_in_tuple<T, types>::value>(funcs)))();
}
Related
I am new to template meta programming and was trying to create a program that would find if a parameter pack has consecutive same type names. For example <int, int>, <int, char, char> would return true and <int,char> and <int, char, int> would not.
I managed to write this piece of code but it seems to be comparing each value of parameter pack with itself. I am just looking for a way to iterate through the values of parameter pack to compare with it's consecutive element.
template<typename T, typename U>
struct sameTypename{
enum {value = false};
};
template<typename T>
struct sameTypename<T, T>{
enum {value = true};
};
template <typename T, typename ...args>
struct consTypename{
enum {value = (sameTypename<consTypename<args...>, consTypename<args...>>::value)};
};
template <typename T>
struct consTypename<T, T>{
enum {value = true};
};
template <typename T>
struct consTypename<T>{
enum {value = false};
};
Here you go:
#include <type_traits>
template <typename ...P> struct has_adjacent_same_types : std::false_type {};
template <typename A, typename B, typename ...P> struct has_adjacent_same_types<A, B, P...>
: std::bool_constant<std::is_same_v<A,B> || has_adjacent_same_types<B, P...>::value> {};
I used : std::false_type {}; and : std::bool_constant<X> {}; instead of
{enum{value = false};}; and {enum{value = X};}; respectively, but that's simply a matter of preference.
Some of the features I used are from C++17. If you're using an older version, note that:
std::bool_constant and std::is_same_v are available only starting from C++17 (but that you can use std::integral_constant and std::is_same<>::value before).
(c) #max66
A variation of the HolyBlackCat's solution.
template <typename ...>
struct has_adjacent_same_types : public std::false_type
{ };
template <typename T0, typename ... Ts>
struct has_adjacent_same_types<T0, T0, Ts...> : public std::true_type
{ };
template <typename T0, typename T1, typename ... Ts>
struct has_adjacent_same_types<T0, T1, Ts...>
: public has_adjacent_same_types<T1, Ts...>
{ };
Two simpler specializations instead of only one, more complex.
Substantially is the same things (I suppose) but I find it a little clear to read and understand.
I propose also a completely different solution that uses template folding (so only C++17 or newer, unfortunately) instead of template recursion.
template <typename...>
struct sae_helper;
template <typename ... Ts, typename ... Us>
struct sae_helper<std::tuple<Ts...>, std::tuple<Us...>>
: public std::bool_constant<(std::is_same_v<Ts, Us> || ...)>
{ };
template <typename ... Ts>
struct some_adjacent_equal
: public sae_helper<std::tuple<void, Ts...>, std::tuple<Ts..., void>>
{ };
If void is a possible type in the list of type to check, calling sae_helper from some_adjacent_equal instead of void must be used a different type, obviously.
I suppose that this solution is preferable, over a recursive one, when the list of types is very long because avoid compilers template-recursion limits.
If you can use C++14, you can use a constexpr function instead of template folding (and a tag-type instead of void) as follows
template <typename ... Ts, typename ... Us>
constexpr bool sae_helper (std::tuple<Ts...> const &,
std::tuple<Us...> const &)
{
using unused = bool[];
bool ret { false };
(void)unused { true, ret |= std::is_same<Ts, Us>::value... };
return ret;
}
struct no_type
{ };
template <typename ... Ts>
struct some_adjacent_equal
: public std::integral_constant<bool, sae_helper(std::tuple<no_type, Ts...>{},
std::tuple<Ts..., no_type>{})>
{ };
but, this way, you loose short-circuiting in or evaluation.
I have the following code:
template <template <class...> class Temp, class Specialization>
struct IsSpecialization : std::false_type {};
template <template <typename...> class Temp1,
template <typename...> class Temp2, typename... Ts>
struct IsSpecialization<Temp1, Temp2<Ts...>>
: std::is_same<Temp1<Ts...>, Temp2<Ts...>> {};
struct ExprKindMerge {};
struct ExprKindSequence {};
template <class Tag, class... Args>
struct Expr {
std::tuple<Args...> tup;
constexpr std::tuple<Args...> toStdTuple() const {
return this->tup;
}
constexpr std::size_t size() const noexcept {
return std::tuple_size<decltype(tup)>{};
}
};
template <class...Args>
using MergeExpr = Expr<ExprKindMerge, Args...>;
template <class...Args>
using SequenceExpr = Expr<ExprKindSequence, Args...>;
And this function, which sometimes receives a SequenceExpr<Something> as the template parameter:
template <class FullExpr>
auto f(FullExpr expr) {
///**************THIS RETURNS FALSE I EXPECT TRUE
///Type of full expr is Expr<ExprKindSequence, ...> which is
/// the expansion of SequenceExpr<...>
if constexpr (IsSpecialization<SequenceExpr, FullExpr>::value)
//...
}
I want to be able to detect if FullExpr is a specialization of SequenceExpr but it fails for some unknown reason.
Why it fails is easy. For a SequenceExpr<ExtraArgs...>, Temp2 is deduced to be Expr and Ts... is deduced to be ExprKindSequence, ExtraArgs.... Then Temp1<Ts...> is Expr<ExprKindSequence, ExprKindSequence, ExtraArgs...> and is obviously not the same as Temp2<Ts...>.
I know of no fully generic way to do this. After all, such a hypothetical template would presumably need to return true for IsSpecialization<std::remove_reference_t, int>...
If we limit it to alias templates that use their parameters in deducible contexts (which is the case in your example), then one possible way is to ask the question "Can I deduce the Ts... in Temp<Ts...> from Specialization?":
namespace detail {
template<class> class type {};
template<template<class...> class Temp, class...Ts>
void try_deduce(type<Temp<Ts...>>);
}
template <template <class...> class, class, class = void>
struct IsSpecialization : std::false_type {};
template <template <typename...> class Temp, class Specialization>
struct IsSpecialization<Temp, Specialization,
decltype(detail::try_deduce<Temp>(detail::type<Specialization>()))>
: std::true_type {};
static_assert(IsSpecialization<SequenceExpr, SequenceExpr<int>>()());
static_assert(IsSpecialization<Expr, SequenceExpr<int>>()());
static_assert(!IsSpecialization<MergeExpr, SequenceExpr<int>>()());
static_assert(!IsSpecialization<SequenceExpr, MergeExpr<int>>()());
If your interested only in detecting specializations of SequenceExpr, the best I can imagine is to add a specialization of IsSpecialization defined as follows
template <typename... Ts>
struct IsSpecialization<SequenceExpr, Expr<ExprKindSequence, Ts...>>
: std::true_type
{ };
Obviously this isn't a general solution that I don't think it's possible.
Take in count that if you call f with a (by example) Expr<ExprKindSequence, int, long> value
f(Expr<ExprKindSequence, int, long>{}); // result true !!!
the specialization of IsSpecialization above match and you get true; I don't know if is what do you want.
The following is a full working example
#include <tuple>
#include <iostream>
#include <type_traits>
template <template <typename...> typename Temp, typename Specialization>
struct IsSpecialization : std::false_type
{ };
template <template <typename...> class Temp1,
template <typename...> class Temp2, typename... Ts>
struct IsSpecialization<Temp1, Temp2<Ts...>>
: std::is_same<Temp1<Ts...>, Temp2<Ts...>>
{ };
struct ExprKindMerge {};
struct ExprKindSequence {};
template <typename Tag, typename... Args>
struct Expr
{
std::tuple<Args...> tup;
constexpr std::tuple<Args...> toStdTuple () const
{ return this->tup; }
constexpr std::size_t size () const noexcept
{ return std::tuple_size<decltype(tup)>{}; }
};
template <typename ... Args>
using MergeExpr = Expr<ExprKindMerge, Args...>;
template <typename ... Args>
using SequenceExpr = Expr<ExprKindSequence, Args...>;
template <typename ... Ts>
struct IsSpecialization<SequenceExpr, Expr<ExprKindSequence, Ts...>>
: std::true_type
{ };
template <class FE>
auto f (FE expr)
{ std::cout << IsSpecialization<SequenceExpr, FE>::value << std::endl; }
int main ()
{
f(SequenceExpr<int, long>{}); // print 1
f(Expr<ExprKindSequence, int, long>{}); // print 1 (?)
f(Expr<int, long>{}); // print 0
f(int{}); // print 0
}
Would it be possible to implement a function member of a variadic template class that return the index of a given type from the variadic argument list.
The issue I see is to create some kind of fake variadic argument list, just to trigger the compile time template evaluation.
template<typename... TArgs>
class Foo
{
template<typename T, typename TArg>
int _get_idx(int i, const TArg &curr, TArgs...args)
{
if (std::is_same(T, TArg)) {
return i;
}
else {
return get_id(i+1, args...);
}
}
Usage would be something like:
Foo<A, B, C> foo;
int i = foo.get_idx<B>(); // i == 1
You may use something like:
template <typename T, typename... Ts> struct get_index;
template <typename T, typename... Ts>
struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename Tail, typename... Ts>
struct get_index<T, Tail, Ts...> :
std::integral_constant<std::size_t, 1 + get_index<T, Ts...>::value> {};
#if 1 // explicit error case, but you already have error without that.
template <typename T>
struct get_index<T>
{
// condition is always false, but should be dependant of T
static_assert(sizeof(T) == 0, "element not found");
};
#endif
Live example
Note: You don't specify what happens for duplicate matching type (So I take the first one),
nor if the type is not matched (so I did a compile time error)
Live Demo with duplicates
I have a template:
template<typename... Ts> //T1,T2,T3,...
struct foo {
//my struct
};
I want to do static_assert checks on T1,T3,T5,... (the "odd types") and on T2,T4,T6,... (the "even types") separately.
I have found this simple solution:
template<size_t N, typename... Ts>
struct perform_checks {};
template<size_t N, typename T, typename U, typename... Ts>
struct perform_checks<N, T, U, Ts...> : perform_checks<N, Ts...>
{
//check for odd types
static_assert(std::is_default_constructible<T>::value,"failure");
//check for even types
static_assert(std::is_copy_constructible<U>::value,"failure");
};
The N parameter allows it to end. I use it like this:
template<typename... Ts>
struct foo {
perform_checks<0,Ts...> hello;
};
This seems to be working fine. But is it possible to avoid the hello variable? I never use it for any other purpose.
Derive foo from perform_checks<> privately:
template <typename... Ts> struct foo : private perform_checks<Ts...> {
// stuff
};
Oh, and get rid of the N parameter that you don't need:
template <typename... Ts> struct perform_checks {};
template <typename T> struct perform_checks<T> {
template <typename U> struct dependent_name_hack : std::false_type {};
static_assert(dependent_name_hack<T>::value,
"Odd number of parameters not acceptable.");
};
template <typename T, typename U, typename... Ts>
struct perform_checks<T, U, Ts...> : perform_checks<Ts...> {
//check for odd types
static_assert(std::is_default_constructible<T>::value,"failure");
//check for even types
static_assert(std::is_copy_constructible<U>::value,"failure");
};
You can use enable_if1 (and boost::mpl) in more-or-less the following way:
#include <boost/mpl/and.hpp>
template<size_t N, typename... Ts>
struct perform_checks {};
template<size_t N, typename T, typename U, typename... Ts>
struct perform_checks<N, T, U, Ts...> : perform_checks<N, Ts...>
{
typedef boost::mpl::and_<std::is_default_constructible<T>::type,
std::is_copy_constructible<U>::type> type;
};
template < class... Ts,
class = typename std::enable_if<perform_checks<0, Ts...>::type>
struct foo {
//my struct
};
The only purpose of foo in the OP is triggering the check when it's instantiated. That's why you need the variable hello: it's an instantiation of foo.
I would rather follow the approach of traits in <type_traits>. More precisely, I would turn perform_checks into class (or struct) that has public static constexpt bool member called value which is true or false depending on whether the given types pass the test or not. Then I would use a single static_assert to stop compilation if value is false.
My solution, which assumes that the number of template type arguments is even, follows:
#include <type_traits>
template<typename First, typename Second, typename... Others>
struct perform_checks :
std::integral_constant<bool,
perform_checks<First, Second>::value && // Checks First and Second
perform_checks<Others...>::value // Recursively "calls" itself on Others
> {
};
// This specialization finishes the recursion and effectively performs the test
template<typename First, typename Second>
struct perform_checks<First, Second> :
std::integral_constant<bool,
std::is_default_constructible<First>::value && // Checks First
std::is_copy_constructible<Second>::value // Checks Second
> {
};
Here is a simple test:
struct NonDefaultConstructible {
NonDefaultConstructible() = delete;
};
struct NonCopyConstructible {
NonCopyConstructible(const NonCopyConstructible&) = delete;
};
int main() {
static_assert(perform_checks<int, double>::value, "Failure");
static_assert(perform_checks<int, int, double, double>::value, "Failure");
static_assert(!perform_checks<NonDefaultConstructible, int>::value, "Failure");
static_assert(!perform_checks<int, NonCopyConstructible>::value, "Failure");
static_assert(!perform_checks<int, int, double, NonCopyConstructible>::value, "Failure");
}
Notice that no variable was created.
Currently, I'm trying to get some code to react differently to different types. This isn't the exact code, but it gets the message across.
template<class A, class B>
struct alpha {
enum { value = 0 };
};
template<class T, class... Args>
struct alpha<std::tuple<Args...>, T> {
enum { value = 1 };
};
// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., std::vector<T> >, T> {
enum { value = 2 };
};
// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., T>, T> {
enum { value = 3 };
};
template<class T, class... Args>
struct alpha<T, std::tuple<Args...> > {
enum { value = 4 };
};
template<class... LArgs, class... RArgs>
struct alpha<std::tuple<LArgs...>, std::tuple<RArgs...> > {
enum { value = 5 };
};
int main(int argc, char* argv[]) {
std::cout << alpha<std::tuple<int, double>, double>::value << std::endl; // prints 1
return 0;
}
I've tried more than this code shows, but nothing works so far and I ran across a problem with explicit specialization in a non-namespace scope. For reference, I'm working on gcc 4.6 (the one that comes with oneiric server), which I believe has complete variadic template support. I don't care how ugly it gets if the implementation works to detect the last argument of the parameter pack and the other types as well. Any suggestions?
EDIT:
I wanted to share the solution I used based on the answers (this is an example).
template<typename T> struct tuple_last;
template<typename T, typename U, typename... Args>
struct tuple_last<std::tuple<T,U,Args...>> {
typedef typename tuple_last<std::tuple<U,Args...>>::type type;
};
template<typename T>
struct tuple_last<std::tuple<T>> {
typedef T type;
};
namespace details {
// default case:
template<class T, class U>
struct alpha_impl {
enum { value = 1 };
};
template<class T>
struct alpha_impl<T, T> {
enum { value = 101 };
};
template<class T>
struct alpha_impl<T, std::vector<T>> {
enum { value = 102 };
};
// and so on.
}
template<class T, class... Args>
struct alpha<std::tuple<Args...>, T>
: details::alpha_impl<T, tuple_last<std::tuple<Args...>>;
If you compile using clang, it helpfully reports that (2) and (3) are unusable. The warning for (3), which you expect to be selected, is as follows:
warning: class template partial specialization contains a template parameter that can not be deduced; this partial specialization will never be used
struct alpha<std::tuple<Args..., T>, T> {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: non-deducible template parameter 'Args'
template<class T, class... Args>
^
Why is Args not deducible? The C++0x FDIS states at ยง14.8.2.5/9:
If the template argument list of [a type that is specified in terms of template parameters] contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context.
In your specialization, the type std::tuple<Args..., T> is a type that is specified in terms of template parameters Args and T. It contains a pack expansion (Args...), but that pack expansion is not the last template argument (T is the last template argument). Thus, the entire template argument list of the tuple (the entirety of <Args..., T>) is a non-deduced context.
The argument list of the std::tuple is the only place in the template specialization's argument list that Args appears; since it is not deducible from there, it is not deducible at all and the specialization will never be used.
Matthieu M. provides a clever workaround in his answer.
#James provided the why, now let's try to find an alternative.
I would suggest using another level of indirection.
1. Getting the last argument
template <typename T> struct Last;
template <typename T, typename U, typename... Args>
struct Last<std::tuple<T,U,Args...>>
{
typedef typename Last<std::tuple<U,Args...>>::type type;
};
template <typename T>
struct Last<std::tuple<T>>
{
typedef T type;
};
2. Introducing a specialized helper
template <typename T, typename U>
struct alpha_tuple
{
enum { value = 1 };
};
template <typename T>
struct alpha_tuple<T,T>
{
enum { value = 3 };
};
template <typename T>
struct alpha_tuple<std::vector<T>,T>
{
enum { value = 2; }
};
3. Hooking it up
template <typename T>
struct alpha<std::tuple<>, T>
{
enum { value = 1 };
};
template <typename T, typename U, typename Args...>
struct alpha<std::tuple<U, Args...>, T>
{
typedef typename Last<std::tuple<U, Args...>>::type LastType;
enum { value = alpha_tuple<LastType,T>::value };
};
Note that there is no last type for empty tuples, so I had to deal with them in a separate specialization.
If you like to find out whether a tuple as a specific last member, here's a type trait for that:
#include <type_traits>
#include <tuple>
template <typename ...Args> struct back;
template <typename T, typename ...Args> struct back<T, Args...>
{ typedef typename back<Args...>::type type; };
template <typename T> struct back<T>
{ typedef T type; };
template <typename...> struct tuple_has_last : public std::false_type {};
template <typename T, typename... Args> struct tuple_has_last<T, std::tuple<Args...>>
{
static const bool value = std::is_same<typename back<Args...>::type, T>::value;
};
Edit: Oh, I didn't see that Matthieu had already written the exact same thing. Never mind.