1

I'm trying to make a templated class, which has an Add method, that attaches a function callback to the class, so when I can then call it from there with the specified arguments list.It compiles fine, except for the part where I invoke the callback.It just doesn't accept the arguments, I've tried everything I can think of, but it still gives me the same "cannot expand parameter pack" error.Im using Visual Studio 2012 with the Microsoft Visual C++ Compiler Nov 2012 CTP (v120_CTP_Nov2012) Here is the example source: template class Variadic { private: void (*)($arguments...) callbackPtr;

public:
    Variadic();
    ~Variadic();

    void Attach(void (*callback)($arguments...));

    void operator()($arguments... arguments);
};

I then add a callback to it:

template<typename... $arguments>
void Variadic<$arguments...>::Attach(void (*callback)($arguments...))
{
    callbackPtr = callback;
}

And with the () operator I execute it:

template<typename... $arguments>
void Variadic<$arguments...>::operator()($arguments... arguments)
{
    (callbackPtr)(arguments...);
}

In main.cpp I do a small test:

    void test(int testInt, float testFloat)
{
    //DoNothing
}

int main()
{
Variadic<int, float> var; //create one that will have a callback that takes an int and a float argument
var.Attach(test); //attach test, which takes an int and a float as arguments
var(2, 3.0f); //try to call it
}

The problem comes when I build - it gives me 2 errors at this exact line: (callbackPtr)(arguments...); The errors are:

error C3546: '...' : there are no parameter packs available to expand error C2065: 'arguments' : undeclared identifier

At first I thought it was a syntax problem and I wasn't passing arguments... properly, but I tried every way possible, it still gives me the same error.I can't find much info on "parameter pack expansion" in google either.What could I be doing wrong?I'm sure I'm somehow incorrectly using the (callbackPtr)(arguments...); call, but can't figure out how.

Any help would be appreciated.

ulak blade
  • 2,515
  • 5
  • 37
  • 81

1 Answers1

1

Before I get into the answer, some things you should know:

  • The Microsoft VC++ November 2012 CTP does not play nice with Variadics and Function Pointers / Function Signatures. In almost all cases, it is necessary to expand them manually by hand. It sucks majorly, but you'll have to live with it until all that money we throw at VS and VC++ actually bears fruit and we get a compiler with a good chunk of C++11 features that other compilers are already supporting.

  • Passing function pointers and having the compiler determine the right type is a little trickier than most people would guess at first blush. There's a lot of type seducing deducting and template specialization that goes into it.

With that aside, it takes a lot of template magic and a lot of interesting functionality to have callbacks based on function pointers and member functions, rather than just using std::function<>. Before I show you the solution I ended up using, I seriously encourage you to use a std::vector<std::function<[RETURNTYPE]( [PARAMS] )> > (or just std::function for a single return) to save yourself the massive headache of trying to make this all work out. In either case, see my answer underneath @Insilico's for a Callback and Event system that works fine in GCC with variadic templates.

For a version that works in VC++, as I said before, you have to hack away at the various definitions manually, which I ended up creating a Callback Class and an Event class to do. It's for multiple callbacks, but you can simplify the Event class to be a single attach/callback if you need to:

template<typename TFuncSignature>
class Callback;

/////////////////
/* 2 ARGUMENT  */
/////////////////

template<typename R, typename T1, typename T2>
class Callback<R (T1, T2)> {
public:
    typedef R (*TFunc)(void*, T1, T2);

    const static size_t Arity = 2;

    Callback() : obj(0), func(0) {}
    Callback(void* o, TFunc f) : obj(o), func(f) {}

    R operator()(T1 t1, T2 t2) const {
        return (*func)(obj, t1, t2);
    }

    typedef void* Callback::*SafeBoolType;
    operator SafeBoolType () const {
        return func != 0? &Callback::obj : 0;
    }

    bool operator! () const {
        return func == 0;
    }

    bool operator== ( const Callback<R (T1, T2)>& right ) const {
        return obj == right.obj && func == right.func;
    }

    bool operator!= ( const Callback<R (T1, T2)>& right ) const {
        return obj != right.obj || func != right.func;
    }

private:
    void* obj;
    TFunc func;
};

namespace detail {
    template<typename R, class T, typename T1, typename T2>
    struct DeduceConstMemCallback2 { 
        template<R(T::*Func)(T1, T2) const> inline static Callback<R(T1, T2)> Bind(T* o) {
            struct _ { static R wrapper(void* o, T1 t1, T2 t2) { return (static_cast<T*>(o)->*Func)(std::forward<T1>(t1, t2); } };
            return Callback<R(T1, T2)>(o, (R(*)(void*, T1, T2)) _::wrapper);
        }
    };

    template<typename R, class T, typename T1, typename T2>
    struct DeduceMemCallback2 { 
        template<R(T::*Func)(T1, T2)> inline static Callback<R(T1, T2)> Bind(T* o) {
            struct _ { static R wrapper(void* o, T1 t1, T2 t2) { return (static_cast<T*>(o)->*Func)(t1, t2)); } };
            return Callback<R(T1, T2)>(o, (R(*)(void*, T1, T2)) _::wrapper);
        }
    };

    template<typename R, typename T1, typename T2>
    struct DeduceStaticCallback2 { 
        template<R(*Func)(T1, T2)> inline static Callback<R(T1, T2)> Bind() { 
            struct _ { static R wrapper(void*, T1 t1, T2 t2) { return (*Func)(t1), t2); } };
            return Callback<R(T1, T2)>(0, (R(*)(void*, T1, T2)) _::wrapper); 
        }
    };
}

template<typename R, class T, typename T1, typename T2>
detail::DeduceConstMemCallback2<R, T, T1, T2> DeduceCallback2(R(T::*)(T1, T2) const) {
    return detail::DeduceConstMemCallback2<R, T, T1, T2>();
}

template<typename R, class T, typename T1, typename T2>
detail::DeduceMemCallback2<R, T, T1, T2> DeduceCallback2(R(T::*)(T1, T2)) {
    return detail::DeduceMemCallback2<R, T, T1, T2>();
}

template<typename R, typename T1, typename T2>
detail::DeduceStaticCallback2<R, T1, T2> DeduceCallback2(R(*)(T1, T2)) {
    return detail::DeduceStaticCallback2<R, T1, T2>();
}

template <typename T1, typename T2> class Event2 {
public:
    typedef void(* TSignature)(T1, T2);
    typedef Callback<void(T1, T2)> TCallback;
    typedef std::vector<TCallback> InvocationTable;

protected:
    InvocationTable invocations;

public:
    const static int ExpectedFunctorCount = 2;

    Event2 () : invocations() {
        invocations.reserve( ExpectedFunctorCount );
    }

    Event2 ( int expectedfunctorcount ) : invocations() {
        invocations.reserve( expectedfunctorcount );
    }

    template <void (* TFunc)(T1, T2)> void Add (  ) {
        TCallback c = DeduceCallback2( TFunc ).template Bind< TFunc >( );
        invocations.push_back( c );
    }

    template <typename T, void (T::* TFunc)(T1, T2)> void Add ( T& object ) {
        Add<T, TFunc>( &object );
    }

    template <typename T, void (T::* TFunc)(T1, T2)> void Add ( T* object ) {
        TCallback c = DeduceCallback2( TFunc ).template Bind< TFunc >( object );
        invocations.push_back( c );
    }

    template <typename T, void (T::* TFunc)(T1, T2) const> void Add ( T& object ) {
        Add<T, TFunc>( &object );
    }

    template <typename T, void (T::* TFunc)(T1, T2) const> void Add ( T* object ) {
        TCallback c = DeduceCallback2( TFunc ).template Bind< TFunc >( object );
        invocations.push_back( c );
    }

    void Invoke ( T1 t1, T2 t2 ) {
        size_t i;
        for ( i = 0; i < invocations.size(); ++i ) {
            invocations[i]( t1, t2 );
        }
    }

    void operator() ( T1 t1, T2 t2 ) {
        size_t i;
        for ( i = 0; i < invocations.size(); ++i ) {
            invocations[i]( t1, t2 );
        }
    }

    size_t InvocationCount ( ) {
        return invocations.size( );
    }

    template <void (* TFunc)(T1, T2)> bool Remove ()          
    { return Remove (DeduceCallback2(TFunc).template Bind<TFunc>()); } 
    template <typename T, void (T::* TFunc)(T1, T2)> bool Remove (T& object) 
    { return Remove <T, TFunc>(&object); } 
    template <typename T, void (T::* TFunc)(T1, T2)> bool Remove (T* object) 
    { return Remove (DeduceCallback2(TFunc).template Bind<TFunc>(object)); } 
    template <typename T, void (T::* TFunc)(T1, T2) const> bool Remove (T& object) 
    { return Remove <T, TFunc>(&object); } 
    template <typename T, void (T::* TFunc)(T1, T2) const> bool Remove (T* object) 
    { return Remove (DeduceCallback2(TFunc).template Bind<TFunc>(object)); } 

protected:

    bool Remove( TCallback const& target ) {
        auto it = std::find(invocations.begin(), invocations.end(), target);
        if ( it == invocations.end()) 
            return false;
        invocations.erase(it);
        return true;
    }

};
Community
  • 1
  • 1
  • 1
    This is a terrible answer, and you should feel terrible. – Lightness Races in Orbit Feb 22 '13 at 21:32
  • @LightnessRacesInOrbit q__q I'm sorry. –  Feb 22 '13 at 21:33
  • nice! I know ThePhD is using Visual C++. Does anyone else know if this plays nice with pre-C++-11 on a mac? (clang) – Dave Mar 05 '13 at 18:38
  • Eh. I'm pretty sure this can work on clang. What you could also do is just throw it in a C++ file and give ti a try and see if it compiles: it's not like you have to do anything fancy except build and use it works. –  Mar 05 '13 at 18:45