5年后,有没有比“最快可能的C ++代表”更好的东西?

我知道“C ++委托”这个话题已经被完成了,而且这两个方法都深深的涵盖了这个问题。

一般来说, 克鲁格斯顿(Don Clugston)最快的代表似乎是许多人的首选。 还有其他一些stream行的。

不过,我注意到这些文章中的大部分都是旧的(大约在2005年),许多deviseselect似乎都是考虑到像VC7这样的旧编译器。

我需要一个非常快速的委托实施audio应用程序。

我仍然需要它是可移植的(Windows,Mac,Linux),但我只使用现代编译器(VC9,在VS2008 SP1和GCC 4.5.x)。

我的主要标准是:

  • 它一定是快!
  • 它必须与新版本的编译器向前兼容。 我对唐的执行有一些怀疑,因为他明确指出它不符合标准。
  • 可选地,KISS语法和易用性很好
  • 多播将是很好,虽然我相信这是很容易围绕任何代表图书馆

而且,我并不需要奇特的function。 我只需要很好的旧的指针方法的东西。 无需支持静态方法,免费function或类似的东西。

到今天为止,推荐的方法是什么? 仍然使用唐的版本 ? 还是有关于另一种select的“社区共识”?

我真的不想使用Boost.signal / signal2,因为在性能方面它是不可接受的。 对QT的依赖也是不可接受的。

此外,我看到一些较新的图书馆,而谷歌search,如cpp事件,但我找不到任何来自用户的反馈,包括SO。

更新: 一个包含完整的源代码和更详细的讨论的文章已发布在代码项目上。

那么,指向方法的问题是它们不是全部相同的大小。 因此,我们不需要直接存储指向方法的指针,而是需要“标准化”它们,使它们具有恒定的大小。 这就是克鲁格斯顿试图在他的代码项目文章中实现的。 他这样做是使用最受欢迎的编译器的知识。 我断言在“普通”C ++中可以做到这一点,而不需要这些知识。

考虑下面的代码:

void DoSomething(int) { } void InvokeCallback(void (*callback)(int)) { callback(42); } int main() { InvokeCallback(&DoSomething); return 0; } 

这是使用普通的旧函数指针实现callback的一种方法。 但是,这不适用于对象中的方法。 我们来解决这个问题:

 class Foo { public: void DoSomething(int) {} static void DoSomethingWrapper(void* obj, int param) { static_cast<Foo*>(obj)->DoSomething(param); } }; void InvokeCallback(void* instance, void (*callback)(void*, int)) { callback(instance, 42); } int main() { Foo f; InvokeCallback(static_cast<void*>(&f), &Foo::DoSomethingWrapper); return 0; } 

现在,我们有一个可用于免费和成员函数的callback系统。 但是,这很笨拙,容易出错。 但是,有一种模式 – 使用包装函数将静态函数调用“转发”到正确实例上的方法调用。 我们可以使用模板来帮助解决这个问题 – 让我们尝试概括一下wrapper函数:

 template<typename R, class T, typename A1, R (T::*Func)(A1)> R Wrapper(void* o, A1 a1) { return (static_cast<T*>(o)->*Func)(a1); } class Foo { public: void DoSomething(int) {} }; void InvokeCallback(void* instance, void (*callback)(void*, int)) { callback(instance, 42); } int main() { Foo f; InvokeCallback(static_cast<void*>(&f), &Wrapper<void, Foo, int, &Foo::DoSomething> ); return 0; } 

这仍然是非常笨拙的,但至less现在我们不必每次都写出一个包装函数(至less在1个参数的情况下)。 另一件我们可以概括的事实是,我们总是传递一个指向void*的指针。 不要把它作为两个不同的值来传递,为什么不把它们放在一起呢? 而我们这样做的时候,为什么不把它推广呢? 嘿,让我们把一个operator()()所以我们可以把它称为一个函数!

 template<typename R, typename A1> class Callback { public: typedef R (*FuncType)(void*, A1); Callback(void* o, FuncType f) : obj(o), func(f) {} R operator()(A1 a1) const { return (*func)(obj, a1); } private: void* obj; FuncType func; }; template<typename R, class T, typename A1, R (T::*Func)(A1)> R Wrapper(void* o, A1 a1) { return (static_cast<T*>(o)->*Func)(a1); } class Foo { public: void DoSomething(int) {} }; void InvokeCallback(Callback<void, int> callback) { callback(42); } int main() { Foo f; Callback<void, int> cb(static_cast<void*>(&f), &Wrapper<void, Foo, int, &Foo::DoSomething>); InvokeCallback(cb); return 0; } 

我们正在取得进展! 但是现在我们的问题是语法是绝对可怕的。 语法看起来多余; 编译器能不能找出指向方法本身的types? 不幸的是,但我们可以帮助它。 请记住,编译器可以通过函数调用中的模板参数推导来推断types。 那我们为什么不从这个开始呢?

 template<typename R, class T, typename A1> void DeduceMemCallback(R (T::*)(A1)) {} 

在函数内部,我们知道RTA1是什么。 那么,如果我们可以构造一个可以“保存”这些types并从函数中返回的结构呢?

 template<typename R, class T, typename A1> struct DeduceMemCallbackTag { }; template<typename R, class T, typename A1> DeduceMemCallbackTag2<R, T, A1> DeduceMemCallback(R (T::*)(A1)) { return DeduceMemCallbackTag<R, T, A1>(); } 

由于DeduceMemCallbackTag知道types,为什么不把它作为一个静态函数的封装函数呢? 既然包装函数在里面,为什么不把代码构造成我们的Callback对象呢?

 template<typename R, typename A1> class Callback { public: typedef R (*FuncType)(void*, A1); Callback(void* o, FuncType f) : obj(o), func(f) {} R operator()(A1 a1) const { return (*func)(obj, a1); } private: void* obj; FuncType func; }; template<typename R, class T, typename A1> struct DeduceMemCallbackTag { template<R (T::*Func)(A1)> static R Wrapper(void* o, A1 a1) { return (static_cast<T*>(o)->*Func)(a1); } template<R (T::*Func)(A1)> inline static Callback<R, A1> Bind(T* o) { return Callback<R, A1>(o, &DeduceMemCallbackTag::Wrapper<Func>); } }; template<typename R, class T, typename A1> DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1)) { return DeduceMemCallbackTag<R, T, A1>(); } 

C ++标准允许我们调用实例(!)的静态函数:

 class Foo { public: void DoSomething(int) {} }; void InvokeCallback(Callback<void, int> callback) { callback(42); } int main() { Foo f; InvokeCallback( DeduceMemCallback(&Foo::DoSomething) .Bind<&Foo::DoSomething>(&f) ); return 0; } 

尽pipe如此,这是一个冗长的expression式,但我们可以把它放到一个简单的macros(!)中:

 template<typename R, typename A1> class Callback { public: typedef R (*FuncType)(void*, A1); Callback(void* o, FuncType f) : obj(o), func(f) {} R operator()(A1 a1) const { return (*func)(obj, a1); } private: void* obj; FuncType func; }; template<typename R, class T, typename A1> struct DeduceMemCallbackTag { template<R (T::*Func)(A1)> static R Wrapper(void* o, A1 a1) { return (static_cast<T*>(o)->*Func)(a1); } template<R (T::*Func)(A1)> inline static Callback<R, A1> Bind(T* o) { return Callback<R, A1>(o, &DeduceMemCallbackTag::Wrapper<Func>); } }; template<typename R, class T, typename A1> DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1)) { return DeduceMemCallbackTag<R, T, A1>(); } #define BIND_MEM_CB(memFuncPtr, instancePtr) \ (DeduceMemCallback(memFuncPtr).Bind<(memFuncPtr)>(instancePtr)) class Foo { public: void DoSomething(int) {} }; void InvokeCallback(Callback<void, int> callback) { callback(42); } int main() { Foo f; InvokeCallback(BIND_MEM_CB(&Foo::DoSomething, &f)); return 0; } 

我们可以通过添加一个安全的布尔来增强Callback对象。 禁止相等运算符也是一个好主意,因为不可能比较两个Callback对象。 更好的是,使用部分专业化来允许“首选语法”。 这给了我们:

 template<typename FuncSignature> class Callback; template<typename R, typename A1> class Callback<R (A1)> { public: typedef R (*FuncType)(void*, A1); Callback() : obj(0), func(0) {} Callback(void* o, FuncType f) : obj(o), func(f) {} R operator()(A1 a1) const { return (*func)(obj, a1); } typedef void* Callback::*SafeBoolType; operator SafeBoolType() const { return func != 0? &Callback::obj : 0; } bool operator!() const { return func == 0; } private: void* obj; FuncType func; }; template<typename R, typename A1> // Undefined on purpose void operator==(const Callback<R (A1)>&, const Callback<R (A1)>&); template<typename R, typename A1> void operator!=(const Callback<R (A1)>&, const Callback<R (A1)>&); template<typename R, class T, typename A1> struct DeduceMemCallbackTag { template<R (T::*Func)(A1)> static R Wrapper(void* o, A1 a1) { return (static_cast<T*>(o)->*Func)(a1); } template<R (T::*Func)(A1)> inline static Callback<R (A1)> Bind(T* o) { return Callback<R (A1)>(o, &DeduceMemCallbackTag::Wrapper<Func>); } }; template<typename R, class T, typename A1> DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1)) { return DeduceMemCallbackTag<R, T, A1>(); } #define BIND_MEM_CB(memFuncPtr, instancePtr) \ (DeduceMemCallback(memFuncPtr).Bind<(memFuncPtr)>(instancePtr)) 

用法示例:

 class Foo { public: float DoSomething(int n) { return n / 100.0f; } }; float InvokeCallback(int n, Callback<float (int)> callback) { if(callback) { return callback(n); } return 0.0f; } int main() { Foo f; float result = InvokeCallback(97, BIND_MEM_CB(&Foo::DoSomething, &f)); // result == 0.97 return 0; } 

我已经在Visual C ++编译器(版本15.00.30729.01,VS 2008自带的版本)上进行了testing,而且您确实需要一个相当新的编译器来使用代码。 通过检查反汇编,编译器能够优化掉包装函数和DeduceMemCallback调用,减less到简单的指针分配。

在callback的两边使用起来很简单,只使用(我相信是)标准的C ++。 我上面显示的代码适用于具有1个参数的成员函数,但可以推广到更多的参数。 它也可以通过支持静态function进一步推广。

请注意, Callback对象不需要堆分配 – 由于这个“标准化”过程,它们的大小不变。 正因为如此,可能有一个Callback对象是大类的成员,因为它有一个默认的构造函数。 它也是可分配的(编译器生成的复制分配函数就足够了)。 这也是types安全,感谢模板。

我想用@Instilico的回答跟我一些自己的东西。

在我偶然发现这个答案之前,我试图找出快速的callback,并且没有发生开销,只能通过函数签名进行唯一的比较/识别。 我最终创造的 – 在发生在烧烤场的克林贡的一些严肃的帮助下 – 适用于所有的functiontypes(除了Lambda,除非你存储Lambda,但不要尝试它,因为这是非常困难和难以做到的)可能会导致一个机器人向你certificate这是多么困难,并让你吃它的狗屎 )。 感谢@sehe,@ nixeagle,@StackedCrooked,@CatPlusPlus,@ Xeo,@DeadMG,当然@Insilico帮助创build事件系统。 随意使用,因为你的愿望。

无论如何,一个例子是ideone,但源代码也在这里供您使用(因为,因为Liveworkspaceclosures,我不相信他们阴暗的编译服务,谁知道什么时候ideone会下降?!)。 我希望这对于不忙于Lambda / Function的人来说是有用的:

重要提示:截至目前(28/11/2012,9:35 PM)这个可变参数版本将不适用于微软VC ++ 2012十一月CTP(米兰)。 如果你想使用它,你将不得不摆脱所有的可变参数,并明确地枚举参数的数量(也可能模板专门化1为void Eventtypes),使其工作。 这是一种痛苦,在我疲倦之前,我只能写出四个论点(并决定通过4个以上的论点是有点延伸)。

源示例

资源:

 #include <iostream> #include <vector> #include <utility> #include <algorithm> template<typename TFuncSignature> class Callback; template<typename R, typename... Args> class Callback<R(Args...)> { public: typedef R(*TFunc)(void*, Args...); Callback() : obj(0), func(0) {} Callback(void* o, TFunc f) : obj(o), func(f) {} R operator()(Args... a) const { return (*func)(obj, std::forward<Args>(a)...); } typedef void* Callback::*SafeBoolType; operator SafeBoolType() const { return func? &Callback::obj : 0; } bool operator!() const { return func == 0; } bool operator== (const Callback<R (Args...)>& right) const { return obj == right.obj && func == right.func; } bool operator!= (const Callback<R (Args...)>& right) const { return obj != right.obj || func != right.func; } private: void* obj; TFunc func; }; namespace detail { template<typename R, class T, typename... Args> struct DeduceConstMemCallback { template<R(T::*Func)(Args...) const> inline static Callback<R(Args...)> Bind(T* o) { struct _ { static R wrapper(void* o, Args... a) { return (static_cast<T*>(o)->*Func)(std::forward<Args>(a)...); } }; return Callback<R(Args...)>(o, (R(*)(void*, Args...)) _::wrapper); } }; template<typename R, class T, typename... Args> struct DeduceMemCallback { template<R(T::*Func)(Args...)> inline static Callback<R(Args...)> Bind(T* o) { struct _ { static R wrapper(void* o, Args... a) { return (static_cast<T*>(o)->*Func)(std::forward<Args>(a)...); } }; return Callback<R(Args...)>(o, (R(*)(void*, Args...)) _::wrapper); } }; template<typename R, typename... Args> struct DeduceStaticCallback { template<R(*Func)(Args...)> inline static Callback<R(Args...)> Bind() { struct _ { static R wrapper(void*, Args... a) { return (*Func)(std::forward<Args>(a)...); } }; return Callback<R(Args...)>(0, (R(*)(void*, Args...)) _::wrapper); } }; } template<typename R, class T, typename... Args> detail::DeduceConstMemCallback<R, T, Args...> DeduceCallback(R(T::*)(Args...) const) { return detail::DeduceConstMemCallback<R, T, Args...>(); } template<typename R, class T, typename... Args> detail::DeduceMemCallback<R, T, Args...> DeduceCallback(R(T::*)(Args...)) { return detail::DeduceMemCallback<R, T, Args...>(); } template<typename R, typename... Args> detail::DeduceStaticCallback<R, Args...> DeduceCallback(R(*)(Args...)) { return detail::DeduceStaticCallback<R, Args...>(); } template <typename... T1> class Event { public: typedef void(*TSignature)(T1...); typedef Callback<void(T1...)> TCallback; typedef std::vector<TCallback> InvocationTable; protected: InvocationTable invocations; public: const static int ExpectedFunctorCount = 2; Event() : invocations() { invocations.reserve(ExpectedFunctorCount); } template <void (* TFunc)(T1...)> void Add() { TCallback c = DeduceCallback(TFunc).template Bind<TFunc>(); invocations.push_back(c); } template <typename T, void (T::* TFunc)(T1...)> void Add(T& object) { Add<T, TFunc>(&object); } template <typename T, void (T::* TFunc)(T1...)> void Add(T* object) { TCallback c = DeduceCallback(TFunc).template Bind<TFunc>(object); invocations.push_back(c); } template <typename T, void (T::* TFunc)(T1...) const> void Add(T& object) { Add<T, TFunc>(&object); } template <typename T, void (T::* TFunc)(T1...) const> void Add(T* object) { TCallback c = DeduceCallback(TFunc).template Bind<TFunc>(object); invocations.push_back(c); } void Invoke(T1... t1) { for(size_t i = 0; i < invocations.size() ; ++i) invocations[i](std::forward<T1>(t1)...); } void operator()(T1... t1) { Invoke(std::forward<T1>(t1)...); } size_t InvocationCount() { return invocations.size(); } template <void (* TFunc)(T1...)> bool Remove () { return Remove (DeduceCallback(TFunc).template Bind<TFunc>()); } template <typename T, void (T::* TFunc)(T1...)> bool Remove (T& object) { return Remove <T, TFunc>(&object); } template <typename T, void (T::* TFunc)(T1...)> bool Remove (T* object) { return Remove (DeduceCallback(TFunc).template Bind<TFunc>(object)); } template <typename T, void (T::* TFunc)(T1...) const> bool Remove (T& object) { return Remove <T, TFunc>(&object); } template <typename T, void (T::* TFunc)(T1...) const> bool Remove (T* object) { return Remove (DeduceCallback(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; } };