Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

void return value from a function used as input to a templated function is seen as a parameter

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?

like image 474
Skillcheck Avatar asked Sep 06 '10 02:09

Skillcheck


People also ask

How do you return a value from a void function?

In lieu of a data type, void functions use the keyword "void." A void function performs a task, and then control returns back to the caller--but, it does not return a value. You may or may not use the return statement, as there is no return value.

What do you mean by void return type?

void (C++) When used as a function return type, the void keyword specifies that the function doesn't return a value. When used for a function's parameter list, void specifies that the function takes no parameters. When used in the declaration of a pointer, void specifies that the pointer is "universal."

How do you write a void function in C++?

A void function with value parameters are declared by enclosing the list of types for the parameter list in the parentheses. To activate a void function with value parameters, we specify the name of the function and provide the actual arguments enclosed in parentheses.

What is a template parameter?

In UML models, template parameters are formal parameters that once bound to actual values, called template arguments, make templates usable model elements. You can use template parameters to create general definitions of particular types of template.


2 Answers

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;
};
like image 95
Johannes Schaub - litb Avatar answered Nov 20 '22 11:11

Johannes Schaub - litb


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.

like image 26
Potatoswatter Avatar answered Nov 20 '22 11:11

Potatoswatter