Static functions from boost.lambda or boost.phoenix

跟風遠走 提交于 2019-12-01 16:22:02
  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!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!