从boost.lambda或boost.phoenix静态函数(Static functions f

2019-06-23 15:16发布

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

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

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

但问题是,这样做的效率运行。 即使add在这里的功能是无状态的,返回的拉姆达类型不是空的,它sizeof大于1(因此boost::function的默认构造函数和拷贝构造函数将涉及new )。 我真的很怀疑,有一个从编译器或升压转换器的一侧,以检测这种无国籍和生成代码等同于使用一种机制:

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

当然,人们可以使用C ++ 11 auto ,但随后的变量不能左右的非模板上下文进行传递。 我终于成功地做到几乎我想要的东西,使用以下方法:

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

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

25
Segmentation fault

如预期。 在这里,我保持一个静态指针lambda表达式类型(常量尽可能优化的目的),并对其进行初始化,以NULL 。 这样,如果你尝试“staticify”与状态的lambda表达式,你一定会得到一个运行时错误。 如果你staticify一个真正的无状态的lambda表达式,一切顺利。

到问题(S):

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

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

埃里克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
}

Answer 1:

  1. 有没有办法让这个清洁工。 你是通过调用一个空指针的成员函数。 这是各种不确定的行为,但你已经知道了。
  2. 你可以不知道,如果一个Boost.Lambda功能是无状态的。 这是一个黑盒子。 Boost.Phoenix是一个不同的故事。 它是建立在Boost.Proto,一个DSL工具包,以及凤凰出版它的语法,让您挂钩反思其产生的lambda表达式。 你可以很容易地编写一个原算法来寻找状态的终端和在编译时弹了出来,如果它发现任何。 (但我的回答不会改变上面的#1)。

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

=== UPDATE ===

虽然通过一个空指针调用成员函数是一个可怕的想法(不这样做,你会去盲目的),你可以通过缺省构造相同类型的原始的一个的拉姆达对象。 如果您结合起来,与我在上面#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
}

免责声明:我不是凤凰的作者。 我不知道,如果默认构造性是保证所有的无状态的lambda表达式。

测试了MSVC-10.0。

请享用!



文章来源: Static functions from boost.lambda or boost.phoenix