Din*_*aiz 72 c++ performance delegates
我知道"C++代表"的主题已经完成,而http://www.codeproject.com和http://stackoverflow.com都深深地涵盖了这个问题.
一般来说,似乎Don Clugston最快的代表是许多人的首选.还有一些其他流行的.
但是,我注意到这些文章中的大多数都是旧的(大约在2005年),并且许多设计选择似乎都是考虑到VC7之类的旧编译器.
我需要一个非常快速的代理实现音频应用程序.
我仍然需要它可移植(Windows,Mac,Linux),但我只使用现代编译器(VC9,VS2008 SP1和GCC 4.5.x中的编译器).
我的主要标准是:
此外,我真的不需要异国情调的功能.我只需要一个好的旧指针到方法的东西.无需支持静态方法,自由函数或类似的东西.
截至今天,推荐的方法是什么?仍然使用唐的版本?或者是否存在关于另一种选择的"社区共识"?
我真的不想使用Boost.signal/signal2,因为它在性能方面是不可接受的.对QT的依赖也是不可接受的.
此外,我在google搜索时看到了一些较新的库,例如cpp-events,但我找不到任何来自用户的反馈,包括SO.
In *_*ico 120
更新: 在"代码项目"上发布了一篇包含完整源代码和更详细讨论的文章.
嗯,指向方法的问题是它们的大小不一样.因此,我们不需要直接存储指向方法的指针,而是需要对它们进行"标准化",使它们具有恒定的大小.这就是Don Clugston在他的Code Project文章中试图实现的目标.他使用最流行的编译器的亲密知识这样做.我声称可以在"普通"C++中进行,而不需要这些知识.
请考虑以下代码:
void DoSomething(int)
{
}
void InvokeCallback(void (*callback)(int))
{
callback(42);
}
int main()
{
InvokeCallback(&DoSomething);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是使用普通旧函数指针实现回调的一种方法.但是,这不适用于对象中的方法.我们解决这个问题:
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;
}
Run Code Online (Sandbox Code Playgroud)
现在,我们有一个回调系统,可以用于免费和成员函数.然而,这是笨拙且容易出错的.但是,有一种模式 - 使用包装函数将静态函数调用"转发"到适当实例上的方法调用.我们可以使用模板来帮助解决这个问题 - 让我们尝试概括包装函数:
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;
}
Run Code Online (Sandbox Code Playgroud)
这仍然非常笨拙,但至少现在我们不必每次都写出一个包装函数(至少对于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;
}
Run Code Online (Sandbox Code Playgroud)
我们正在取得进步!但现在我们的问题是语法绝对可怕.语法似乎是多余的; 难道编译器不能从指针到方法本身找出类型吗?不幸的是没有,但我们可以帮助它.请记住,编译器可以通过函数调用中的模板参数推导来推断类型.那么我们为什么不从那开始呢?
template<typename R, class T, typename A1>
void DeduceMemCallback(R (T::*)(A1)) {}
Run Code Online (Sandbox Code Playgroud)
在函数内部,我们知道什么R,T和A1是.那么如果我们可以构造一个可以"保持"这些类型并从函数中返回它们的结构呢?
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>();
}
Run Code Online (Sandbox Code Playgroud)
既然DeduceMemCallbackTag知道了类型,为什么不把我们的包装函数作为静态函数呢?由于包装函数在其中,为什么不把代码放在其中构造我们的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>();
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
不过,这是一个冗长的表达式,但我们可以把它放到一个简单的宏(!)中:
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;
}
Run Code Online (Sandbox Code Playgroud)
我们可以Callback通过添加安全bool 来增强对象.禁用相等运算符也是一个好主意,因为无法比较两个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))
Run Code Online (Sandbox Code Playgroud)
用法示例:
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;
}
Run Code Online (Sandbox Code Playgroud)
我已经在Visual C++编译器(版本15.00.30729.01,VS 2008附带的版本)上对此进行了测试,并且您确实需要一个相当新的编译器来使用该代码.通过检查反汇编,编译器能够优化包装函数和DeduceMemCallback调用,减少到简单的指针赋值.
它很容易用于回调的两面,并且仅使用(我相信的)标准C++.我上面显示的代码适用于带有1个参数的成员函数,但可以推广到更多参数.通过允许支持静态函数,还可以进一步推广它.
请注意,该Callback对象不需要堆分配 - 由于这种"标准化"过程,它们具有恒定的大小.因此,可以让Callback对象成为更大类的成员,因为它具有默认构造函数.它也是可分配的(编译器生成的拷贝分配功能就足够了).由于模板,它也是类型安全的.
小智 10
我想用一些我自己的东西来关注@ Insilico的回答.
在我偶然发现这个答案之前,我试图找出快速回调,它没有产生任何开销,只能通过功能签名进行唯一比较/识别.我最终创造的东西 - 在Klingons发生烧烤的一些严肃的帮助- 适用于所有功能类型(除了Lambdas,除非你存储Lambda,但不要尝试它,因为它真的很难,很难做到和可能会导致一个机器人向你证明它是多么困难,并让你为它吃屎).感谢@sehe,@ nixeagle,@ StackedCrooked,@ CatPlusPlus,@ Xeo,@ DeadMG以及@Insilico在创建活动系统方面的帮助.随意使用,随意使用.
无论如何,一个例子是关于ideone,但是源代码也在这里供你使用(因为,因为Liveworkspace失败了,我不相信他们阴暗的编译服务.谁知道ideone何时会崩溃?!).我希望这对于那些不忙于Lambda/Function的人来说是有用的 - 将世界分解为碎片:
重要说明:截至目前(2012年11月28日,下午9:35)此可变版本不适用于Microsoft VC++ 2012 November CTP(米兰).如果你想使用它,你将不得不摆脱所有可变参数的东西,并明确枚举参数的数量(并可能模板 - 专门为1参数类型Eventfor void),使其工作.这是一种痛苦,在我累了之前我只能设法写出4个参数(并且决定传递超过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;
}
};
Run Code Online (Sandbox Code Playgroud)