是否可以在编译时初始化对象的 C 样式函数指针,以便它调用对象的成员函数?

Can I initialize an object's C style function pointer at compile time so that it calls the object's member function?

本文关键字:函数 对象 指针 成员 调用 编译 是否 初始化 样式      更新时间:2023-10-16

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

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 指针的 lambda 和模板函数,但我要么无法在构造函数中完成此操作,要么无法在编译时实现(在编译时通过this&myClass获取实例的地址似乎是不可能的(。

将来可能会允许constexpr参数,这使得实现起来变得微不足道,但是现在有没有办法用 C++20 来实现这一点?

是的,这显然是可能的。请参阅以下片段:

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); }
}};
}

在此处查看编译器资源管理器的工作示例。由于mc是静态的,因此允许 lambda 访问它而无需捕获。此解决方案可能有改进的余地,例如,由函数生成 lambda。

编辑:

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

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

这允许执行以下操作(编译器资源管理器(:

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>());
}