2012-04-13 1 views
8

私は定期的にboost.lambda(とphoenix)を使ってC++でラムダ関数を定義しています。多態性、その表現の簡潔さ、そしてC++での関数型プログラミングのやり方がずっと簡単なのは本当に好きです。場合によっては、小さな関数を定義したり、静的なスコープで名前を付けるためにそれらを使用するためには、よりクリーンで読みやすい(読み込みに慣れている)人もいます。ほとんどの従来の機能に似ているこれらの汎関数を格納するためのboost.lambdaやboost.phoenixの静的関数

方法はboost::function

const boost::function<double(double,double)> add = _1+_2; 

でそれらをキャプチャすることですしかし、問題はそうすることの実行時の非効率性です。ここでaddの機能がステートレスであっても、返されるラムダ型は空ではなく、sizeofは1よりも大きいため(boost::functionデフォルトのctorとcopy ctorにはnewが含まれます)。

一つはもちろん++ 11 auto Cを使用することができます
double (* const add)(double,double) = _1+_2; //not valid right now 

が、その後、変数:私は本当にこのステートレスを検出して使用することと等価であるコードを生成するコンパイラのかブースト者側からのメカニズムがあることを疑いますテンプレート化されていないコンテキストの周りを渡すことはできません。 GCC 4.6.1このプログラムからの出力でコンパイル

#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 
} 

(関係なく、最適化レベルの)です:

25 
Segmentation fault 
私は最終的に次のようなアプローチを使用して、ほとんど私がやりたいことができました

が期待通りです。ここでは、ラムダ式型(最適化のために可能な限りconstとして)への静的ポインタを保持し、それをNULLに初期化しています。このように、ラムダ式をstateで "静的化"しようとすると、実行時エラーが発生します。真に無国籍のラムダ式を静的にすると、すべてがうまくいきます。質問(複数可)をオン

:メソッドは、この不正な動作を行いますどんな状況、またはコンパイラの前提と考えることができ、少し汚れているようだ

  1. は(期待される動作は:ラムダの場合は正常に動作しますステートレス、segfault)。

  2. ラムダ式に状態があるときにsegfaultの代わりにコンパイラエラーが発生すると思いますか?エリックNieblerの答えの後

EDIT:

#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 
} 
+1

Phoenixを使用している場合、結果を 'boost :: function'ではなく' boost :: phoenix :: function'の中に格納して、効率を低下させることができます( 'boost :: phoenix: :function'はPOD型であり、コンパイル時に静的に初期化することができます)。 – ildjarn

+0

@ildjarn 'boost :: phoenix :: function'について頭をおいてくれてありがとうございました。これは多くの場合に役立つはずです。私はまだキャプチャネイティブ同等の(ランタイムパフォーマンス賢明な)ラムダ関数を得ることに興味があります。私はこの生産品質を作ることがすべて可能かどうかはわかりませんが、興味深いのがわかります。 – enobayram

答えて

11
  1. このクリーナーを作るための方法はありません。ヌルポインターを通じてメンバー関数を呼び出しています。これはすべての未定義の振る舞いですが、すでに分かっています。
  2. Boost.Lambda関数がステートレスであるかどうかはわかりません。それはブラックボックスです。 Boost.Phoenixは別の話です。これはBoost.Proto(DSLツールキット)上に構築されており、Phoenixはその文法を公開し、生成するラムダ式をイントロスペクトするフックを提供します。ステートフル端末を探してコンパイル時に爆発するProtoアルゴリズムを簡単に書くことができます。 (しかし、それは上記の#1に対する私の答えを変えない。)

あなたはブーストのラムダ関数の多自然のように述べていますが、上記のコード内でそのプロパティを使用していません。私の提案:C++ 11ラムダを使用してください。ステートレスなものは、未処理の関数ポインタへの暗黙的な変換を既に持っています。それはただあなたが探しているものです、IMO。

=== === UPDATE

NULLポインタを通じてメンバ関数を呼び出すと、恐ろしい考えがあるが、(それをしない、あなたは盲目行くよ)、あなたはでき default- NEW元の同じ種類のラムダオブジェクトを作成します。上記の#2で私の提案と組み合わせれば、あなたが何をしているのかを知ることができます。

#include <iostream> 
#include <type_traits> 
#include <boost/mpl/bool.hpp> 
#include <boost/mpl/and.hpp> 
#include <boost/phoenix.hpp> 

namespace detail 
{ 
    using namespace boost::proto; 
    namespace mpl = boost::mpl; 

    struct is_stateless 
     : or_< 
      when<terminal<_>, std::is_empty<_value>()>, 
      otherwise< 
       fold<_, mpl::true_(), mpl::and_<_state, is_stateless>()> 
      > 
     > 
    {}; 

    template<typename Lambda> 
    struct static_lambda 
    { 
     template<typename Sig> 
     struct impl; 

     template<typename Ret, typename Arg0, typename Arg1> 
     struct impl<Ret(Arg0, Arg1)> 
     { 
      static Ret apply(Arg0 arg0, Arg1 arg1) 
      { 
       return Lambda()(arg0, arg1); 
      } 
     }; 

     template<typename Fun> 
     operator Fun*() const 
     { 
      return &impl<Fun>::apply; 
     } 
    }; 

    template<typename Lambda> 
    inline static_lambda<Lambda> make_static(Lambda const &l) 
    { 
     static_assert(
      boost::result_of<is_stateless(Lambda)>::type::value, 
      "Lambda is not stateless" 
     ); 
     return static_lambda<Lambda>(); 
    } 
} 

using detail::make_static; 

int main() 
{ 
    using namespace boost::phoenix; 
    using namespace placeholders; 

    int c=5; 
    int (*add)(int,int) = make_static(_1+_2); 

    // We can even define arrays with the following syntax 
    static double (*const func_array[])(double,double) = 
    { 
     make_static(_1+_2), 
     make_static(_1*_2) 
    }; 
    std::cout << func_array[0](10,15) << "\n"; 
    std::cout << func_array[1](10,15); 

    // If you try to create a stateless lambda from a lambda 
    // with state, you trigger a static assertion: 
    int (*oops)(int,int) = make_static(_1+_2+42); // ERROR, not stateless 
} 

免責事項:私はフェニックスの著者ではないよ。ここのコードです。すべてのステートレスラムダでデフォルトの構築性が保証されているかどうかはわかりません。

MSVC-10.0で試験しました。

お楽しみください!

+2

+1、権威ある答えのようなものはありません。 : - ] – ildjarn

+0

私はちょうどあなたのアップデートを見てきました、私はまた、元の答えを読んだ後にフェニックスラムダを使用しようとし、彼らがステートレスラムダのデフォルト構築をサポートしていることに気づいた。私はまた、そのプロパティを使用する解決策を考え出しました(私の編集をチェックしてください)。私はあなたの解決策がより良いとより教えていると思うが:) – enobayram

+2

注意深い。デフォルトで構築可能なステートレスフェニックスラムダだけでなく、それらの地方自治体のタイプ自体がデフォルト構成可能である限り、あなたのソリューションは値で地元を捉えるラムダを捕まえません。私が上で書いた 'is_stateless' Protoアルゴリズムを使う必要があります。 –

関連する問題