【问题标题】:C++ wrapper for bound member function on resource constrained MCU资源受限 MCU 上绑定成员函数的 C++ 包装器
【发布时间】:2021-09-10 19:11:46
【问题描述】:

我正在尝试实现一个指向成员函数包装器的指针,其中返回类型和参数类型已知,但成员函数的类类型未知。这是针对资源受限(无堆分配/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 参数。任何有关使这项工作的建议将不胜感激。

【问题讨论】:

  • 有一个问题:在 C++ 中,指向成员的类型不能保证都具有相同的大小。它们确实不同,至少在涉及多重继承时是这样。这意味着,任何解决方案都会有其缺点,可能是:编译器依赖性、堆分配、臃肿的member_cb、臃肿的文本段(大量的蹦床)等等。

标签: c++ embedded function-pointers microprocessors


【解决方案1】:

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

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 本身很小。

【讨论】:

  • 这是一个可靠的答案,但看起来cb_func 需要有Args 是右值引用,或者wrapper 不需要是右值引用(编译器资源管理器链接@ 987654321@)
  • 已修复,谢谢。请注意,尽管使用相同的语法,但这不是右值引用:由于 Args 是模板参数,它是一个通用引用,可以成为任何类型的引用,具体取决于 Args 是什么,就像在 godbolt.org/z/nxa18snzY .尽管Args 是固定的,而不是推断出来的,但在这里不是很有用。
猜你喜欢
  • 2011-10-15
  • 1970-01-01
  • 2015-05-10
  • 1970-01-01
  • 1970-01-01
  • 2018-06-29
  • 2013-02-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多