Я хочу обернуть эту 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 */
Обратный вызов является бесплатной функцией, но вы можете предоставить произвольный первый аргумент в. Поэтому, если вы хотите передать функцию-член, мы можем воспользоваться тем, что лямбда без захвата может быть преобразована в указатель на функцию (благодаря какое-то колдовство):
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);
}
Обратите внимание, что функция-член также должна быть аргументом шаблона, чтобы лямбда-код не захватывал. Было бы проще, если бы вам просто потребовалось конкретное имя функции, например sqlCallback()
тогда ваша функция обратного вызова будет:
+[](void* arg, int i, char** p, char** q) {
return static_cast<F*>(arg)->sqlCallback(i, p, q);
}
хорошо, так что после некоторых испытаний и неудач я получил способ добиться ИМЕННО ТАК Что я хочу сделать.
Мое решение не простое … мои страхи сбываются …
Потратив несколько часов на ползти по SO и интернету ( как в темном, холодном и жутком подземелье, с моим светлым факелом и музыкой Angelscourge ) Я заключаю, что есть НЕТ такая вещь, как простой способ!
/*...I can't blame a C lib to don't support some C++ feature...*/
Итак, мне нужно было найти другую стратегию … поэтому я интенсивно использовал свой мозг (a.k.a. headbanging) … там выскочило решение:
/* 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()
должны быть идентичны тем callback()
, sqlite3_exec
Кажется, что функция cb вызывается только в том случае, если есть, что возвращать.BookOfSouls
а также YearOfTheGoat
, Первый NON instanciable. Во-вторых, я использовал объект. (С момента BookOfSouls
не является нематериальным, движущимся callback
а также sigc::signal
внутри BookOfSouls
неплохая идея).комментарии очень приветствуются