资源受限 MCU 上绑定成员函数的 C++ 包装器

C++ wrapper for bound member function on resource constrained MCU

我正在尝试实现一个指向成员函数包装器的指针,其中 return 类型和参数类型已知,但成员函数的 class 类型未知。这是针对资源受限(无堆 allocation/c++ 标准库)微处理器的 C++ 项目。

下面的代码似乎可以工作(你可以 运行 它 HERE)并且它满足了我们的一般要求,但它使用了 reinterpret_cast hackery。

//can't use any c++ std libs or heap allocation
#include <cstdio>

template<typename Ret,typename... Args>
struct member_cb
{

    template<typename T>
    void set_cb(T *obj_,Ret (T::*func)(Args...))
    {
        obj = reinterpret_cast<member_cb*>(obj_);
        cb = reinterpret_cast<Ret (member_cb::*)(Args...)>(func);
    }

    Ret operator()(Args... num)
    {
        return (obj->*cb)(num...);
    }

    member_cb* obj;
    Ret (member_cb::*cb)(Args...);

};

struct Bar{

    void someFunc(int n2){
        printf("someFunc called with parameter: %d\n",n2);
    }

};

int main()
{ 
    //class type of Bar is not known at wrapper creation
    member_cb<void,int> foo;
    
    Bar bar;
    
    foo.set_cb(&bar, &Bar::someFunc);
    
    foo(42);
}

我想在没有 reinterpret_cast 的情况下执行此操作。我相信有一个使用由 set_cb() 函数创建的 lambda 的解决方案,如下所示:

template<typename Ret,typename... Args>
struct member_cb
{
    template<typename T>
    void set_cb(T *obj_,Ret (T::*func)(Args...))
    {
        cb_func = [=](Args...num)
        {
            (obj_->*func)(num...);
        };
    }

    Ret operator()(Args... args)
    {
        return cb_func(args...);
    }

    Ret (*cb_func)(Args...);
}

以上代码编译不通过(你可以试试HERE)。问题是像 cb_func 这样的常规函数​​指针只能指向没有捕获的 lambda,而 lambda 必须捕获 obj_ 和 func 参数。任何关于使这项工作的建议将不胜感激。

一个简单的、独立于编译器的解决方案:

template <typename Signature>
struct member_cb;

template <typename Ret, typename... Args>
struct member_cb<Ret(Args...)>
{
    template <typename T, Ret (T::*func)(Args...)>
    static Ret wrapper(void *object, Args&&... args) {
        T *o = reinterpret_cast<T *>(object);
        return (o->*func)(std::forward<Args>(args)...);
    }
    
    template <typename T, Ret (T::*func)(Args...)>
    void set_cb(T *obj_)
    {
        obj = obj_;
        cb_func = wrapper<T, func>;
    }

    // since C++17:
    template <auto func, typename T>
    void set_cb(T *obj_)
    {
        obj = obj_;
        cb_func = wrapper<T, func>;
    }

    Ret operator() (Args&& ...args)
    {
        return cb_func(obj, std::forward<Args>(args)...);
    }

    void *obj;
    Ret (*cb_func)(void *, Args&&...);
};

...
member_cb<void(int)> foo;
foo.set_cb<Bar, &Bar::someFunc>(&bar);
foo.set_cb<&Bar::someFunc>(&bar); // since C++17

关键限制是 someFunc 必须在编译时在调用 set_cb 的地方知道,所以你不能采用任意方法指针并将其转换为 member_cb.不过,可以更改对象而保持方法指针完好无损(但这不是类型安全的)。

另一个可能的问题是编译器可能需要为每个用作回调的方法发出一个 wrapper 实例。

优点是它应该可以在任何兼容 C++11 的编译器(C++17 以获得更好的语法)上运行,并且 member_cb 本身很小。