K
In den Beispielen des OPs werden nur freie Funktionen oder statische Elementfunktionen benutzt. Hier reichen Funktionszeiger sogar aus. In C# kann man über "Delegates" auch nicht-statische Elementfunktionen ("Objekt-Methoden") aufrufen. Das ist sogar der eigentliche Witz an diesen Dingern. Das und noch mehr bekommst Du mit boost/std::tr1 function und boost/std::tr1 bind.
Wenn Du Dich auf freie Funktionen und Elementfunktionen beschränken willst -- also so, wie es bei C# ist, ohne "Funktoren" -- dann kann man das auch mit etwas weniger Overhead erledigen. Man kommt ohne dynamischen Speicher aus und kann dadurch, dass Funktionszeiger als Template-Parameter benutzt werden, Inlining ausnutzen. Das sollte dann mit einem guten Compiler recht flott laufen.
Folgendes kompiliert bei mir mit
g++ -std=c++0x -Wall delegate.cpp
(Ich nutze hier die experimentellen C++0x Erweiterungen des GCCs, um beliebig viele Funktionsargumente unterstützen zu können.)
#include <stdexcept>
#include <utility>
template<class Ret, class...Args>
struct wrapper
{
template<Ret(*FunPtr)(Args...)>
struct free_fun
{
static Ret invoke(void*, Args... args)
{ return FunPtr(std::forward<Args>(args)...); }
};
template<class C, Ret (C::*MemFun)(Args...)>
struct mem_fun_nonconst
{
static Ret invoke(void* obj, Args... args)
{ return (static_cast<C*>(obj)->*MemFun)(std::forward<Args>(args)...); }
};
template<class C, Ret (C::*MemFun)(Args...) const>
struct mem_fun_const
{
static Ret invoke(void* obj, Args... args)
{ return (static_cast<C const*>(obj)->*MemFun)(std::forward<Args>(args)...); }
};
};
class bad_function_call : public std::logic_error
{
public:
bad_function_call()
: logic_error("bad function call") {}
};
template<typename FunSig> class delegate;
template<class Ret, class... Args>
class delegate<Ret(Args...)>
{
Ret (*fun)(void*, Args...);
void *data;
static Ret unbound(void*, Args...)
{ throw bad_function_call(); }
public:
delegate() : fun(&unbound), data(0) {}
Ret operator()(Args... args) const
{ return fun(data,std::forward<Args>(args)...); }
template<Ret(*FunPtr)(Args...)>
void init()
{
typedef typename wrapper<Ret,Args...>::template free_fun<FunPtr> w;
fun = &w::invoke;
data = 0;
}
template<class C, Ret(C::*MemFun)(Args...)>
void init(C* obj)
{
typedef typename wrapper<Ret,Args...>::template mem_fun_nonconst<C,MemFun> w;
fun = &w::invoke;
data = obj;
}
template<class C, Ret(C::*MemFun)(Args...) const>
void init(C const* obj)
{
typedef typename wrapper<Ret,Args...>::template mem_fun_const<C,MemFun> w;
fun = &w::invoke;
data = const_cast<C*>(obj);
}
};
#include <iostream>
int plus(int a, int b) {return a+b;}
int minus(int a, int b) {return a-b;}
struct foo
{
int param;
explicit foo(int p) : param(p) {}
int dings(int a, int b) const
{ return (a-param)*(b+param); }
};
int main()
{
delegate<int(int,int)> d;
d.init<&plus>();
std::cout << d(9,5) << '\n';
d.init<&minus>();
std::cout << d(9,5) << '\n';
foo f (2);
d.init<foo,&foo::dings>(&f);
std::cout << d(9,5) << '\n';
}
und liefert folgende Ausgabe:
14
4
49
kk