Funkcje statyczne z boost.lambda lub boost.phoenix
Regularnie używam boost.lambda (i phoenix) do definiowania funkcji lambda w C ++. Bardzo podoba mi się ich właściwość polimorficzna, prostota ich reprezentacji i sposób, w jaki znacznie ułatwiają programowanie funkcjonalne w C ++. W niektórych przypadkach jest nawet czystszy i bardziej czytelny (jeśli jesteś przyzwyczajony do ich czytania), aby użyć ich do definiowania małych funkcji i nazywania ich w zakresie statycznym.
Sposobem na przechowywanie tych funkcjonałów, które przypominają konwencjonalne funkcje, jest uchwycenie ich w aboost::function
<code>const boost::function<double(double,double)> add = _1+_2; </code>
Problemem jest jednak nieefektywność działania. Nawet jeśliadd
funkcja tutaj jest bezstanowa, zwrócony typ lambda nie jest pusty i jegosizeof
jest większe niż 1 (takboost::function
domyślny ctor i kopia ctor będą obejmowaćnew
). Naprawdę wątpię, czy istnieje mechanizm ze strony kompilatora lub boostu, aby wykryć tę bezpaństwowość i wygenerować kod, który jest równoważny z użyciem:
<code>double (* const add)(double,double) = _1+_2; //not valid right now </code>
Można oczywiście użyć c ++ 11auto
, ale wtedy zmienna nie może być przekazywana w kontekstach nie-szablonowych. W końcu udało mi się zrobić prawie to, co chcę, stosując następujące podejście:
<code>#include <boost/lambda/lambda.hpp> using namespace boost::lambda; #include <boost/type_traits.hpp> #include <boost/utility/result_of.hpp> using namespace boost; template <class T> struct static_lambda { static const T* const t; // Define a static function that calls the functional t template <class arg1type, class arg2type> static typename result_of<T(arg1type,arg2type)>::type apply(arg1type arg1,arg2type arg2){ return (*t)(arg1,arg2); } // The conversion operator template<class func_type> operator func_type*() { typedef typename function_traits<func_type>::arg1_type arg1type; typedef typename function_traits<func_type>::arg2_type arg2type; return &static_lambda<T>::apply<arg1type,arg2type>; } }; template <class T> const T* const static_lambda<T>::t = 0; template <class T> static_lambda<T> make_static(T t) {return static_lambda<T>();} #include <iostream> #include <cstdio> int main() { int c=5; int (*add) (int,int) = make_static(_1+_2); // We can even define arrays with the following syntax double (*const func_array[])(double,double) = {make_static(_1+_2),make_static(_1*_2*ref(c))}; std::cout<<func_array[0](10,15)<<"\n"; std::fflush(stdout); std::cout<<func_array[1](10,15); // should cause segmentation fault since func_array[1] has state } </code>
Skompilowane z gcc 4.6.1 Dane wyjściowe tego programu to (niezależnie od poziomu optymalizacji):
<code>25 Segmentation fault </code>
zgodnie z oczekiwaniami. W tym miejscu utrzymuję statyczny wskaźnik do typu wyrażenia lambda (w miarę możliwości dla celów optymalizacji) i inicjalizuję goNULL
. W ten sposób, jeśli spróbujesz „zharmonizować” wyrażenie lambda ze stanem, na pewno otrzymasz błąd wykonania. A jeśli statyfikujesz prawdziwie bezstanowe wyrażenie lambda, wszystko działa.
Na pytanie (-a):
Metoda wydaje się trochę brudna, możesz pomyśleć o jakichkolwiek okolicznościach lub założeniu kompilatora, które sprawią, że będzie się źle zachowywało (oczekiwane zachowanie: działa poprawnie, jeśli lambda jest bezstanowa, segfault inaczej).
Czy możesz myśleć o tym, że próba tego spowoduje błąd kompilatora zamiast segfaultu, gdy wyrażenie lambda ma stan?
EDIT po odpowiedzi Erica Nieblera:
<code>#include <boost/phoenix.hpp> using namespace boost::phoenix; using namespace boost::phoenix::arg_names; #include <boost/type_traits.hpp> #include <boost/utility/result_of.hpp> using boost::function_traits; template <class T> struct static_lambda { static const T t; // A static function that simply applies t template <class arg1type, class arg2type> static typename boost::result_of<T(arg1type,arg2type)>::type apply(arg1type arg1,arg2type arg2){ return t(arg1,arg2); } // Conversion to a function pointer template<class func_type> operator func_type*() { typedef typename function_traits<func_type>::arg1_type arg1type; typedef typename function_traits<func_type>::arg2_type arg2type; return &static_lambda<T>::apply<arg1type,arg2type>; } }; template <class T> const T static_lambda<T>::t; // Default initialize the functional template <class T> static_lambda<T> make_static(T t) {return static_lambda<T>();} #include <iostream> #include <cstdio> int main() { int (*add) (int,int) = make_static(_1+_2); std::cout<<add(10,15)<<"\n"; int c=5; // int (*add_with_ref) (int,int) = make_static(_1+_2+ref(c)); causes compiler error as desired } </code>