0

我想包装这个 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对象的私有成员函数作为回调函数传递,并且我想从该函数访问对象的私有数据。

4

2 回答 2

1

关键是void*论据:

int (*callback)(void*,int,char**,char**),  /* Callback function */
void *,                                    /* 1st argument to callback */

回调是一个自由函数,但您可以提供任意的第一个参数。所以如果你想传入一个成员函数,我们可以利用一个无捕获的 lambda 可以转换为函数指针的事实(感谢一些巫术):

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);
}
于 2015-09-05T17:59:05.943 回答
0

好的,所以经过一些测试和失败后,我找到了一种方法来实现想要做的事情。

我的解决方案并不简单……我的恐惧成真了……

在花了几个小时在 SO 和互联网上爬行(就像在一个黑暗、寒冷和令人毛骨悚然的地牢中,用我的手电筒和一些 Angelscourge 的音乐后,我得出结论,没有一种直截了当的方法!

         /*...I can't blame a C lib to don't support some C++ feature...*/

所以,我需要找到另一种策略......所以我大量使用我的大脑(又名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 函数。
  • 不要混淆BookOfSoulsYearOfTheGoat。第一个是不可实例化的。第二,我使用了一个对象。(注意,因为BookOfSouls它是不可实例化的,所以移动callbacksigc::signal内部BookOfSouls并不是一个坏主意)。

非常欢迎评论

于 2015-09-07T00:18:20.823 回答