SFINAE and detect if a C ++ function object returns void

6 answers

Wouldn't it be easier to implement an overloaded version of no-op print (void)?

Good. Function templates and overloading can easily handle this at runtime.

It gets a little more sticky if you wanted to process it at compile time, for use with #if macros or with moments of static compilation.

But since you only need the first one, can I suggest something like this as a starting point:

(Tested under (GCC) 3.4.4 and 4.0.1. - I know, I need to update!)

 #include <iostream> using namespace std; struct Foo { void operator()() {} }; struct Bar { bool operator()() { return false; } }; Foo foo; Bar bar; bool baz() { return false; } void bang() {} struct IsVoid { typedef char YES[1]; typedef char NO[2]; /* Testing functions for void return value. */ template <typename T> static IsVoid::NO & testFunction( T (*f)() ); static IsVoid::YES & testFunction( void (*f)() ); static IsVoid::NO & testFunction( ... ); /* Testing Objects for "void operator()()" void return value. */ template <typename C, void (C::*)()> struct hasOperatorMethodStruct { }; template <typename C> static YES & testMethod( hasOperatorMethodStruct<C, &C::operator()> * ); template <typename C> static NO & testMethod( ... ); /* Function object method to call to perform test. */ template <typename T> bool operator() (T & t) { return ( ( sizeof(IsVoid::testFunction(t)) == sizeof(IsVoid::YES) ) || ( sizeof(IsVoid::testMethod<T>(0)) == sizeof(IsVoid::YES) ) ); } }; #define BOUT(X) cout << # X " = " << boolToString(X) << endl; const char * boolToString( int theBool ) { switch ( theBool ) { case true: return "true"; case false: return "false"; default: return "unknownvalue"; } } int main() { IsVoid i; BOUT( IsVoid()(foo) ); BOUT( IsVoid()(bar) ); BOUT( IsVoid()(baz) ); BOUT( IsVoid()(bang) ); cout << endl; BOUT( i(foo) ); BOUT( i(bar) ); BOUT( i(baz) ); BOUT( i(bang) ); } 



Well, I'm starting to see more of a problem.

So far, we can do something like that:

 #include <iostream> using namespace std; struct FooA { void operator()() {} }; struct FooB { bool operator()() { return false; } }; struct FooC { int operator()() { return 17; } }; struct FooD { double operator()() { return 3.14159; } }; FooA fooA; FooB fooB; FooC fooC; FooD fooD; void barA() {} bool barB() { return false; } int barC() { return 17; } double barD() { return 3.14159; } namespace N { /* Functions */ template <typename R> R run( R (*f)() ) { return (*f)(); } bool run( void (*f)() ) { (*f)(); return true; } /* Methods */ template <typename T, typename R> R run( T & t, R (T::*f)() ) { return (t .* f) (); } template <typename T> bool run( T & t, void (T::*f)() ) { (t .* f) (); return true; } }; #define SHOW(X) cout << # X " = " << (X) << endl; #define BOUT(X) cout << # X " = " << boolToString(X) << endl; const char * boolToString( int theBool ) { switch ( theBool ) { case true: return "true"; case false: return "false"; default: return "unknownvalue"; } } int main() { SHOW( N::run( barA ) ); BOUT( N::run( barA ) ); SHOW( N::run( barB ) ); BOUT( N::run( barB ) ); SHOW( N::run( barC ) ); SHOW( N::run( barD ) ); cout << endl; SHOW( N::run(fooA,&FooA::operator())); BOUT( N::run(fooA,&FooA::operator())); SHOW( N::run(fooB,&FooB::operator())); BOUT( N::run(fooB,&FooB::operator())); SHOW( N::run(fooC,&FooC::operator())); SHOW( N::run(fooD,&FooD::operator())); } 

You still have such an unpleasant need for the root & CLASS :: operator () as an argument.


Ultimately, although we can determine whether the object () method returns the void method, we cannot normally overload it based on the returned types.

We can get around this overload restriction with specialized specialization. But then we get into this ugliness, in which we still need to specify types ... Especially the return type! Either manually or by passing a suitable argument from which we can extract the necessary types.

By the way: #define macros won't help either. Tools like ?: Require the same type for both? and: part.

So this is the best I can do ...



Of course...

If you do not need a return type ...

If you just pass the result to another function ...

You can do something like this:

 #include <iostream> using namespace std; struct FooA { void operator()() {} }; struct FooB { bool operator()() { return false; } }; struct FooC { int operator()() { return 17; } }; struct FooD { double operator()() { return 3.14159; } }; FooA fooA; FooB fooB; FooC fooC; FooD fooD; void barA() {} bool barB() { return false; } int barC() { return 17; } double barD() { return 3.14159; } #define SHOW(X) cout << # X " = " << (X) << endl; namespace N { template <typename T, typename R> R run( T & t, R (T::*f)() ) { return (t .* f) (); } template <typename T> bool run( T & t, void (T::*f)() ) { (t .* f) (); return true; } template <typename T> void R( T & t ) { SHOW( N::run( t, &T::operator() ) ); } template <typename T> void R( T (*f)() ) { SHOW( (*f)() ); } void R( void (*f)() ) { (*f)(); SHOW( true ); } }; int main() { N::R( barA ); N::R( barB ); N::R( barC ); N::R( barD ); N::R( fooA ); N::R( fooB ); N::R( fooC ); N::R( fooD ); } 
+1
source share

To detect the void return value at compile time, the standard trick is operator, overloading. The wonderful thing with the comma operator is that it can accept the void parameter, in which case, by default, it is built into operator, In code:

 template <typename> tag {}; template <typename T> tag<T> operator,(T, tag<void>); 

Now expr, tag<void>() is of type tag<typeof(expr)> , even if expr is of type void. You can catch this with the usual tricks:

 char (&test(tag<void>))[1]; template <typename T> char (&test(tag<T>))[2]; template <typename F> struct nullary_functor_traits { static const bool returns_void = sizeof(test((factory()(), tag<void>()))) == 1; private: static F factory(); }; 
+6
source share

Perhaps you can use the fact that void & does not make sense as a type, but void * does.

+1
source share

With C ++ 0x, you can do this easily using decltype .

0
source share

If you can use Boost , the following code will probably work. I assume that all functions / functors are null, as in your question. However, to use this, result_type must be defined in all functors (function class).

 #include <boost/utility/result_of.hpp> #include <boost/utility/enable_if.hpp> #include <boost/type_traits.hpp> using namespace boost; // Sorry, for brevity template< class F > // typename result_of< F() >::type typename disable_if< is_void< typename result_of< F() >::type > , typename result_of< F() >::type >::type f( F const& x ) { return x(); } template< class F > typename enable_if< is_void< typename result_of< F() >::type >, bool >::type f( F const& x ) { x(); return true; } template< class T > T f( T x() ) { return x(); } bool f( void x() ) { x(); return true; } static void void_f() {} static int int_f() { return 1; } struct V { typedef void result_type; result_type operator()() const {} }; struct A { typedef int result_type; result_type operator()() const { return 1; } }; int main() { A a; V v; f( void_f ); f( int_f ); f( a ); f( v ); } 

Hope this helps

0
source share

try specializing in void return type:

 template<class F> class traits; template<class F, class T> class traits<T (F)()>; template<class F> class traits<void (F)()>; 

I think...

-one
source share

All Articles