【问题标题】:Adding a callback to a variadic template class - impossible?向可变参数模板类添加回调 - 不可能吗?
【发布时间】:2013-02-22 20:23:37
【问题描述】:

我正在尝试创建一个模板类,它有一个 Add 方法,将函数回调附加到类,所以当我可以从那里使用指定的参数列表调用它时。它编译得很好,除了我调用回调的部分。它只是不接受参数,我已经尝试了我能想到的一切,但它仍然给我同样的“无法扩展参数包”错误。我正在使用 Visual Studio 2012 使用 Microsoft Visual C++ 编译器 2012 年 11 月 CTP (v120_CTP_Nov2012) 这是示例源: 模板 类可变参数 { 私人的: void (*)($arguments...) callbackPtr;

public:
    Variadic();
    ~Variadic();

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

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

然后我给它添加一个回调:

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

我使用 () 运算符执行它:

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

在 main.cpp 我做了一个小测试:

    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
}

问题出现在我构建时 - 它在这一行给了我 2 个错误:(callbackPtr)(arguments...); 错误是:

错误 C3546:“...”:没有可扩展的参数包 错误 C2065:“参数”:未声明的标识符

起初我认为这是语法问题,我没有正确传递 参数...,但我尝试了所有可能的方法,它仍然给我同样的错误。我不能在 google 中也可以找到有关“参数包扩展”的大量信息。我可能做错了什么?我确定我在某种程度上错误地使用了 (callbackPtr)(arguments. ..); 打电话,但不知道怎么打。

任何帮助将不胜感激。

【问题讨论】:

  • void (*)($arguments...) callbackPtr; 不应该是void (*callbackPtr)($arguments...);吗?
  • 嗯,如果我想在 std::vector 中添加多个像这样:std::vector)($arguments...)> handlers;* 用于多个事件处理程序?不确定在这种情况下命名如何工作。
  • 也许 MSVS 只是不接受 $ 字符
  • 那就是std::vector&lt;void(*)($arguments...)&gt; handlers;
  • 在 Visual Studio 上这是完全合法的,它以前一直对我有用,这是微软的事情:D

标签: c++ visual-studio-2012 callback variadic-templates


【解决方案1】:

在我回答之前,你应该知道一些事情:

  • Microsoft VC++ 2012 年 11 月版 CTP 不能很好地处理可变参数和函数指针/函数签名。在几乎所有情况下,都需要手动手动扩展它们。它很糟糕,但你必须忍受它,直到我们投入 VS 和 VC++ 的所有资金真正开花结果,并且我们得到一个具有其他编译器已经支持的大量 C++11 功能的编译器。

  • 传递函数指针并让编译器确定正确的类型比大多数人乍看之下猜测的要复杂一些。有很多类型 seducing 演绎和模板专业化。

除此之外,基于函数指针和成员函数的回调需要很多模板魔法和很多有趣的功能,而不仅仅是使用std::function&lt;&gt;。在我向您展示我最终使用的解决方案之前,我强烈建议您使用std::vector&lt;std::function&lt;[RETURNTYPE]( [PARAMS] )&gt; &gt;(或仅使用std::function 进行单次退货),以免您为解决所有问题而头疼。无论哪种情况,see my answer underneath @Insilico's for a Callback and Event system that works fine in GCC with variadic templates

对于在 VC++ 中工作的版本,正如我之前所说,您必须手动修改各种定义,我最终创建了一个回调类和一个事件类来执行此操作。它适用于多个回调,但如果需要,您可以将 Event 类简化为单个附加/回调:

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

};

【讨论】:

  • 这是一个糟糕的答案,你应该感到很糟糕。
  • @LightnessRacesInOrbit q__q 对不起。
  • 不错!我知道 ThePhD 正在使用 Visual C++。有没有人知道这是否与 Mac 上的 pre-C++-11 配合得很好? (叮当声)
  • 嗯。我很确定这可以在铿锵声中工作。你也可以做的只是把它扔到一个 C++ 文件中,然后试一试,看看它是否可以编译:除了构建和使用它之外,你不需要做任何花哨的事情。
猜你喜欢
  • 1970-01-01
  • 2021-10-01
  • 2023-03-07
  • 1970-01-01
  • 1970-01-01
  • 2016-12-01
  • 2013-05-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多