gpt4 book ai didi

c++ - 来自 boost.lambda 或 boost.phoenix 的静态函数

转载 作者:可可西里 更新时间:2023-11-01 17:38:22 28 4
gpt4 key购买 nike

我经常使用 boost.lambda(和 phoenix)在 C++ 中定义 lambda 函数。我真的很喜欢它们的多态性、它们表示的简单性以及它们使 C++ 中的函数式编程变得如此容易的方式。在某些情况下,使用它们来定义小函数并在静态范围内命名它们甚至更清晰、更具可读性(如果您习惯阅读它们)。

存储这些最类似于常规函数的函数的方法是将它们捕获在 boost::function

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

但问题是这样做的运行时效率低下。尽管此处的 add 函数是无状态的,但返回的 lambda 类型不为空并且其 sizeof 大于 1(因此 boost::function default ctor 和 copy ctor 将涉及 new)。我真的怀疑编译器或 boost 端是否有一种机制来检测这种无状态并生成等同于使用的代码:

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。这样,如果您尝试使用状态“静态化”lambda 表达式,您肯定会遇到运行时错误。如果您静态化一个真正无状态的 lambda 表达式,一切都会顺利进行。

关于问题:

  1. 该方法似乎有点脏,您能想到任何情况或编译器假设会导致这种行为不当(预期行为:如果 lambda 是无状态的,则工作正常,否则会出现段错误)。

  2. 当 lambda 表达式有状态时,您能想出任何方法来尝试此操作会导致编译器错误而不是段错误吗?

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

最佳答案

  1. 没有办法让这个更干净。您正在通过空指针调用成员函数。这是各种未定义的行为,但您已经知道了。
  2. 您无法知道 Boost.Lambda 函数是否是无状态的。这是一个黑盒子。 Boost.Phoenix 是另一回事。它建立在 Boost.Proto 之上,这是一个 DSL 工具包,Phoenix 发布了它的语法,并为您提供了一些钩子(Hook)来检查它生成的 lambda 表达式。您可以很容易地编写一个 Proto 算法来查找有状态终端,如果找到任何终端,则在编译时将其炸毁。 (但这并没有改变我对上面#1 的回答。)

您说您喜欢 Boost 的 lambda 函数的多态性,但您没有在上面的代码中使用该属性。我的建议:使用 C++11 lambda。无状态的已经隐式转换为原始函数指针。这正是您正在寻找的,IMO。

===UPDATE===

虽然通过空指针调用成员函数是一个糟糕的主意(不要这样做,你会瞎的),你可以默认构造一个NEW 与原始类型相同的 lambda 对象。如果您将其与我在上面 #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
}

免责声明:我不是 Phoenix 的作者。我不知道是否保证所有无状态 lambda 的默认可构造性。

使用 MSVC-10.0 测试。

尽情享受吧!

关于c++ - 来自 boost.lambda 或 boost.phoenix 的静态函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10144948/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com