从boost.lambda或boost.phoenix静态函数 [英] Static functions from boost.lambda or boost.phoenix

查看:321
本文介绍了从boost.lambda或boost.phoenix静态函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经常使用boost.lambda(龙凤)在C中定义lambda函数++。我真的很喜欢他们的多态性的财产,其再presentation的简单性和他们的方式使用C函数式编程++容易得多。在某些情况下,它甚至更清洁,更可读的(如果你已经习惯了阅读他们)用它们来定义的小功能,在静态范围命名它们。

来存储这些函类似于常规功能的方式最多的是捕捉它们在的boost ::功能

 常量的boost ::功能<双(双,双)>添加= _1 + _2;

但问题是,这样做的效率运行。尽管这里的添加功能是无状态的,返回的拉姆达类型不是空的,它的的sizeof 大于1(所以的boost ::功能默认的构造函数和拷贝构造函数将涉及)。我真的很怀疑,有一个从编译器或升压转换器的一侧的机制来检测这种无国籍并产生code相当于使用:

 双(* const的补充)(双,双)= _1 + _2; //无效现在

一个当然可以使用C ++ 11 汽车,但随后的变量不能围绕非模板上下文通过。我终于成功地几乎我想要做什么,使用下面的方法:

 的#include<升压/λ/ lambda.hpp>
使用空间boost ::拉姆达;#包括LT&;升压/ type_traits.hpp>
#包括LT&;升压/实用/ result_of.hpp>
使用名字空间boost;
模板<类T>
结构static_lambda {    静态常量T * const的吨;    //定义调用功能吨静态函数
    模板<类arg1type,类arg2type>
    静态类型名的result_of< T(arg1type,arg2type)GT; ::类型
        申请(arg1type ARG1,ARG2 arg2type){
        回报(* T)(ARG1,ARG2);
    }    //转换操作符
    模板<类func_type>
    操作者func_type *(){
       的typedef typename的function_traits< func_type> :: arg1_type arg1type;
       的typedef typename的function_traits< func_type> :: arg2_type arg2type;
       返回&功放; static_lambda< T> ::申请< arg1type,arg2type取代;
    }
};模板<类T>
常量T * const的static_lambda< T> :: T = 0;模板<类T>
static_lambda< T> make_static(T(T)){返回static_lambda< T>();}#包括LT&;&iostream的GT;
#包括LT&;&cstdio GT;
诠释主(){
    INT C = 5;
    INT(*添加)(INT,INT)= make_static(_1 + _2);
    //我们甚至可以用下面的语法定义数组
    双(* const的func_array [])(双,双)= {make_static(_1 + _2),make_static(_1 * _2 * REF(C))};
    性病::法院LT&;< func_array [0](10,15)LT;<\\ n;
    的std :: fflush(标准输出);
    性病::法院LT&;< func_array [1](10,15); //应该引起分割错,因为func_array [1]的状态
}

使用GCC编译4.6.1该程序的输出(不管优化级别的):

  25
分段故障

预期。在这里,我保持一个静态指针拉姆达前pression型(为const尽可能优化的目的),并将其初始化为 NULL 。这样,如果你尝试staticify拉姆达前pression与状态时,你一定要得到一个运行时错误。如果你staticify一个真正的无状态的lambda前pression,一切顺利。

在这个问题(S):


  1. 该方法似乎有点脏,你能想到的任何情况下,或编译器的假设,这将使这个行为不端的(预期的行为:正常工作,如果拉姆达是无状态的,段错误,否则)


  2. 你能想到的任何方式企图,这将导致,而不是一个段错误编译器错误,当拉姆达前pression有状态?


埃里克Niebler的回答后编辑:

 的#include<升压/ phoenix.hpp>
使用空间boost ::凤;
使用空间boost ::凤凰:: arg_names;#包括LT&;升压/ type_traits.hpp>
#包括LT&;升压/实用/ result_of.hpp>
使用boost :: function_traits;模板<类T>
结构static_lambda {
    静态常量T(T);    //简单地适用于T A的静态函数
    模板<类arg1type,类arg2type>
    静态类型名的boost ::的result_of< T(arg1type,arg2type)GT; ::类型
    申请(arg1type ARG1,ARG2 arg2type){
    返回T(ARG1,ARG2);
    }    //转换为一个函数指针
    模板<类func_type>
    操作者func_type *(){
    的typedef typename的function_traits< func_type> :: arg1_type arg1type;
        的typedef typename的function_traits< func_type> :: arg2_type arg2type;
        返回&功放; static_lambda< T> ::申请< arg1type,arg2type取代;
    }
};模板<类T>
常量ŧstatic_lambda< T> ::吨; //默认初始化功能模板<类T>
static_lambda< T> make_static(T(T)){返回static_lambda< T>();}#包括LT&;&iostream的GT;
#包括LT&;&cstdio GT;
诠释主(){
    INT(*添加)(INT,INT)= make_static(_1 + _2);    性病::法院LT&;<添加(10,15)LT;<\\ n;    INT C = 5;    // INT(* add_with_ref)(INT,INT)= make_static(_1 + _2 + REF(C));导致编译器错误根据需要
}


解决方案

  1. 有没有办法让这个清洁工。你是通过调用一个空指针的成员函数。这是各种不确定的行为,但你已经知道了。

  2. 您可以不知道,如果一个Boost.Lambda功能是无状态的。这是一个黑盒子。 Boost.Phoenix是一个不同的故事。它是建立在Boost.Proto,一个DSL工具包,以及凤凰出版其语法,让您挂钩反思它所产生的拉姆达前pressions。你可以非常容易地编写一个原始算法来寻找状态终端和在编译的时候弹了出来,如果​​发现任何。 (但是,这并没有改变我的回答#1以上。)

你说你喜欢Boost的lambda函数的性质多态,但你没有使用该属性在code以上。我的建议:使用C ++ 11 lambda表达式。无状态的人已经有一个隐式转换原始函数指针。这就是你要找的,海事组织。

===更新===

虽然通过一个空指针调用成员函数是一个可怕的想法(不这样做,你会去盲目的),你的可以的默认构造的同类型的原始的拉姆达对象。如果再加上我在上面#2的建议,你可以得到你以后。这里的code:

 的#include<&iostream的GT;
#包括LT&;&type_traits GT;
#包括LT&;提升/ MPL / bool.hpp>
#包括LT&;提升/ MPL / and.hpp>
#包括LT&;升压/ phoenix.hpp>命名空间细节
{
    使用空间boost ::原;
    命名空间MPL =提振:: MPL;    结构is_stateless
      :or_<
            当<终端LT; _>中的std :: is_empty<&_value GT;()>中
            否则<
                折叠< _,MPL :: true_(),MPL :: and_< _state,is_stateless>()>
            >
        >
    {};    模板< typename的LAMBDA>
    结构static_lambda
    {
        模板< typename的西格>
        结构IMPL;        模板< typename的RET,类型名称为arg0,类型名的Arg1>
        结构IMPL<惩戒(arg0中,Arg1)将>
        {
            静态惩戒申请(为arg0为arg0,ARG1的Arg1)
            {
                返回LAMBDA()(为arg0,ARG1);
            }
        };        模板< typename的乐趣>
        运营商玩转*()const的
        {
            返回&功放; IMPL<娱乐与GT; ::适用;
        }
    };    模板< typename的LAMBDA>
    内嵌static_lambda<&LAMBDA GT; make_static(LAMBDA常量和放大器; L)
    {
        static_assert(
            提高::的result_of< is_stateless(波长)GT; ::类型::值,
            LAMBDA不是无状态
        );
        返回static_lambda<&LAMBDA GT;();
    }
}使用细节:: make_static;诠释的main()
{
    使用空间boost ::凤;
    使用空间占位;    INT C = 5;
    INT(*添加)(INT,INT)= make_static(_1 + _2);    //我们甚至可以用下面的语法定义数组
    静态双(* const的func_array [])(双,双)=
    {
        make_static(_1 + _2)
        make_static(_1 * _2)
    };
    性病::法院LT&;< func_array [0](10,15)所述;&下; \\ n;
    性病::法院LT&;< func_array [1](10,15);    //如果您尝试从一个lambda创建一个无状态的lambda
    //使用状态,触发一个静态断言:
    INT(*哎呀)(INT,INT)= make_static(_1 + _2 + 42); //错误,不是无状态
}

免责声明:的我不是凤凰的作者。我不知道是否默认可施工是保证所有的无状态的lambda表达式。

与MSVC-10.0的测试。

享受!

I regularly use boost.lambda (and phoenix) to define lambda functions in C++. I really like their polymorphic property, the simplicity of their representation and the way they make functional programming in C++ so much easier. In some cases, it's even cleaner and more readable (if you're used to reading them) to use them for defining small functions and naming them in the static scope.

The way to store these functionals that resembles conventional functions the most is to capture them in a boost::function

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

But the problem is the runtime inefficiency of doing so. Even though the add function here is stateless, the returned lambda type is not empty and its sizeof is greater than 1 (so boost::function default ctor and copy ctor will involve new). I really doubt that there's a mechanism from the compiler's or the boost's side to detect this statelessness and generate code which is equivalent to using:

double (* const add)(double,double) = _1+_2; //not valid right now

One could of course use the c++11 auto, but then the variable can't be passed around non-templated contexts. I've finally managed to do almost what I want, using the following approach:

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

Compiled with gcc 4.6.1 The output from this program is (regardless of the optimization level):

25
Segmentation fault

as expected. Here, I'm keeping a static pointer to the lambda expression type (as const as possible for optimization purposes) and initializing it to NULL. This way, if you try to "staticify" a lambda expression with state, you're sure to get a runtime error. And if you staticify a genuinely stateless lambda expression, everything works out.

On to the question(s):

  1. The method seems a bit dirty, can you think of any circumstance, or compiler assumption that will make this misbehave (expected behavior: work fine if lambda is stateless, segfault otherwise).

  2. Can you think of any way that attempting this will cause a compiler error instead of a segfault when the lambda expression has state?

EDIT after Eric Niebler's answer:

#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. There is no way to make this cleaner. You are calling a member function through a null pointer. This is all kinds of undefined behavior, but you know that already.
  2. You can't know if a Boost.Lambda function is stateless. It's a black box. Boost.Phoenix is a different story. It's built on Boost.Proto, a DSL toolkit, and Phoenix publishes its grammar and gives you hooks to introspect the lambda expressions it generates. You can quite easily write a Proto algorithm to look for stateful terminals and bomb out at compile time if it finds any. (But that doesn't change my answer to #1 above.)

You said you like the polymorphic nature of Boost's lambda functions, but you're not using that property in your code above. My suggestion: use C++11 lambdas. The stateless ones already have an implicit conversion to raw function pointers. It's just what you're looking for, IMO.

===UPDATE===

Although calling a member function through a null pointer is a terrible idea (don't do it, you'll go blind), you can default-construct a NEW lambda object of the same type of the original. If you combine that with my suggestion in #2 above, you can get what you're after. Here's the code:

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

Disclaimer: I am not the author of Phoenix. I don't know if default-constructability is guaranteed for all stateless lambdas.

Tested with MSVC-10.0.

Enjoy!

这篇关于从boost.lambda或boost.phoenix静态函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆