使用好友 class 隐藏私有静态回调方法
Use of a friend class to hide private static callback methods
我需要用一些 API 注册一个 table 函数指针。假设 API 看起来像这样:
void (*FuncPtr)(void*);
void RegisterCallbacks(FuncPtr const (&callbacks)[10], void* context);
因为我正在编写 OOP 代码,所以我在我的 class 中创建静态 "thunk" 方法,这些方法依次调用我的 class 的对象上的方法。我使用 context
指针将指针传递给我的对象。我想在静态初始化时静态分配整个 table 回调指针。所以我最终得到了这个:
class MyClass
{
public:
static void Thunk (void* context)
{
reinterpret_cast<MyClass*>(context)->Method();
}
void Method ();
//
// More callback thunks and methods...
//
MyClass ()
{
RegisterCallbacks(s_callbackTable, this);
}
private:
static const FuncPtr s_callbackTable[];
}
//
// In the .cpp file:
//
const FuncPtr MyClass::s_callbackTable[] =
{
&MyClass::Thunk,
...
};
这种方法的问题是我需要使 MyClass::Thunk
publically 可见,以便静态初始化可以访问它。
编辑:我错了。显然你不需要 public 来做到这一点。
不过我宁愿不要。我可以想到两种方法来解决这个问题。
哪种方法更好?
创建一个私有静态方法,并使回调table成为方法内部的静态变量。这样我也可以使用指向私有方法的指针来初始化它。从构造函数内部调用私有静态方法以获取 table.
创建一个包含 public 静态 thunk 方法的单独 class,并使 class 成为 MyClass
的朋友。 Forward declare that class in the header for MyClass
, 只是为了允许友元声明。我喜欢这个,因为它还对任何外部消费者完全隐藏了 thunk 方法。不过,我不太确定这样做是否有切实的好处。
像这样:
// .h file:
class MyClassInternals
class MyClass
{
public:
void Method ();
private:
friend class MyClassInternals;
static const FuncPtr s_callbackTable[];
}
// .cpp file:
class MyClassInternals
{
public:
static void Thunk(void* context)
{
reinterpret_cast<MyClass*>(context)->Method();
}
}
const FuncPtr MyClass::s_callbackTable[] =
{
&MyClassInternals::Thunk,
...
};
初始化同一个 class 的成员时,您无需使 Thunk
可见即可使用它。
修复了一些小的语法错误后,您的原始示例在 Thunk
为 private
时编译得很好。
您可以简单地将所有蹦床机械保留在构造函数实现 (.cpp) 中,无需暴露任何关于接口级别的蹦床 (.h):
MyClass::MyClass() {
struct trampolines {
static void method1(void *ctx) { ((MyClass *)ctx)->method1(); }
static void method2(void *ctx) { ((MyClass *)ctx)->method2(); }
};
static void (*callbacks[])(void*) = { trampolines::method1,
trampolines::method2 };
register_callbacks(callbacks, this);
}
您甚至不需要在 (.h) 中包含使用 callback/context 接口的 C 库的头文件。这更干净,因为您的模块的客户端将不依赖于那些 C 细节。
我需要用一些 API 注册一个 table 函数指针。假设 API 看起来像这样:
void (*FuncPtr)(void*);
void RegisterCallbacks(FuncPtr const (&callbacks)[10], void* context);
因为我正在编写 OOP 代码,所以我在我的 class 中创建静态 "thunk" 方法,这些方法依次调用我的 class 的对象上的方法。我使用 context
指针将指针传递给我的对象。我想在静态初始化时静态分配整个 table 回调指针。所以我最终得到了这个:
class MyClass
{
public:
static void Thunk (void* context)
{
reinterpret_cast<MyClass*>(context)->Method();
}
void Method ();
//
// More callback thunks and methods...
//
MyClass ()
{
RegisterCallbacks(s_callbackTable, this);
}
private:
static const FuncPtr s_callbackTable[];
}
//
// In the .cpp file:
//
const FuncPtr MyClass::s_callbackTable[] =
{
&MyClass::Thunk,
...
};
这种方法的问题是我需要使 MyClass::Thunk
publically 可见,以便静态初始化可以访问它。
编辑:我错了。显然你不需要 public 来做到这一点。
不过我宁愿不要。我可以想到两种方法来解决这个问题。
哪种方法更好?
创建一个私有静态方法,并使回调table成为方法内部的静态变量。这样我也可以使用指向私有方法的指针来初始化它。从构造函数内部调用私有静态方法以获取 table.
创建一个包含 public 静态 thunk 方法的单独 class,并使 class 成为
MyClass
的朋友。 Forward declare that class in the header forMyClass
, 只是为了允许友元声明。我喜欢这个,因为它还对任何外部消费者完全隐藏了 thunk 方法。不过,我不太确定这样做是否有切实的好处。像这样:
// .h file: class MyClassInternals class MyClass { public: void Method (); private: friend class MyClassInternals; static const FuncPtr s_callbackTable[]; } // .cpp file: class MyClassInternals { public: static void Thunk(void* context) { reinterpret_cast<MyClass*>(context)->Method(); } } const FuncPtr MyClass::s_callbackTable[] = { &MyClassInternals::Thunk, ... };
初始化同一个 class 的成员时,您无需使 Thunk
可见即可使用它。
修复了一些小的语法错误后,您的原始示例在 Thunk
为 private
时编译得很好。
您可以简单地将所有蹦床机械保留在构造函数实现 (.cpp) 中,无需暴露任何关于接口级别的蹦床 (.h):
MyClass::MyClass() {
struct trampolines {
static void method1(void *ctx) { ((MyClass *)ctx)->method1(); }
static void method2(void *ctx) { ((MyClass *)ctx)->method2(); }
};
static void (*callbacks[])(void*) = { trampolines::method1,
trampolines::method2 };
register_callbacks(callbacks, this);
}
您甚至不需要在 (.h) 中包含使用 callback/context 接口的 C 库的头文件。这更干净,因为您的模块的客户端将不依赖于那些 C 细节。