Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is wrong with this variadic templates example?

The base class is :

#include <memory>

namespace cb{

template< typename R, typename ... Args >
class CallbackBase
{
public:
    typedef std::shared_ptr< CallbackBase< R, Args... > >
            CallbackPtr;

    virtual ~CallbackBase()
    {
    }
    virtual R Call(  Args ... args) = 0;
};
} // namespace cb

Derived class is this :

namespace cb{
template< typename R, typename ... Args >
class FunctionCallback : public CallbackBase< R, Args... >
{
public:
    typedef R (*funccb)(Args...);

    FunctionCallback( funccb cb_ ) : 
        CallbackBase< R, Args... >(),
        cb( cb_ )
    {
    }
    virtual ~FunctionCallback()
    {
    }
    virtual R Call(Args... args)
    {
      return cb( args... );
    }
private:
  funccb cb;
};
} // namespace cb

Function to create :

namespace cb{
template < typename R, typename ...Args >
typename CallbackBase< R, Args... >::CallbackBasePtr
    MakeCallback( typename FunctionCallback< R, Args... >::funccb cb )
{
    typename CallbackBase< R, Args... >::CallbackBasePtr
        p( new FunctionCallback< R, Args... >( cb )
);
    return p;
}
} // namespace cb

And the example :

bool Foo_1args( const int & t)
{
    return true;
}
int main()
{
    auto cbObj = cb::MakeCallback( & Foo_1args );
}

I keep getting this error :

error: no matching function for call to ‘MakeCallback(bool (*)(const int&))’
error: unable to deduce ‘auto’ from ‘<expression error>’

I tried to change it, but I couldn't figure out how to fix.

So, what is wrong? And how to fix this example?

like image 666
BЈовић Avatar asked Dec 01 '22 02:12

BЈовић


2 Answers

The problem might make sense with a simpler example. Try identifying the problem here:

template <typename T>
struct id { typedef T type; };

template <typename T>
void foo(typename id<T>::type x);

foo(5); // error

The problem is that the compiler cannot deduce what T should be; it's not directly used anywhere. You'd have to explicitly provide it: foo<int>(5), or let it deduce it in some other way:

template <typename T>
void foo(typename id<T>::type x, T y);

foo(5, 7); // okay, T is int because 7 is int

This makes sense: how could the compiler figure out which T's, supplied to id, result in id<T>::type matching? There could be specializations, and the entire thing would be costly anyway, if possible.


Likewise, there's nothing the compiler has available to deduce R and Args. Instead, you should do this:

template < typename R, typename ...Args >
typename CallbackBase< R, Args... >::CallbackBasePtr
    MakeCallback( R cb(Args...) )
{
    typename CallbackBase< R, Args... >::CallbackBasePtr
        p( new FunctionCallback< R, Args... >( cb ));

    return p;
}

Finally, you have other minor issues that need fixing, which Xeo has outlined.

like image 181
GManNickG Avatar answered Dec 04 '22 04:12

GManNickG


To recollect what I mentioned in the comments of the other answers:

  • First, as @GMan says, your argument of MakeCallback was non-deducible.
  • Second, your return type of MakeCallback was wrong. It should be CallbackPtr, as a CallbackBasePtr typedef doesn't exist. This lead to SFINAE kicking in and not considering your function as a possible function to call even when the argument was fixed.
  • Third, your FunctionCallback constructor wanted a funccb* pointer, while funccb already is a (function-)pointer, so you would have to pass a pointer-to-function-pointer, eg. new FunctionCallback(&cb)
like image 40
Xeo Avatar answered Dec 04 '22 03:12

Xeo