views:

103

answers:

3

Given the following template:

template<class T>
class Container
{
private:

    boost::function<T> f;
};

... and its instantiation, perhaps as follows:


    Container<bool(int, int)> myContainer;

, is there a way to access the return type of the function description and compile conditionally against it? For example, if the caller specifies his function returns bool (as in the above case), I want to include a function that returns a value. If he specifies that the function is void, I don't want this function to be included. For example:


// Include if the return type of T is void
template<class T1, class T2>
void DoSomething(T1 t1, T2 t2)
{
    f(t1, t2);
}

// Include if the return type of T is not void
template<class T1, class T2>
***whatever the return type is*** DoSomething(T1 t1, T2 t2)
{
    return f(t1, t2);
}

I'm guessing there is a solution here, but it probably involves some horrendously obfuscated template meta-programming solution. I know Gregor Cantor went mad contemplating infinity... template meta-programming kind-of has the same effect on me :p.

Thanks for any thoughts you might have.

RobinsonT

Edit: Obviously this can be solved by implementing a different class (perhaps derived from a common base), one called VoidContainer and the other called ReturnsContainer (or similar). However this seems a little unsatisfactory to me...

+3  A: 

Yes, you can use boost::function_traits, which has a result_type typedef.

James McNellis
What would that look like? I did play with is_void and enable_if but couldn't get anything to compile using them. Can I write is_void<T> in the above example, where T is function prototype bool(int, int)? Or do I have to pass in the result_type trait to the is_void function? Function traits in this case is a on T. So do I write function_traits<T>::result_type?
Robinson
@robinsont: `is_void<T>` is false if `T` is a function type, so, yes, you'd basically need to use `boost::is_void<typename boost::function_traits<T>::result_type>::value` (or something close to that; I don't have Boost on this lappy to test, and C++0x doesn't have `function_traits`).
James McNellis
A: 

Depending on what you want to so, maybe you are making things more complicated than necessary. If the f you call in the void case is a void function itself, you can just keep the return.

Explicitly returning a "void value" is ok:

void f() {
}

void g() {
   return f();
}
sth
+2  A: 

I don't think you actually need to specialize for void return type. A void function is allowed to return the "result" of another void function for exactly this scenario.

void foo() { }
void bar() { return foo(); } //this is OK

int main()
{
    bar();
}

So your only problem would be how to determine the return type.

It appears that boost::function has a typedef for result_type (see http://beta.boost.org/doc/libs/1_37_0/doc/html/boost/functionN.html)

#include <boost/function.hpp>


template<class T>
class Container
{
public:
    typedef typename boost::function<T>::result_type result_type;
private:

    boost::function<T> f;
};

Container<bool(int, int)>::result_type r = true;

Edit: Now that you know what the result_type is, and you do need to distinguish between void/non-void results, you can employ enable_if and disable_if. The only complication is that those only work with function templates, so a non-template foo calls a templated do_foo.

#include <boost/function.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>
#include <cstdio>

template<class T>
class Container
{
public:
    typedef typename boost::function<T>::result_type result_type;


    result_type foo() 
    {
        return do_foo<result_type>();
        //note that this still works because you can return the void result! :)
    }
private:
    //use this if the result_type is void
    template <class U>
    typename boost::enable_if<boost::is_same<U, void>, U >::type do_foo()
    {
        std::puts("for void");
    }

    //else
    template <class U>
    typename boost::disable_if<boost::is_same<U, void>, U>::type do_foo()
    {
        std::puts("other");
        return U();
    }
private:

    boost::function<T> f;
};


int main()
{
    Container<void()> a;
    a.foo();

    Container<int()> b;
    b.foo();
}
UncleBens
For void return type, it's true you don't need to specialise in the case I gave above. But suppose I have a collection of boost::function<f> and that I want to store the result of each call to f() [0..n] into a collection, assuming that DoSomething returns a collection now rather than ::result_type. In the void case a collection won't compile (std::list<void>) and, indeed, wouldn't make any sense even if it did. That's why I wanted something that would discriminate between the two cases and not try to instantiate the non-void function if the return type of T is void.
Robinson
@robinsont: Edited answer to show how `enable_if` could be used to distinguish between the two cases.
UncleBens
Thanks very much all. This is extremely helpful.
Robinson