【问题标题】:Can I initialize an object's C style function pointer at compile time so that it calls the object's member function?我可以在编译时初始化对象的 C 风格函数指针,以便它调用对象的成员函数吗?
【发布时间】:2020-06-04 15:59:11
【问题描述】:

我正在编写一个类来包装一个需要回调函数指针的库。见下文:

struct LibraryConfig {
    // Omitting other members...
    void (*callback)(const char *);
};

class MyClass {
private:
    LibraryConfig m_config;

public:
    MyClass(const LibraryConfig &config) {
        // Initialize m_config using config, would like to set callback so that it calls
        // this->myCallback().
    }

    void myCallback(const char *);
};

只会声明 MyClass 的静态实例,因此可以在编译时保持构造。我已经尝试了接受 MyClass 指针的 lambdas 和模板函数,但我要么无法在构造函数中完成此操作,要么无法在编译时完成此操作(通过 this&myClass 获取实例的地址在编译时似乎不可能)。

constexpr 参数将来可能会被允许,这使得实现起来很简单,但是现在有没有办法用 C++20 来实现呢?

【问题讨论】:

  • fwiw,constexpr 参数不太可能被添加到语言中。

标签: c++ c++20


【解决方案1】:

是的,这显然是可能的。看下面的sn-p:

struct LibraryConfig {
    void (*callback)(const char *);
};

class MyClass {
private:
    LibraryConfig config;

public:
    consteval MyClass(const LibraryConfig& cfg) :
        config(cfg) {}

    void myCallback(const char *data);
};

int main()
{
    constinit static MyClass mc = {{
        [](const char *data) { mc.myCallback(data); }
    }};
}

请参阅 Compiler Explorer here 上的工作示例。由于mc 是静态的,因此允许 lambda 访问它而无需捕获。此解决方案可能有改进的余地,例如让 lambda 由函数产生。

编辑:

我想出了一个通用函数来创建 lambda:

template<auto& inst, auto func>
consteval auto make_member_callback()
{
    return []<typename... Args>(Args... args) { (inst.*func)(args...); };
}

这使得以下操作成为可能 (compiler explorer):

constinit static MyClass mc {{
    make_member_callback<mc, &MyClass::myCallback>()
}};

编辑 2:

这是我最后一次尝试,初始化是在类中完成的:

struct LibraryConfig {
    void (*callback)(const char *);
};

template<auto& v, auto f>
constexpr auto member_callback = [](auto... args) { (v.*f)(args...); };

class MyClass {
private:
    LibraryConfig config;

public:
    consteval MyClass(const LibraryConfig& cfg = {}) :
        config(cfg) {}

    template<MyClass& MC>
    constexpr static MyClass& init() {
        MC.config.callback = member_callback<MC, &MyClass::myCallback>;
        return MC;
    }

    void myCallback(const char *data);
};

int main()
{
    constinit static MyClass mc = (mc = MyClass(), MyClass::init<mc>());
}

【讨论】:

  • 我很确定这是 UB,因为您在自己的初始化程序中使用了 mc
  • DeviceConfig vs LibraryConfig 这是一个错字吗?
  • @idclev463035818 已修复。谢谢
  • @dfri / cigien 确定吗? mc 在 lambdas 主体中,只有在对象完全存活后才会使用
  • mc 具有静态存储持续时间,因此根本不会被捕获。只有自动存储持续时间和this 是可捕获的。静态存储时长基本上是全局变量,无法捕获。
猜你喜欢
  • 2016-10-13
  • 2014-11-03
  • 2019-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多