Say you have some target class with some methods on it:
class Subject
{
public:
void voidReturn() { std::cout<<__FUNCTION__<<std::endl; }
int intReturn() { std::cout<<__FUNCTION__<<std::endl; return 137; }
};
And a Value class (similar in concept to Boost.Any):
struct Value
{
Value() {}
Value( Value const & orig ) {}
template< typename T > Value( T const & val ) {}
};
And I want to produce a Value object using a method from the Subject class:
Subject subject;
Value intval( subject.intReturn() );
Value voidVal( subject.voidReturn() ); // compilation error
I get the following errors in VC++2008:
error C2664: 'Value::Value(const Value &)' : cannot convert parameter 1 from 'void' to 'const Value &'
Expressions of type void cannot be converted to other types
and gcc 4.4.3:
/c/sandbox/dev/play/voidreturn/vr.cpp:67: error: invalid use of void expression
The context for this is when you want to use it inside a templated class:
template< typename Host, typename Signature > class Method;
// Specialization for signatures with no parameters
template< typename Host, typename Return >
class Method< Host, Return () >
{
public:
typedef Return (Host::*MethodType)();
Method( Host * host, MethodType method ) : m_Host(host), m_Method(method) {}
Value operator()() { return Value( (m_Host->*m_Method)() ); }
private:
Host * m_Host;
MethodType m_Method;
};
Using this Method class on the method which returns something (namely intReturn) would look like:
Method< Subject, int () > intMeth( &subject, &Subject::intReturn );
Value intValue = intMeth();
However, doing this with the voidReturn method:
Method< Subject, void () > voidMeth( &subject, &Subject::voidReturn );
Value voidValue = voidMeth();
yields similar errors as above.
One solution is to further partially specialize Method for void return types:
template< typename Host >
class Method< Host, void () >
{
public:
typedef void Return;
typedef Return (Host::*MethodType)();
Method( Host * host, MethodType method ) : m_Host(host), m_Method(method) {}
Value operator()() { return (m_Host->*m_Method)(), Value(); }
private:
Host * m_Host;
MethodType m_Method;
};
Besides it just feeling ugly, I'm also wanting to specialize the Method class for X numbers of signature parameters, which already involves a lot of code duplication (hopefuly Boost.Preprocessor can help here), and then adding a specialization for void return types just doubles that duplication effort.
Is there anyway to avoid this second specialization for void return types?
You could use Return
and just specialize operator()
handling. No need to duplicate the whole template.
// I think it's a shame if c++0x really gets rid of std::identity. It's soo useful!
template<typename> struct t2t { };
// Specialization for signatures with no parameters
template< typename Host, typename Return >
class Method< Host, Return () >
{
public:
typedef Return (Host::*MethodType)();
Method( Host * host, MethodType method ) : m_Host(host), m_Method(method) {}
Value operator()() { return call(t2t<Return>()); }
private:
Value call(t2t<void>) { return Value(); }
template<typename T>
Value call(t2t<T>) { return Value((m_Host->*m_Method)()); }
private:
Host * m_Host;
MethodType m_Method;
};