c ++ 14 — перенос C-функции со статическим параметром обратного вызова в C ++ — функцию, которая принимает закрытый член в качестве обратного вызова

Я хочу обернуть эту 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 объект в качестве функции обратного вызова, и я хочу получить доступ к частным данным объекта из этой функции.

0

Решение

Ключ является 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);
}
1

Другие решения

хорошо, так что после некоторых испытаний и неудач я получил способ добиться ИМЕННО ТАК Что я хочу сделать.

Мое решение не простое … мои страхи сбываются …

Потратив несколько часов на ползти по 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 неплохая идея).

комментарии очень приветствуются

0

По вопросам рекламы [email protected]