Reputation: 769
assume I have a number of enums enum EnumA { fooA, barA, quuzA };
, enum EnumB {fooB, barB, quuzB };
, etc and a number of template structs in the form:
template<EnumA value> struct StructA {};
template<EnumB value> struct StructB {};
and so on and so forth. I would like to achieve an implicit conversion from enum values to the corresponding structs, obviously when the enum value is a compile time constant.
For more context, I would like to be able to do use it in a context such as the following. I have a function
template<typename T> void somefunc(int somepar, T);
which I can use as somefunc(somepar, StructB<quuzB>());
and which I would like to be able to use as somefunc(somepar, quuzB);
instead.
(And no, overloading somefunc as a set of template functions, one for each enum, that does the appropriate struct wrapping itself is NOT an option, due to (1) the number of enums and (2) the fact that I would also like to have something like template<typename T1, typename T2> void somefunc(int par1, T1, int par2, T2)
to use as somefunc(p1, StructB<quuzB>(), p2, StructA<barA>())
etc.)
Upvotes: 1
Views: 551
Reputation: 145269
The enum
value that you want to provide as acual argument in a call, ends up as a run time value. But to transform it to a value-specific type, compile time knowledge of the value is needed. Currently you're supplying the compile time knowledge manually, and with your proposed simpler call syntax the only general way to pick up that knowledge is via some source code preprocessing.
To avoid the preprocessing (by whatever means, e.g. macro or script, or trained chimpanzee) you can adjust the requirements for the call syntax, e.g. explicitly providing the enum
type, so instead of
somefunc( somepar, quuzB );
… you'll write
somefunc<EnumB, quuzB>( somepar );
Example, where the macro thing is only for exposition in case you'd want that, but I recommend doing without a macro (explicit is good, implicit bad):
enum EnumA { fooA, barA, quuzA };
enum EnumB { fooB, barB, quuzB };
template< EnumA value> struct StructA {};
template< EnumB value> struct StructB {};
//template<typename T> void somefunc( int somepar, T );
template< class Enum_type, Enum_type value >
void somefunc( int somepar );
void foo()
{
somefunc<EnumB, quuzB>( 42 );
}
//-------------------------------------------------------------
#define SOMEFUNC( par, e ) somefunc<decltype(e), e>( par )
void bar()
{
SOMEFUNC( 42, quuzB );
}
//-------------------------------------------------------------
#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;
#ifdef __GNUC__
# include <cxxabi.h>
auto demangled( char const name[] )
-> string
{
int status = 0;
char* c_result = abi::__cxa_demangle( name, 0, 0, &status);
string result = c_result;
free( c_result );
return result;
}
#else
auto demangled( char const name[] )
-> string
{ return name; }
#endif
template< class Type >
void somefunc_impl( int x, Type )
{
cout << x << ", " << demangled( typeid( Type ).name() ) << endl;
}
template< class Enum_type >
struct Enum_to_struct;
template<>
struct Enum_to_struct<EnumA>
{
template< EnumA e > struct Struct{ using T = StructA<e>; };
};
template<>
struct Enum_to_struct<EnumB>
{
template< EnumB e > struct Struct{ using T = StructB<e>; };
};
template< class Enum_type, Enum_type value >
void somefunc( int somepar )
{
using Struct = typename Enum_to_struct<Enum_type>::template Struct<value>::T;
somefunc_impl( somepar, Struct() );
}
auto main() -> int
{
foo();
bar();
}
This can be simplified – you can avoid the Enum_to_struct
traits – by using partially specialized class templates instead of unrelated individually named templates like StructA
and StructB
enum EnumA { fooA, barA, quuzA };
enum EnumB { fooB, barB, quuzB };
template< class Enum_type, Enum_type value >
struct Struct{};
template< EnumA e >
struct Struct< EnumA, e > {}; // Whatever, corresponding to StructA
template< class Enum_type, Enum_type value >
void somefunc( int somepar );
void foo()
{
somefunc<EnumB, quuzB>( 42 );
}
//-------------------------------------------------------------
#define SOMEFUNC( par, e ) somefunc<decltype(e), e>( par )
void bar()
{
SOMEFUNC( 42, quuzB );
}
//-------------------------------------------------------------
#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;
#ifdef __GNUC__
# include <cxxabi.h>
auto demangled( char const name[] )
-> string
{
int status = 0;
char* c_result = abi::__cxa_demangle( name, 0, 0, &status);
string result = c_result;
free( c_result );
return result;
}
#else
auto demangled( char const name[] )
-> string
{ return name; }
#endif
template< class Type >
void somefunc_impl( int x, Type )
{
cout << x << ", " << demangled( typeid( Type ).name() ) << endl;
}
template< class Enum_type, Enum_type value >
void somefunc( int somepar )
{
somefunc_impl( somepar, Struct<Enum_type, value>() );
}
auto main() -> int
{
foo();
bar();
}
For completeness, even though macros are Evil™ and I recommend against them, if you choose to go the macro route you can reduce the overhead to a single macro instead of one per function, at the cost of less obvious call syntax:
#define TV( v ) decltype( v ), v
void qwepoi()
{
somefunc<TV( quuzB )>( 42 );
}
Upvotes: 2