将带有静态回调参数的 C 函数包装到接受私有成员作为回调的 C++ 函数
wrapping a C-function with a static callback parameter to a C++-function which accepts a private member as callback
我想包装这个 C 函数,
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
在 C++ 函数中,例如,
namespace
IronMaiden
{
int
BookOfSouls::MyWrapper( DB* db
, std::string& sql
, ?? callback function ?? int (*callback)(void*,int,char**,char**) ?? )
{
.
.
.
sqlite3_exec( db->get(), sql.c_str(), ?? callback ??, nullptr, nullptr);
.
.
.
}
}
我可以将任何静态函数传递给 sqlite3_exec()
,但我想从 BookOfSouls
对象传递一个私有成员函数作为回调函数,并且我想访问该对象的私有数据来自那个函数。
关键是void*
参数:
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
回调是一个自由函数,但您可以提供任意第一个参数。所以如果你想传入一个成员函数,我们可以利用无捕获 lambda 可以转换为函数指针这一事实(感谢 some sorcery):
template <typename F, int (F::*mem)(int, char**, char**)>
void set_callback(F* f) {
sqlite3_exec(db->get(), sql.c_str(),
+[](void* arg, int i, char** p, char**q){
return (static_cast<F*>(arg)->*mem)(i, p, q);
},
f,
nullptr, nullptr);
}
请注意,成员函数也必须是模板参数,这样 lambda 就不必捕获。如果您只需要一个特定的函数名称,它会更简单,例如sqlCallback()
,那么您的回调函数将是:
+[](void* arg, int i, char** p, char** q) {
return static_cast<F*>(arg)->sqlCallback(i, p, q);
}
好的,所以在一些测试和失败之后,我找到了一种方法来实现 EXACTLY 我想做的事情。
我的解决方案并不简单...我的恐惧成真...
花了几个小时在 SO 和互联网上爬行(就像在一个黑暗、寒冷和令人毛骨悚然的地牢里,带着我的手电筒和一些 Angelscourge 的音乐)我得出结论是 没有这么简单的方法!
/*...I can't blame a C lib to don't support some C++ feature...*/
所以,我需要找到另一种策略...所以我大量使用我的大脑(a.k.a。撞头)...突然出现了一个解决方案:
/* The solution uses `libsigc++` AND a static function. */
静态函数在包装器的命名空间中。这个静态函数会做一件事,发出一个信号。因此,如果我采用与问题中相同的示例,
namespace
IronMaiden
{
sigc::signal<int,void*,int,char**,char**> signalCB; // `extern` is a friend
.
.
.
static int
callback( void *PersonalUse
, int argc
, char **argv
, char **azColName )
{
return IronMaiden::signalCB.emit( PersonalUse, argc, argv, azColName);
}
.
.
.
int
BookOfSouls::MyWrapper( DB* db
, std::string& sql
, const sigc::slot<int,void*,int,char**,char**>& slotCB )
{
.
.
.
sigc::connection signalConnection = IronMaiden::signalCB.connect( slotCB );
sqlite3_exec( db->get(), sql.c_str(), callback, nullptr, nullptr);
signalConnection.disconnect();
.
.
.
}
}
要连接一个(私有成员)函数,比方说YearOfTheGoat::ImTheCallbackFunction()
,从一个YearOfTheGoat
对象,
YearOfTheGoat::ImSomeFunctionFromWhereIWantToUseMyWrapper()
{
IronMaiden::MyWrapper( DBobj, transaction
, sigc::mem_fun(*this, &YearOfTheGoat::ImTheCallbackFunction) );
}
所以现在每次 sqlite3_exec()
调用 callback()
时,都会发出信号,然后调用 ImTheCallbackFunction()
一些重要说明:
- 在此示例中,
ImTheCallbackFunction()
的参数和 return 类型必须与 callback()
的相同。
- 注意断开连接,否则信号将保持连接状态。
- 到目前为止,我对该解决方案没有任何问题。但是您必须明白,我是在程序中编写代码之后才写下这个答案的。所以我不会声称它是 最好的 解决方案。但它 WORKS!
- 关于
sqlite3_exec
的注意事项,似乎只有在 returns 有内容时才会调用 cb 函数。
- 不要混淆
BookOfSouls
和 YearOfTheGoat
。第一个是 NON 实例化。第二,我使用了一个对象。 (N.B。因为 BookOfSouls
是不可实例化的,所以将 callback
和 sigc::signal
移动到 BookOfSouls
中并不是一个坏主意。
欢迎评论
我想包装这个 C 函数,
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
在 C++ 函数中,例如,
namespace
IronMaiden
{
int
BookOfSouls::MyWrapper( DB* db
, std::string& sql
, ?? callback function ?? int (*callback)(void*,int,char**,char**) ?? )
{
.
.
.
sqlite3_exec( db->get(), sql.c_str(), ?? callback ??, nullptr, nullptr);
.
.
.
}
}
我可以将任何静态函数传递给 sqlite3_exec()
,但我想从 BookOfSouls
对象传递一个私有成员函数作为回调函数,并且我想访问该对象的私有数据来自那个函数。
关键是void*
参数:
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
回调是一个自由函数,但您可以提供任意第一个参数。所以如果你想传入一个成员函数,我们可以利用无捕获 lambda 可以转换为函数指针这一事实(感谢 some sorcery):
template <typename F, int (F::*mem)(int, char**, char**)>
void set_callback(F* f) {
sqlite3_exec(db->get(), sql.c_str(),
+[](void* arg, int i, char** p, char**q){
return (static_cast<F*>(arg)->*mem)(i, p, q);
},
f,
nullptr, nullptr);
}
请注意,成员函数也必须是模板参数,这样 lambda 就不必捕获。如果您只需要一个特定的函数名称,它会更简单,例如sqlCallback()
,那么您的回调函数将是:
+[](void* arg, int i, char** p, char** q) {
return static_cast<F*>(arg)->sqlCallback(i, p, q);
}
好的,所以在一些测试和失败之后,我找到了一种方法来实现 EXACTLY 我想做的事情。
我的解决方案并不简单...我的恐惧成真...
花了几个小时在 SO 和互联网上爬行(就像在一个黑暗、寒冷和令人毛骨悚然的地牢里,带着我的手电筒和一些 Angelscourge 的音乐)我得出结论是 没有这么简单的方法!
/*...I can't blame a C lib to don't support some C++ feature...*/
所以,我需要找到另一种策略...所以我大量使用我的大脑(a.k.a。撞头)...突然出现了一个解决方案:
/* The solution uses `libsigc++` AND a static function. */
静态函数在包装器的命名空间中。这个静态函数会做一件事,发出一个信号。因此,如果我采用与问题中相同的示例,
namespace
IronMaiden
{
sigc::signal<int,void*,int,char**,char**> signalCB; // `extern` is a friend
.
.
.
static int
callback( void *PersonalUse
, int argc
, char **argv
, char **azColName )
{
return IronMaiden::signalCB.emit( PersonalUse, argc, argv, azColName);
}
.
.
.
int
BookOfSouls::MyWrapper( DB* db
, std::string& sql
, const sigc::slot<int,void*,int,char**,char**>& slotCB )
{
.
.
.
sigc::connection signalConnection = IronMaiden::signalCB.connect( slotCB );
sqlite3_exec( db->get(), sql.c_str(), callback, nullptr, nullptr);
signalConnection.disconnect();
.
.
.
}
}
要连接一个(私有成员)函数,比方说YearOfTheGoat::ImTheCallbackFunction()
,从一个YearOfTheGoat
对象,
YearOfTheGoat::ImSomeFunctionFromWhereIWantToUseMyWrapper()
{
IronMaiden::MyWrapper( DBobj, transaction
, sigc::mem_fun(*this, &YearOfTheGoat::ImTheCallbackFunction) );
}
所以现在每次 sqlite3_exec()
调用 callback()
时,都会发出信号,然后调用 ImTheCallbackFunction()
一些重要说明:
- 在此示例中,
ImTheCallbackFunction()
的参数和 return 类型必须与callback()
的相同。 - 注意断开连接,否则信号将保持连接状态。
- 到目前为止,我对该解决方案没有任何问题。但是您必须明白,我是在程序中编写代码之后才写下这个答案的。所以我不会声称它是 最好的 解决方案。但它 WORKS!
- 关于
sqlite3_exec
的注意事项,似乎只有在 returns 有内容时才会调用 cb 函数。 - 不要混淆
BookOfSouls
和YearOfTheGoat
。第一个是 NON 实例化。第二,我使用了一个对象。 (N.B。因为BookOfSouls
是不可实例化的,所以将callback
和sigc::signal
移动到BookOfSouls
中并不是一个坏主意。
欢迎评论