Switch Passed Type from Template

Switch passed type from template

Yes, it is...but it probably won't work the way you expect.

template < typename T >
void foo()
{
if (is_same<T,SomeClass>::value) ...;
else if (is_same<T,SomeClass2>::value) ...;
}

You can get is_same from std:: or boost:: depending on your desire/compiler. The former is only in C++0x.

The problem comes with what is in .... If you expect to be able to make some function call specific to those types within foo, you are sadly mistaken. A compiler error will result even though that section of code is never run when you pass in something that doesn't obey that expected interface.

To solve THAT problem you need to do something a bit different. I'd recommend tag dispatching:

struct v1_tag {};
struct v2_tag {};

template < typename T > struct someclass_version_tag;
template < > struct someclass_version_tag<SomeClass> { typedef v1_tag type; };
template < > struct someclass_version_tag<SomeClass2> { typedef v2_tag type; };

void foo(v1_tag) { ... }
void foo(v2_tag) { ... }
template < typename T > void foo()
{
typedef typename someclass_version_tag<T>::type tag;
foo(tag());
}

Note that you will not be suffering any runtime-polymorphism overhead here and with optimizations turned on it should result in the same or even smaller code size AND speed (though you shouldn't be worrying about that anyway until you've run a profiler).

Switch template type

If your compiler supports this1, you can use template function specialisations:

class WorldSettings
{
private:
std::map<std::string, int> mIntegerStorage;
std::map<std::string, float> mFloatStorage;
std::map<std::string, std::string> mStringStorage;

public:
template <typename T>
T Get(const std::string &key); // purposely left undefined
};

...

template<>
int WorldSettings::Get<int>(const std::string& key) {
return mIntegerStorage[key];
}

template<>
float WorldSettings::Get<float>(const std::string& key) {
return mFloatStorage[key];
}

// etc

Notice that the methods are not const because map<>::operator[] is not const.

Also, if someone tries to use the template with a type other than one you have provided a specialisation for, they will get linker errors, so your code won't misbehave or anything. Which is optimal.


1 If not, see @gwiazdorrr's answer

C++ template instantiation: Avoiding long switches

You could use a variadic template, maybe like this:

#include <cstdlib>
#include <string>

int main(int argc, char * argv[])
{
if (argc != 2) { return EXIT_FAILURE; }

handle_cases<1, 3, 4, 9, 11>(std::stoi(argv[1]));
}

Implementation:

template <int ...> struct IntList {};

void handle_cases(int, IntList<>) { /* "default case" */ }

template <int I, int ...N> void handle_cases(int i, IntList<I, N...>)
{
if (I != i) { return handle_cases(i, IntList<N...>()); }

Wrapper<I> w;
w.foo();
}

template <int ...N> void handle_cases(int i)
{
handle_cases(i, IntList<N...>());
}

Replacing switch statements when interfacing between templated and non-templated code

Just to expand YoungJohn's comment, it looks like this (I've included a single initialization of the operator, and it could be made simpler if there was no parameters, but if there are no parameters there is little reason to do this anyway :-P).

#include <functional>
#include <map>

////////////////////////////////////
//////specific impmenetation code
class base{public: virtual void foo() = 0;};

template <typename x>
struct derived : public base
{
virtual void foo() {std::cout << "Ima " << typeid(x).name() << std::endl;}
};

struct my_op
{
int some_param_; /// <shared parameter

my_op(int some_param) : some_param_(some_param){} /// <constructor

template<typename T>
base* do_stuff() const
{
std::cout << "Use some parameter: " << some_param_ << std::endl;
base* ret = new derived<T>();
ret->foo();
return ret;
}
};

base* init_from_params(int some_param, char key)
{
my_op op(some_param);
using factoryFunction = std::function<base*()>;
std::map<char, factoryFunction> mp
{
{ 'a', std::bind(&my_op::do_stuff<int>, &op)},
{ 'b', std::bind(&my_op::do_stuff<long int>, &op)},
{ 'c', std::bind(&my_op::do_stuff<double>, &op)}
} ;
factoryFunction& f = mp[key];
if (f)
{
return f();
}
return NULL;
}

int main(int argc, char** argv)
{
volatile int parameters = 10;

while (true)
{
std::cout << "Press a, b, or c" << std::endl;
char key;
std::cin >> key;

base* value = init_from_params(parameters, key);

std::cout << (void*)value << std::endl;
}
}

Pros: so much shorter, so much more standard, so much less weird template stuff. It also doesn't require the templated arguments to all be types, we can select whatever we want to initialize the function.

Cons: In theory, it could have more overhead. In practice, I totally doubt that the overhead would ever matter.

I like it!

Optimize template replacement of a switch

This is what I call the magic switch problem -- how to take a (range of) run time values and turn it into a compile time constant.

Abstractly, you want to generate this switch statement:

switch(n) {
(case I from 0 to n-1: /* use I as a constant */)...
}

You can use parameter packs to generate code that is similar to this in C++.

I'll start with c++14-replacing boilerplate:

template<unsigned...> struct indexes {typedef indexes type;};
template<unsigned max, unsigned... is> struct make_indexes: make_indexes<max-1, max-1, is...> {};
template<unsigned... is> struct make_indexes<0, is...>:indexes<is...> {};
template<unsigned max> using make_indexes_t = typename make_indexes<max>::type;

Now we can create a compile-time sequence of unsigned integers from 0 to n-1 easily. make_indexes_t<50> expands to indexes<0,1,2,3, ... ,48, 49>. The c++14 version does so in O(1) steps, as most (all?) compilers implement std::make_index_sequence with an intrinsic. The above does it in linear (at compile time -- nothing is done at run time) recursive depth, and quadratic compile time memory. This sucks, and you can do better with work (logarithmic depth, linear memory), but do you have more than a few 100 types? If not, this is good enough.

Next, we build an array of callbacks. As I hate C legacy function pointer syntax, I'll throw in some pointless boilerplate to hide it:

template<typename T> using type = T; // pointless boilerplate that hides C style function syntax

template<unsigned... Is>
Base_Type construct_runtime_helper( indexes<Is...>, Base_Type::type_enum e, QVariant const& v ) {
// array of pointers to functions: (note static, so created once)
static type< Base_Type(const QVariant&) >* const constructor_array[] = {
(&Base_Type::construct<Is>)...
};
// find the eth entry, and call it:
return constructor_array[ unsigned(e) ](v);
}
Base_Type construct_runtime_helper( Base_Type::type_enum e, QVariant const& v ) {
return construct_runtime_helper( make_indexes_t< Base_Type::num_types >(), e, v );
}

and Bob is your Uncle1. An O(1) array lookup (with an O(n) setup, which in theory could be done prior to your executable launching) for dispatch.


1 "Bob's your Uncle" is a British Commonwealth saying that says "and everything is finished and working" roughly.

Is There Anything Like a Templatized Case-Statement

I had to do something like this once so I wrote a small wrapper to acheive the result neatly. You could use it as follows (see here for a test)

template<class T>
typename static_switch<sizeof(T)
,int // default case
,static_case<sizeof(char),char>
,static_case<sizeof(short),short>
,static_case<sizeof(long),long>
>::type foo(T bar){ ... }

Behind the scenes it pretty much does what you already have but by wrapping it we keep it (more) readable. There is also a version to allow you to switch direclty on the type T if you needed that.

Edit: At @Deduplicator's suggestion here is the code behind it

#include <type_traits>  

/*
* Select a type based on the value of a compile-time constant such as a
* constexpr or #define using static_switch.
*/

template<int I,class T>
struct static_case {
static constexpr int value = I;
using type = T;
};

template<int I, class DefaultType, class Case1, class... OtherCases>
struct static_switch{
using type = typename std::conditional< I==Case1::value ,
typename Case1::type,
typename static_switch<I,DefaultType,OtherCases...>::type
>::type;
};

struct fail_on_default {};

template<int I, class DefaultType, class LastCase>
struct static_switch<I,DefaultType,LastCase> {
using type = typename std::conditional< I==LastCase::value ,
typename LastCase::type,
DefaultType
>::type;

static_assert(!(std::is_same<type, fail_on_default>::value),
"Default case reached in static_switch!");
};

C++ templates to avoid long switches, while calling a function with different return types

If you can use c++17, here's a "simplified" version of @Klaus's approach. Instead of using a had-made recursive structure, you could use a c++17 fold-expression:

template<auto... Funcs, std::size_t... I>
bool select_case(std::size_t i, std::integer_sequence<std::size_t, I...>) {
return ([&]{ if(i == I) { print_result(Funcs); return true; } return false; }() || ... );
}

template<auto... Funcs>
struct FuncSwitch {

static bool Call(std::size_t i) {
return select_case<Funcs...>(i, std::make_index_sequence<sizeof...(Funcs)>());
}
};

The idea is to wrap each of Funcs in a lambda such that only the function corresponding to the index passed is called. Note that the || in the fold expression short-circuits.
Would be used like this:

float q0() { return 0.f; }
int q1() { return 1; }
std::string q2() { return "two"; }

int main() {

bool success = FuncSwitch<q0, q1, q2>::Call(1);
}

See here for a complete example.

How to switch passed value through C++ standard type trait?

You can use SFINAE to reject overloads

template<typename T>
typename std::enable_if<std::is_integral<T>::value || std::is_enum<T>::value>::type
func(T t)
{
return t;
}

template<typename T>
typename std::enable_if<std::is_floating_point<T>::value>::type
func(T t)
{
return t;
}

template<typename T>
typename std::enable_if<std::is_same<T, std::string>::value>::type
func(T t)
{
return t;
}

or use tag dispatching similar to what you've shown, but handle the alternatives differently to reduce the number of cases

namespace detail
{
template<typename T>
T func(T t, std::false_type /*is_string*/, std::false_type /*is_float*/, std::true_type /*is_int*/)
{
return t;
}

template<typename T>
T func(T t, std::false_type /*is_string*/, std::true_type /*is_float*/, std::false_type /*is_int*/)
{
return t;
}

template<typename T>
T func(T t, std::true_type /*is_string*/, std::false_type /*is_float*/, std::false_type /*is_int*/)
{
return t;
}
}

template<typename T>
T func(T t)
{
return detail::func(t, std::is_same<string, T>(),
std::is_floating_point<T>(),
std::integral_constant<bool, std::is_integral<T>::value || std::is_enum<T>::value>());
}

Generic switch statement to initialize derived types

You can create a function template that will take functor template to execute. Without C++11 variadic arguments all the functors need the same number of arguments, so you will have to either pack them in a structure or pass NULLs when they are irrelevant. With variadic templates (I haven't use them yet, so I don't remember exact syntax and won't write them here) you can easily have different arguments in each form.

The idea is like (off the top of my head, so there might be some typos):

template <template <typename T> class F>
F<Struct<DNS_Q_A>::Type>::return_type RecordCall(RecordType nType, const F<Struct<DNS_Q_A>::Type>::argument_type &arg)
{
switch(nType)
{
case DNS_Q_A:
return F<Struct<DNSK_Q_A>::Type>()(arg);
case DNS_Q_CNAME:
return F<Struct<DNSK_Q_CNAME>::Type>()(arg);
// ...
}
}

Now you write the individual functions like:

template <typename T>
struct Clone : std::unary_function<BaseType *, const BaseType *>
{
BaseType *operator()(const BaseType *source)
{
return new<T>(dynamic_cast<const BaseType &>(*source));
}
}

and combine together:

target = RecordCall<Clone>(source->GetType(), source);

(and wrap in another function, that will insert the getter and/or pack arguments for the multi-argument forms like placement copy construction)

Though for copying the common way to do this is to have virtual Clone member method. But that won't work for construction.

Edit: Note, that I defined the return and argument types within the inner template as typedefs (using the standard unary_function helper), but they can alternatively be passed as separate template arguments, especially if the use of RecordCall is going to be wrapped in another function. Perhaps the return type does not even have to be a template parameter as it's going to be Record * for all the cases mentioned in the question.



Related Topics



Leave a reply



Submit