C++ convert variables to template arguments
I decided to have some more fun with the code, here's an improved version over my first attempt which has the following benefits:
- Supports
enum
types - Explicitly specify how many parameters should be converted
- Generic implementation for the complicated part, one small helper for each function that uses it.
The code:
#include <iostream>
#include <utility>
#include <type_traits>
// an enum we would like to support
enum class tribool { FALSE, TRUE, FILE_NOT_FOUND };
// declare basic generic template
// (independent of a specific function you'd like to call)
template< template< class > class CB, std::size_t N, typename = std::tuple<> >
struct var_to_template;
// register types that should be supported
template< template< class > class CB, std::size_t N, typename... Cs >
struct var_to_template< CB, N, std::tuple< Cs... > >
{
// bool is pretty simple, there are only two values
template< typename R, typename... Args >
static R impl( bool b, Args&&... args )
{
return b
? var_to_template< CB, N-1, std::tuple< Cs..., std::true_type > >::template impl< R >( std::forward< Args >( args )... )
: var_to_template< CB, N-1, std::tuple< Cs..., std::false_type > >::template impl< R >( std::forward< Args >( args )... );
}
// for each enum, you need to register all its values
template< typename R, typename... Args >
static R impl( tribool tb, Args&&... args )
{
switch( tb ) {
case tribool::FALSE:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::FALSE > > >::template impl< R >( std::forward< Args >( args )... );
case tribool::TRUE:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::TRUE > > >::template impl< R >( std::forward< Args >( args )... );
case tribool::FILE_NOT_FOUND:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::FILE_NOT_FOUND > > >::template impl< R >( std::forward< Args >( args )... );
}
throw "unreachable";
}
// in theory you could also add int, long, ... but
// you'd have to switch on every possible value that you want to support!
};
// terminate the recursion
template< template< class > class CB, typename... Cs >
struct var_to_template< CB, 0, std::tuple< Cs... > >
{
template< typename R, typename... Args >
static R impl( Args&&... args )
{
return CB< std::tuple< Cs... > >::template impl< R >( std::forward< Args >( args )... );
}
};
// here's your function with the template parameters
template< bool B, tribool TB >
int HeavyLoop_impl( int arg )
{
for( int i = 0; i < 10000000; i++ ) {
arg += B ? 1 : 2;
arg += ( TB == tribool::TRUE ) ? 10 : ( TB == tribool::FALSE ) ? 20 : 30;
}
return arg;
}
// a helper class, required once per function that you'd like to forward
template< typename > struct HeavyLoop_callback;
template< typename... Cs >
struct HeavyLoop_callback< std::tuple< Cs... > >
{
template< typename R, typename... Args >
static R impl( Args&&... args )
{
return HeavyLoop_impl< Cs::value... >( std::forward< Args >( args )... );
}
};
// and here, everything comes together:
int HeavyLoop( bool b, tribool tb, int arg )
{
// you provide the helper and the number of arguments
// that should be converted to var_to_template<>
// and you provide the return type to impl<>
return var_to_template< HeavyLoop_callback, 2 >::impl< int >( b, tb, arg );
}
int main()
{
bool b = true;
tribool tb = tribool::FALSE;
int arg = 0;
int res = HeavyLoop( b, tb, arg );
std::cout << "res: " << res << std::endl;
return 0;
}
And here's a live example in case you want to play with it.
Here's how you can do it:
#include <iostream>
using namespace std;
template <bool b1, bool b2>
struct HeavyLoopImpl
{
static int func(int arg)
{
for (int i = 0; i < 10000000; i++) {
arg += b1 ? 1 : 2;
arg += b2 ? 10 : 20;
}
return arg;
}
};
template <template<bool...> class Impl,bool...Bs>
struct GenericJump
{
template<typename... Args>
static int impl(Args&&... args)
{
return Impl<Bs...>::func(std::forward<Args>(args)...);
}
template<typename... Args>
static int impl(bool b, Args&&... args)
{
return b
? GenericJump<Impl,Bs...,true >::impl(std::forward<Args>(args)...)
: GenericJump<Impl,Bs...,false>::impl(std::forward<Args>(args)...);
}
};
int HeavyLoop(bool b1, bool b2, int arg)
{
return GenericJump<HeavyLoopImpl>::impl(b1,b2,arg);
}
int main()
{
bool b1 = true;
bool b2 = false;
int arg = 0;
int res = HeavyLoop(b1, b2, arg);
cout << "res: "<<res<<endl;
return 0;
}
This is basically Daniels solution, but it allows you to use functions other than HeavyLoop_impl()
as implementation. Only being able to call a single template function kind of defeats the purpose of being a generic solution. The GenericJump
template class can call other functions also. You only have to change the HeavyLoop_impl()
template function into a template class with a static function func()
. It works marvellously. It compiles with gcc 4.7.3 and gives the correct output.