c++ - fallback to alternate function when templated function instantiation fails -
i need store pointers instanced template functions , when function cannot instanced store pointer empty function instead. looked sfinae dont think applies here.
struct staticentity { double position; }; struct dynamicentity { double position; double speed; }; class movesystem { public: template <typename t> void update(t& entity, double dt) { entity.position += entity.speed*dt; } }; typedef void (*updateentitiesfunc)(void* system, void* entity, double dt); template <typename s, typename e> static void update(void* system, void* entity, double dt) { // here if inner function cannot instanced skip , "nothing" instead ((s*)system)->update(*(e*)entity, dt); } int main() { updateentitiesfunc uf = update<movesystem, dynamicentity>; updateentitiesfunc uf2 = update<movesystem, staticentity>; //^ not compile // gives error: 'struct staticentity' has no member named 'speed' // compile , contain pointer empty function return 0; }
it solvable template magic cant figure out. ideally without adding complexity both entity , system classes.
design motivation:
for entity , system types want create static array of function pointers:
updateentitiesfunc funcs[entitytypes::gettypescount()][systemtypes::gettypescount()];
and @ runtime call correct function type-ids:
funcs[entity->gettypeid()][system->gettypeid()](&system, &entity, dt);
at runtime check if entity compatible system runtime information. function pointers must registered entity-system pairs @ compile time, though not compatible. wanted create no-op functions.
first, metaprogramming boilerplate:
namespace details { template<class...>struct voider{using type=void;}; template<class...ts>using void_t=typename voider<ts...>::type; template<template<class...>class z, class, class...ts> struct can_apply: std::false_type {}; template<template<class...>class z, class...ts> struct can_apply<z, void_t<z<ts...>>, ts...>: std::true_type {}; } template<template<class...>class z, class...ts> using can_apply=details::can_apply<z,void,ts...>;
now, can detect properties:
template<class t> using speed_t = decltype(std::declval<t>().speed); template<class t> using position_t = decltype(std::declval<t>().position); template<class t> using has_speed = can_apply<speed_t, t>; template<class t> using has_position = can_apply<position_t, t>; template<class s, class e> using update_call_t = decltype( std::declval<s>().update( std::declval<e>(), 0.0 ) ); template<class s, class e> using has_update = can_apply< update_call_t, s, e >;
and have 3 traits, has_position
, has_update
, has_speed
useful.
now fix movesystem
:
struct movesystem { template <class t> std::enable_if_t< has_speed<t&>{} && has_position<t&>{} > update(t& entity, double dt) { entity.position += entity.speed*dt; } };
next, modify update:
namespace updates { template<class s, class e> std::enable_if_t< has_update<s,e>{} > update(s* system, e* entity, double dt ) { system->update(*entity, dt); } void update(void*, void*, double) {} } template<class s, class e> void update(void* system, void* entity, double dt) { using updates::update; update(static_cast<s*>(system), static_cast<e*>(entity), dt ); }
to check .update
method working parameters.
i adl-enabled code such if class has friend void update( s*, e*, double )
work.
this sfinae work. note adding more properties once have can_apply
pretty easy. make alias generates type works if property satisfied, write can_apply
alias converts application compile-time boolean test.
as aside, msvc2015 not c++11 compiler, in cannot compile above code. in msvc have track down proprietary extensions equivalent of above code. involves writing has_position
, other traits differently. call failure obey c++11 standard in case inability "expression sfinae".
note above uses handful of c++14 features. replace std::enable_if_t<??>
typename std::enable_if<??>::type
, replace has_position<??>{}
has_position<??>::value
, similar other changes if compiler doesn't support it.
Comments
Post a Comment