【问题标题】:How to cast "void (MyClass::*)(int)" to "void (*)(int)"?如何将“void (MyClass::*)(int)”转换为“void (*)(int)”?
【发布时间】:2020-04-10 22:36:49
【问题描述】:

我正在寻找如何将类成员转换为 C 风格的回调。

最近我发现了一个特殊的 bind hack 的答案,允许将类成员绑定到 C 风格的回调:

https://stackoverflow.com/a/39524069/5405443

我有这个工作代码将函数 MyClass::f 绑定到 C 函数 f: 但我想避免将 cb_type 作为模板参数显式传递给 c_bind 函数。 在提供的示例中,CB 具有类型 void (*)(int) 并且 Func 模板参数具有 void (MyClass::*)( int) 类型。

template<typename CB, typename Func, typename... Params>
CB* c_bind(std::_Bind<Func(Params...)> function) {
    return Callback<typename ActualType<CB>::type, __COUNTER__, Func>::getCallback(function);
}

typedef void (cb_type)(int);

class MyClass {
public:
    void f(int x) {
        std::cout << "Hello from MyClass::f(int), value: " << x << std::endl;
    }
};

int main() {
    MyClass mc;

    auto f = c_bind<cb_type>(std::bind(&MyClass::f, mc, std::placeholders::_1));
    //                ^ how to avoid explicit callback type declaration here?
    f(10);

    return 0;
}

我还发现了这段代码 (https://gist.github.com/vikchopde/73b62314379f733e8938f11b246df49c) 用于“展开”某种功能。

bool ok = fu::is_unwrappable<decltype(&MyClass::f)>::value; // always false
// fu::unwrap_function<decltype(&MyClass::f)>::type::function_ptr blah; // won't compile

但我不知道为什么它不会起作用。

我的问题是有什么解决方法可以从具有类成员指针的类型中提取返回类型和参数列表,例如 void (MyClass::*)(int) 并构造类 C 类型 无效 (*)(int) ?

感谢您的帮助!

【问题讨论】:

  • 你不会通过随机的谷歌搜索和阅读 Github 上的随机代码来解决这个问题。您需要从根本上彻底了解为什么不能这样投射,std::bind 做了什么,以及在这种情况下它是如何工作的。您需要了解所涉及的基本原理,应该是fully explained in every good C++ book。您可能不想听到有人告诉您“去读一本书并学习这个”,但这是您必须做的,才能正确地做到这一点。
  • 我曾经知道如何做到这一点,我将不得不挖掘代码。它涉及在结构中编写一些机器代码,将指针添加到对象。调用 PrestoChangoSelector。然后将结构类型转换为 C 函数。想法是调用 C 函数,将对象与参数一起压入堆栈,然后调用 C++ 函数(通过跳转指令)。
  • 抱歉,我以为是 Reddit 的 DarkMode!
  • std::_Bind 是标准库实现的实现细节。您不应该在代码中使用它。而是像链接代码一样使用 lambda 和 std::function

标签: c++ templates


【解决方案1】:

好吧,在 C++17 中,您可以将任意非类型参数传递给带有 template&lt;auto&gt; 的类。因此,我们可以将MyClass::f 存储为模板参数,并使用decltype 解析其类型。将此类型传递给另一个模板类后,我们可以使用模板特化提取所需的类型。

下面的代码展示了如何构造一个C风格的函数wrapper&lt;&gt;::func_type

由于您似乎将对象绑定到其成员函数,因此我另外编写了演示代码通过调用wrapper&lt;&gt;::bind 来执行此操作。希望对您有所帮助。

class MyClass {
public:
    void f(int x) {
    std::cout << "Hello from MyClass::f(int), value: " << x << std::endl;
    }
};

void f(int x) {
    std::cout << "Hello from f(int), value: " << x << std::endl;
}

template<auto F>
struct wrapper
{
    template<typename> struct inner;

    template<class Cls, typename Ret, typename... Args>
    struct inner<Ret(Cls::*)(Args...)>
    {
        using func_type = Ret(Args...);

        static auto bind(Cls *obj)
        {
            return [=](Args ...args){
                return (obj->*F)(std::forward<Args>(args)...);
            };
        }
    };

    using func_type = typename inner<decltype(F)>::func_type;
    static const constexpr auto bind = inner<decltype(F)>::bind;
};

int main() {
    MyClass mc;

    auto h = wrapper<&MyClass::f>::bind(&mc);
    h(10);

    using func_t = typename wrapper<&MyClass::f>::func_type;
    std::function<func_t> g = f;
    g(1);
    return 0;
}

【讨论】:

    【解决方案2】:

    首先,我要感谢@Dappur 提供的好例子。使用您的指南,我稍后将使用 std::_Bind 使用重写我丑陋的绑定接口。我还要感谢@Sam Varshavchik 提到了那套 C++ 书籍。我将开始阅读它以成为像您一样的 C++ 大师,以了解“为什么我不能像这样投射它”。但不幸的是,由于我糟糕的 c++ 经验,我现在仍然可以做到。这是工作代码:

    template<class T, unsigned int n, class CallerType>
    struct CallbackWrapper;
    
    template<class Ret, class... Params, unsigned int n, class CallerType>
    struct CallbackWrapper<Ret(Params...), n, CallerType> {
        static auto get(std::function<Ret(Params...)>&& fn) -> Ret(*)(Params...) {
            func = fn;
            return static_cast<Ret(*)(Params...)>(CallbackWrapper<Ret(Params...), n, CallerType>::callback);
        }
    
    private:
        static std::function<Ret(Params...)> func;
    
        static Ret callback(Params... args) {
            return func(args...);
        }
    };
    
    template<class Ret, class... Params, unsigned int n, class CallerType>
    std::function<Ret(Params...)> CallbackWrapper<Ret(Params...), n, CallerType>::func;
    
    template<typename T>
    struct lambda_to_stdf {
        using type = void;
    };
    
    template<typename Ret, typename Class, typename... Args>
    struct lambda_to_stdf<Ret(Class::*)(Args...) const> {
        using type = std::function<Ret(Args...)>;
    };
    
    template<class Ret, class Cls, class... Args1, class... Args2>
    auto c_bind(std::_Bind<Ret(Cls::*(Cls, Args1...))(Args2...)> function) -> Ret(*)(Args2...) {
        return CallbackWrapper<Ret(Args2...), __COUNTER__, Ret(Cls::*(Cls, Args1...))(Args2...)>::get(std::move(function));
    }
    
    template<class Ret, class... Args>
    auto c_bind(std::function<Ret(Args...)> function) -> Ret(*)(Args...) {
        return CallbackWrapper<Ret(Args...), __COUNTER__, std::function<Ret(Args...)>>::get(std::move(function));
    }
    
    template<class F>
    auto c_bind(F function) -> decltype(c_bind((typename lambda_to_stdf<decltype(&F::operator())>::type)(function))) {
        return c_bind((typename lambda_to_stdf<decltype(&F::operator())>::type)(function));
    }
    

    用法:

    class MyClass {
    public:
        void f(int x) {
            std::cout << "Hello from MyClass::f(int), value: " << x << std::endl;
        }
    };
    
    int main() {
        MyClass mc;
    
        auto f = c_bind(std::bind(&MyClass::f, mc, std::placeholders::_1));
        f(10);
    
        std::function<void(int)> stdf = [](int v) {
            std::cout << "hello from std::function, value: " << v << std::endl;
        };
        auto f2 = c_bind(stdf);
        f2(100);
    
        auto f3 = c_bind([](int v) -> int {
            std::cout << "hello from lambda, value: " << v << std::endl;
            return 5.0f;
        });
        f3(1000);
    
        return 0;
    }
    

    希望对某人有所帮助。

    【讨论】:

      猜你喜欢
      • 2018-02-24
      • 2019-11-19
      • 1970-01-01
      • 1970-01-01
      • 2020-02-05
      • 2021-09-06
      • 1970-01-01
      • 1970-01-01
      • 2015-12-03
      相关资源
      最近更新 更多