Uso regolarmente boost.lambda (e phoenix) per definire le funzioni lambda in C++. Mi piace molto la loro proprietà polimorfica, la semplicità della loro rappresentazione e il modo in cui rendono la programmazione funzionale in C++ molto più facile. In alcuni casi, è anche più pulito e più leggibile (se si è abituati a leggerli) per usarli per definire piccole funzioni e nominarle nell'ambito statico.Funzioni statiche da boost.lambda o boost.phoenix
Il modo per memorizzare questi funzionali che ricorda le funzioni convenzionali di più è di catturarli in un boost::function
const boost::function<double(double,double)> add = _1+_2;
Ma il problema è l'inefficienza runtime di farlo. Anche se la funzione add
è stateless, il tipo lambda restituito non è vuoto e il suo sizeof
è maggiore di 1 (quindi il valore predefinito di boost::function
ctor e copy ctor è new
). Realmente dubito che c'è un meccanismo dal compilatore o lato del spinta per rilevare questo apolidia e generare codice che è equivalente all'utilizzo:
double (* const add)(double,double) = _1+_2; //not valid right now
Si potrebbe naturalmente utilizzare il C++ 11 auto
, ma poi la variabile non può essere passato intorno a contesti non basati su modelli. Sono finalmente riuscito a fare quasi quello che voglio, utilizzando il seguente approccio:
#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
}
compilato con GCC 4.6.1 L'output di questo programma è (a prescindere dal livello di ottimizzazione):
25
Segmentation fault
come previsto. Qui, sto mantenendo un puntatore statico al tipo di espressione lambda (come const possibile per scopi di ottimizzazione) e inizializzandolo su NULL
. In questo modo, se provi a "staticificare" un'espressione lambda con stato, sei sicuro di ottenere un errore di runtime. E se si statica un'espressione lambda autenticamente senza stato, tutto funziona.
alla questione (s):
Il metodo sembra un po 'sporco, si può pensare a qualsiasi circostanza, o compilatore presupposto che renderà questo misbehave (comportamento previsto: funzionano bene se lambda è senza stato, segfault altrimenti).
Riesci a pensare a un modo in cui il tentativo di questo causerà un errore del compilatore invece di un segfault quando l'espressione lambda ha uno stato?
EDIT dopo la risposta di Eric Niebler:
#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
}
IIRC, se si utilizza Phoenix, è possibile memorizzare il risultato all'interno di una funzione 'boost :: phoenix ::' piuttosto che una funzione' boost :: 'e attenuare una perdita di efficienza ('boost :: phoenix: : function' sono tipi POD e possono essere inizializzati staticamente in fase di compilazione). – ildjarn
@ildjarn Grazie per l'idea di 'boost :: phoenix :: function', che in molti casi sarà utile. Sono comunque interessato a ottenere la funzione lambda equivalente a quella nativa (runtime-performance wise). Non sono sicuro che sia possibile rendere questa qualità di produzione, ma trovo interessante la ricerca. – enobayram