Question

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?

Was it helpful?

Solution

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;
};

OTHER TIPS

No, there is absolutely no way to pass a void. It is an irregularity in the language.

The function argument list (void) is translated as (). Bjarne prefers the latter to the former, and begrudgingly allowed the C convention as a very limited kind of syntactic sugar. You can't even substitute a typedef alias of void, and you certainly can't have any other arguments.

I personally think this is a bad idea. If you can write void(expr), then you should be able to "initialize" an anonymous argument of type void. If you could also write a function with an arbitrary number of void arguments, there would be a way to execute a number of expressions in unspecified order, which would express concurrency in a way.

As for handling different-sized argument lists (also known as variadic), see variadic templates in C++0x before you start trying to learn Boost Preprocessor.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top