Salve gente.
Tempo fa ho scatenato una discussione su C++ e delegate.
Tale discussione è proseguita poi in una serie di post fra cui spiccano certamente i due di Contezero [1, 2] che fra l’altro includono un’interessante implementazione.
Questione di terminologia
Ebbene i “delegate” esistono da tempo nel C++, sebbene col nome ben più altisonante di polymorphic function wrappers (vedi draft C++0x Marzo 2009, § 20.7.16.2 [func.wrap.func]).
Presenti nelle solite Boost C++ Libraries dal 2001, sono stati in seguito aggiunti nel cosiddetto Technical Report 1 (TR1) del C++ ed infine saranno inclusi on steroids nel nuovo standard C++0x.
Il solito esempio…
Di seguito riporto due piccoli esempi C++ in cui viene emulato il comportamento dei delegate C#.
Nel primo faccio ricorso alle Boost, ossia uso la comoda boost::function (già che c’ero ho aggiunto qualche lambda expression, per la gioia di Zeno).
/**
* FILE : DelegateEmulationBoost.cpp
* AUTHOR : Gian Paolo "JP" Ghilardi (http://rejex.wordpress.com)
* LICENSE : released under the terms of GPL v2.0 ("only")
* COMPILE : g++ -Wall -Winline -pedantic -I<BOOST_PATH>
* DelegateEmulationBoost.cpp -o DelegateEmulationBoost
* PURPOSE : simple example showing how to match C# delegate power and flexibility
* with boost::function (and boost::lambda)
*
* TESTED ON:
* - Windows XP, x86 32-bit, G++ 4.3.3 (Boost C++ Libraries 1.38.0)
* - MacOSX 10.5.6, PPC 32-bit, G++ 4.0.1 (Boost C++ Libraries 1.35.0)
*
* REFERENCES:
* [1]: http://www.boost.org/doc/libs/1_38_0/doc/html/function.html
* [2]: http://www.boost.org/doc/libs/1_38_0/doc/html/lambda.html
* [3]: http://www.boost.org/doc/libs/1_38_0/doc/html/lambda/s08.html
* [4]: http://en.wikipedia.org/wiki/Function_object#Functors_in_C_and_C.2B.2B
*/
#include <boost/lambda/lambda.hpp>
#include <boost/function.hpp>
#include <cstdlib>
#include <iostream>
using namespace std;
using namespace boost::lambda;
// function object (functor) compatible with boost::function<int(int, int)>
struct sum
{
int operator() (int x, int y) const { return x + y; }
};
/* simple function receiving a boost::function and two parameters
NOTE: the boost::function parameter works/acts as a C# delegate.
You can pass everything compatible including functions, lambdas, ...
*/
int h(boost::function<int(int, int)> f, int x, int y)
{
return f(x, y);
}
int main(int argc, char **argv)
{
cout << endl;
cout << "SIMPLE BOOST EXAMPLE:" << endl;
cout << "Simple example showing boost::function" << endl;
cout << "and boost::lambda flexibility (C++)" << endl << endl;
int a = 10;
int b = 20;
boost::function<int(int, int)> f = sum(); // storing a function
boost::function<int(int, int)> g = _1 + _2; // storing a lambda
cout << "Calling a stored function : " << f(a, b) << endl;
cout << "Calling a stored lambda : " << g(a, b) << endl;
cout << "Direct lambda : " << (_1 + _2)(a, b) << endl;
cout << "Function as a parameter : " << h(sum(), a, b) << endl;
cout << "Boost::function as parameter : " << h(f, a, b) << endl;
cout << "Lambda as parameter : " << h(_1 + _2, a, b) << endl;
return EXIT_SUCCESS;
}
In questo secondo esempio sfrutto function, presa però dal TR1 (niente lambda, sorry)…
/**
* FILE : DelegateEmulationTR1.cpp
* AUTHOR : Gian Paolo "JP" Ghilardi (http://rejex.wordpress.com)
* LICENSE : released under the terms of GPL v2.0 ("only")
* COMPILE : g++ -Wall -Winline -pedantic
* DelegateEmulationTR1.cpp -o DelegateEmulationTR1
* PURPOSE : simple example showing how to match C# delegate power and flexibility
* with tr1::function
*
* TESTED ON:
* - Windows XP, x86 32-bit, G++ 4.3.3 (Boost C++ Libraries 1.38.0)
* - MacOSX 10.5.6, PPC 32-bit, G++ 4.0.1 (Boost C++ Libraries 1.35.0)
*
* REFERENCES:
* [1]: http://en.wikipedia.org/wiki/Technical_Report_1#Polymorphic_Function_Wrapper
* [2]: http://en.wikipedia.org/wiki/Function_object#Functors_in_C_and_C.2B.2B
*/
#include <tr1/functional>
#include <cstdlib>
#include <iostream>
using namespace std;
// function object (functor) compatible with boost::function<int(int, int)>
struct sum
{
int operator() (int x, int y) const { return x + y; }
};
/* simple function receiving a boost::function and two parameters
NOTE: the tr1::function parameter works/acts as a C# delegate.
You can pass compatible functions. When C++0x standard will be
ratified, it will be possible to pass also lambdas.
*/
int h(tr1::function<int(int, int)> f, int x, int y)
{
return f(x, y);
}
int main(int argc, char **argv)
{
cout << endl;
cout << "SIMPLE TR1 EXAMPLE:" << endl;
cout << "Simple example showing tr1::function (C++)" << endl << endl;
int a = 10;
int b = 20;
tr1::function<int(int, int)> f = sum(); // storing a function
cout << "Calling a stored function : " << f(a, b) << endl;
cout << "Function as a parameter : " << h(sum(), a, b) << endl;
cout << "tr1::function as parameter : " << h(f, a, b) << endl;
return EXIT_SUCCESS;
}
Come ho già detto con l’avvento dello standard C++0x il C++ “ufficiale” arriverà al livello delle Boost, lambda incluse.
In realtà lo standard si spinge ancora più in là (per quello ho detto “on steroids”). Ad esempio, ecco come appare uno dei costruttori della classe function nel sopracitato draft (§ 20.7.16.2.1):
template<class F> requires CopyConstructible<F> && Callable<F, ArgTypes...> && Convertible<Callable<F, ArgTypes...>::result_type, R> function(F);
L’uso dei concept (clausola “requires”) mostra cosa può la classe template function. Da notare anche l’uso dei variadic template (“ArgTypes…“) che permette di gestire un numero arbitrario di parametri.
Nonostante lo standard C++0x sia ancora in fase di stesura/ratifica, il supporto nei vari compilatori sta evolvendo relativamente in fretta. Il team di sviluppo del g++ sta procedendo a più sospinto (cfr. apposita pagina) e anche quello del Visual C++ non è da meno.
A dirla tutta, Microsoft sembra perfino essere già molto avanti nell’implementazione. A tal proposito, vi consiglio dare un’occhiata al blog del team di sviluppo di Visual C++. In particolare vi segnalo due post: nel primo si parla in maniera estesa ed accurata del supporto, già integrato nel futuro VC++ 2010, a lambda, auto e static_assert; nel secondo si parla diffusamente degli rvalue reference.
Ciau! ^^
