如何传递std :: function作为函数指针? [英] How could I pass std::function as function pointer?

查看:1856
本文介绍了如何传递std :: function作为函数指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个类模板,并且在内部使用一个具有以下接口的C函数(由R环境提供的BFGS优化的实现):

I am trying to write a class template and internally it use a C function (implementation of BFGS optimization, provided by the R environment) with the following interface:

void vmmin(int n, double *x, double *Fmin, 
           optimfn fn, optimgr gr, ... ,
           void *ex, ... );

其中fngr是类型

typedef double optimfn(int n, double *par, void *ex);

typedef void optimgr(int n, double *par, double *gr, void *ex);

分别.我的C++类模板如下:

respectively. My C++ class template looks like this:

template <typename T>
class optim {
 public:
  // ...
  void minimize(T& func, arma::vec &dpar, void *ex) {
    std::function<optimfn> fn = 
      std::bind(&T::fr, func, std::placeholders::_1, 
                std::placeholders::_2, std::placeholders::_3);
    std::function<optimgr> gr = 
      std::bind(&T::grr, func, std::placeholders::_1,
                std::placeholders::_2, std::placeholders::_3,
                std::placeholders::_4);
    // ERROR: cannot convert std::function to function pointer
    vmmin(... , fn, gr, ...);
    // ...
  }  
};

,以便任何具有两个指定成员函数的类都可以实例化它,例如:

so that it can be instantiated by any class with two specified member functions, e.g.:

class Rosen {
 public:
  // ...
  double fr(int n, double *par, void *ex);
  void grr(int n, double *par, double *gr, void *ex);
 private:
  // ...
};

// main.cc
Rosen func;
optim<Rosen> obj;
obj.minimize(func, dpar, ex);

这可能吗?或者,也许有更好的方法可以将两个成员函数分别作为函数指针传递? (如果目标函数和相应的梯度很简单,编写两个函数绝对可以.但是,在大多数情况下,我遇到的问题要复杂得多,必须将该问题作为一个类来实现.)

Is this possible? Or maybe there is a better way of doing this -- pass the two member functions separately as function pointer? (If the objective function and the corresponding gradient are simple, it is absolutely okay to write two functions. However, most of the time, the problem I got is far more complicated and I have to implement the problem as a class).

推荐答案

让我先说:

我不认可以下库的用法

#include<tuple>
#include<type_traits>
#include<utility>

// func_traits
template <typename T>
struct func_traits : public func_traits<decltype(&std::remove_reference_t<T>::operator())> {};

template <typename Callable, typename Ret, typename... Args>
struct func_traits<Ret(Callable::*)(Args...) const> {
    using ptr_type = Ret (*) (Args...);
    using return_type =  Ret;

    template<std::size_t i>
    struct arg
    {
        using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
    };

    template<typename Ret2>
    using cast_return_type = Ret2 (*) (Args...);
};

template<typename Ret, typename... Args>
struct func_traits<Ret (&) (Args...)> : public func_traits<Ret (*) (Args...)> {};

template <typename Ret, typename... Args>
struct func_traits<Ret (*) (Args...)>
{
    using ptr_type = Ret (*) (Args...);
    using return_type =  Ret;

    template<std::size_t i>
    struct arg
    {
        using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
    };

    template<typename Ret2>
    using cast_return_type = Ret2 (*) (Args...);
};



// constexpr counter
template <int N>
struct flag
{
    friend constexpr int adl_flag(flag<N>);
    constexpr operator int() { return N; }
};

template <int N>
struct write
{
    friend constexpr int adl_flag(flag<N>) { return N; }
    static constexpr int value = N;
};

template <int N, int = adl_flag(flag<N>{})>
constexpr int read(int, flag<N>, int R = read(0, flag<N + 1>{}))
{
    return R;
}

template <int N>
constexpr int read(float, flag<N>)
{
    return N;
}

template <int N = 0>
constexpr int counter(int R = write<read(0, flag<N>{})>::value)
{
    return R;
}


// fnptr
template<int nonce = counter()>
class fnptr
{
    //these are to make sure fnptr is never constructed
    //technically the first one should be enough, but compilers are not entirely standard conformant
    explicit fnptr() = delete;
    fnptr(const fnptr&) {}
    ~fnptr() = delete;

    template<typename Callable, typename Ret, typename... Args>
    static auto cast(Callable&& c, Ret(*fp)(Args...)) -> decltype(fp)
    {
        using callable_type = std::remove_reference_t<Callable>;
        static callable_type clb{std::forward<Callable>(c)};
        static bool full = false;
        if(full)
        {
            clb.~callable_type();
            new (&clb) decltype(clb){std::forward<Callable>(c)};
        }
        else
            full = true;
        return [](Args... args) noexcept(noexcept(clb(std::forward<Args>(args)...))) -> Ret
        {
            return Ret(clb(std::forward<Args>(args)...));
        };
    }

public:
    template<typename Signature, typename Callable>
    static Signature* cast(Callable&& c)
    {
        return cast(std::forward<Callable>(c), static_cast<Signature*>(nullptr));
    }

    template<typename Signature, typename Ret, typename... Args>
    static auto cast(Ret (*fp)(Args...))
    {
        static decltype(fp) fnptr;
        fnptr = fp;
        using return_type = typename func_traits<Signature*>::return_type;
        return [](Args... args) noexcept(noexcept(fp(std::forward<Args>(args)...)) -> return_type
        {
            return return_type(fnptr(std::forward<Args>(args)...));
        };
    }

    template<typename Callable>
    static auto get(Callable&& c)
    {
        return cast(std::forward<Callable>(c), typename func_traits<Callable>::ptr_type{nullptr});
    }

    template<typename Ret, typename... Args>
    static auto get(Ret (*fp)(Args...))
    {
        return fp;
    }
};

并将其用作

#include<functional>
#include<iostream>

using optimfn = double (int, double*, void*);
using optimgr = void (int, double*, double*, void*);

void test(optimfn* fn, optimgr* gr)
{
    double d;
    fn(42, &d, &d);
    gr(42, &d, &d, &d);
}

int main()
{
    std::function<optimfn> fn = [](int, double*, void*){
        std::cout << "I'm fn" << std::endl;
        return 0.;
    };
    std::function<optimgr> gr = [](int, double*, double*, void*){
        std::cout << "I'm gr" << std::endl;
    };

    test(fnptr<>::get(fn), fnptr<>::get(gr));
}

实时示例

仅仅是辅助特征类型,它将以易于访问的形式获取任何可调用对象的类型

Is just a helper traits type that will fetch the type of any callable in an easily accessible form

这是正在发生的事情的一半邪恶.有关详细信息,请访问有状态的元编程疾病是否已经形成?

This is half the evilness of what's going on. For details visit is stateful metaprogramming ill formed yet?

代码的实际内容.它接受具有适当签名的所有可调用对象,并在每次调用时隐式声明一个匿名C函数,并将该可调用对象强制转换为C函数.

The actual meat of the code. It takes any callable with appropriate signatures and implicitly declares an anonymous C function at every point it is called and coerces the callable into the C function.

它具有时髦的语法fnptr<>::getfnptr<>::cast<Ret(Args...)>.这是故意.

It has the funky syntax fnptr<>::get and fnptr<>::cast<Ret(Args...)>. This is intentional.

get将声明具有与可调用对象相同签名的匿名C函数.

get will declare the anonymous C function with the same signature as the callable object.

cast适用于任何兼容的可调用类型,即,如果返回类型和参数是隐式可转换的,则可以对其进行强制转换.

cast works on any compatible callable type, that is, if the return type and arguments are implicitly convertible, it can be casted.

fnptr在调用它的代码中的每个地方隐式声明一个匿名C函数.它实际上不是与std::function相同的变量.

fnptr implicitly declares an anonymous C function at each point in the code it is called. It is not the same as std::function that is actually a variable.

如果您再次在代码中调用相同的fnptr,则所有地狱之旅都会失败.

If you call the same fnptr in the code again, all hell breaks lose.

std::vector<int(*)()> v;
for(int i = 0; i < 10; i++)
    v.push_back(fnptr<>::get([i]{return i;}));  // This will implode

您已被警告.

这篇关于如何传递std :: function作为函数指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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