
Below I have outlined my need and implementation for a compile-time conditional function invocation utility. I thought it might be generally useful to others using simmilar metaprogramming techniques. If suitable solutions can be found for some of the outstanding issues perhaps it will prove suitable for inclusion in boost. The following quick documentation and a snapshot of the implementation can be found at http://dump.qxxy.com/execute_if/ and (intermitantly) http://66.1.253.185:8080/src/execute_if/index.html Any feedback would be appreciated. Thanks Jeff Mirwaisi Introduction The following is a simple compile-time conditional function invocation and binding utility type. Generic code often needs to handle a subset, of all the types, in a slightly different way than the rest. template <typename T> void f(T t) { t.a(); t.b(); ... if ( has_member_function_c<T>::value ) t.c(); } This solution obviously will not work if type T has no member function c. The compiler expects all the types used with the template function f to have a member function c even though its clear the conditional expression is either true or false at compile time. One possible workaround for this situation is to use SFINAE and enable_if to overload template function f for types which have a member function c and those that do not. template <typename T> void f(T t, typename enable_if<has_member_function_c<>,T>::type* SFINAE = 0) { t.a(); t.b(); ... t.c(); } template <typename T> void f(T t, typename enable_if<not<has_member_function_c<T> >,T>::type* SFINAE = 0) { t.a(); t.b(); ... } This solution works! Unfortunately it forces us to repeat the majority of the implementation of the two template function overloads f, and it does not scale. If we add a second conditional invocation, the number of overloads jump to four, the same is true of conditional callers, e.g. a second group of two template functions overloads g if theres a single conditional invocation, four if there are two conditional calls etc. To combat this overload explosion we could isolate the conditional call to member function c with a helper type. template <bool> struct call_member_function_c { static inline void invoke(T& t) {t.c();} }; template <> struct call_member_function_c<false> { static inline void invoke(T& t) {} }; template <typename T> void f(T t) { t.a(); t.b(); call_member_function_c< has_member_function_c<T>
::invoke(t); };
This solution improves on the previous one, it scales well as we add conditional member function invocations (call_member_function_d,call_member_function_e,...) and as we increase the number of callers(f,g,h,...). But it has introduced an explicit no-op function for each call_member_function_X helper type, and a partial specialization for each helper type to select whether to generate a call or a no-op. This "helper" type can be formalized and we can provide a generic no-op implementing replacement for all "helpers" which reduces our problem to the simplest solution allowed by C++. Target Holders Unfortunately the limitations of the current language wont allow us to create a generic "helper" type which can be loaded with different template function targets. Instead the user is expected to provide a minimal holder type which delegates to the target template function. Note that the following example is also a late template argument binding function object. This allows the user to either invoke the conditionally executed target immediately or produce a function object which can be stored and called at a later point. template <typename T> void template_fn_target(T) {} struct template_fn_target_holder { typedef void result_type; template <typename Arg> inline result_type operator()(Arg arg) {return template_fn_target(arg);} template <typename Arg> static inline result_type invoke(Arg arg) {return template_fn_target(arg);} }; The target holder type provides the result type of the operation to the user, the function object usage expects a function call overload that can handle the arguments specified, if the call is to be made instantly than we can simplify the use by calling a static member function of the target holder type named invoke. execute_if template <typename Condition > struct execute_if { template< typename TargetHolder > static inline typename TargetHolder::result_type invoke( arg1,...,argN ); template< typename TargetHolder > static inline TargetHolder bind(); }; When the condition value is true the subsequent invoke or bind operation are performed on the target type, if the value is false a no-op invoke or bind are generated. execute_if_c template < bool > struct execute_if_c { template< typename TargetHolder > static inline typename TargetHolder::result_type invoke( arg1,...,argN ); template< typename TargetHolder > static inline TargetHolder bind(); }; The execute_if_c variation on the above execute_if type provides a less generic version of enable_if which uses a boolean value as the condition upon which to select either the specified target or a no-op. execute_if_else template <typename Condition > struct execute_if_else { template< typename TargetHolderA,typename TargetHolderB > static inline typename TargetHolder?::result_type invoke( arg1,...,argN ); template< typename TargetHolderA,typename TargetHolderB > static inline TargetHolder? bind(); }; When the condition value is true the subsequent invoke or bind operation are performed on target holder A, if the value is false target holder B will be used execute_if_else_c template < bool > struct execute_if_else_c { template< typename TargetHolderA,typename TargetHolderB > static inline typename TargetHolder? ::result_type invoke( arg1,...,argN ); template< typename TargetHolderA,typename TargetHolderB > static inline TargetHolder? bind(); }; The execute_if_else_c variation on the above execute_if_else type provides a less generic version of enable_if which uses a boolean value as the condition upon which to select either target holder A or target holder B. To-Do * Currently manually written nullary,unary,and binary execute_if and proxy functions, needs to be rewritten to use the boost preprocessor lib * Possible move of the return type template parameter into the execute_ifxxx template parameter list (out of the bind and invoke template functions) * Investigate using the preprocessor to automatically generate the template function holder types, and other techniques to reduce the users responsibility * Document the lack of a means of representing a template function in the c++ type system, no function equivalent of template template classes * The current code also implements a set of invoke_fn and bind_fn overloads which operate on function references and function object references, we must determine wether these should be provided at all, and if so they should probably be documented

Sounds like a great addition to boosts enable_if. Could you post some "user" code that clearly demonstrates what structures are needed to dispatch within a function body? Andreas Pokorny
participants (2)
-
Andreas Pokorny
-
Jeff Mirwaisi