如何获得在C ++中使用C回调返回的结果

时间:2015-06-02 12:03:46

标签: c++ sqlite design-patterns callback

我很擅长在C ++中处理C回调。我创建了一个sqlite包装器c ++类,它只调用sqlite3_exec()。

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
  SqliteAccessor* sqlite = static_cast<SqliteAccessor*> NotUsed; 
  if(argc > 0) { 
    sqlite->set_table_exists(true); 
  }
  return 0;
}

class SqliteAccessor{    
public:
bool has_table(const string dbName, const string tblName)
{
  string sql;
  sql = "SELECT " + quote_string(tblName) + "FROM " + quote_string(dbName)
           + "WHERE type = 'table' AND name = " + quote_string(tblName) + ";";
  char *zErrMsg = 0;
  int rc = sqlite3_exec(m_db, sql.c_str(), callback, (void*) this, &zErrMsg);
  if( rc != SQLITE_OK ){
    printf("SQL error: %s", zErrMsg);
    sqlite3_free(zErrMsg);
  }
  // anyway to return the result directly?
  // return hasTable;
}

// can I avoid the following methods and the member variable? 
void set_table_exists(bool isExist) { m_table_exist = isExist; }
bool get_table_exists() { return m_table_exist; }
private: 
  static bool m_table_exist; 
};

int caller(){
   SqliteAccessor sqlite;
   // to check if table exist
   if (sqlite->has_table()){
      // will above work or 
      // I should do with an extra call to query the changed state?        
   }
}

现在,我很困惑调用者如何从sqlite包装器中获取结果。我认为,调用者只能通过调用has_table()来获得结果,因为结果是从set_table_exists()的回调中返回的。所以呼叫者通过进行另一次呼叫来获得结果,例如致电sqlite->get_table_exists()

然后这意味着对于每个回调,我需要在类SqliteAccessor中创建一个成员变量(也必须是static),以及一对set/get_state(),这将非常麻烦。

如何设计类以使调用者能够使用它? 不幸的是,我们的代码库不支持c ++ 11。

2 个答案:

答案 0 :(得分:2)

如果您使用的是C ++ 11,请考虑使用lambda而不是回调。

class SqliteAccessor{    
public:
bool has_table(const string dbName, const string tblName)
{
  bool hasTable = false;
  string sql;
  sql = "SELECT " + quote_string(tblName) + "FROM " + quote_string(dbName)
           + "WHERE type = 'table' AND name = " + quote_string(tblName) + ";";
  char *zErrMsg = 0;
  int rc = sqlite3_exec(m_db, sql.c_str(), [&](void *NotUsed, int argc, char **argv, char **azColName){
  SqliteAccessor* sqlite = static_cast<SqliteAccessor*> NotUsed; 
  if(argc > 0) { 
    hasTable = true; 
  }
 }
 , (void*) this, &zErrMsg);
  if( rc != SQLITE_OK ){
    printf("SQL error: %s", zErrMsg);
    sqlite3_free(zErrMsg);
  }
  return hasTable;
}
};

如果您无法访问C ++ 11,则可以随时手动编写您的仿函数。你虽然失去了一点简洁和地方。好的部分是仿函数可以保存你需要的状态。

struct callback{
  bool operator(void *NotUsed, int argc, char **argv, char **azColName)
  {
  SqliteAccessor* sqlite = static_cast<SqliteAccessor*> NotUsed; 
  if(argc > 0) { 
    hasTable = true; 
  }
  return false;
  }
  bool hasTable;
};


class SqliteAccessor{    
public:
bool has_table(const string dbName, const string tblName)
{
  bool hasTable = false;
  string sql;
  sql = "SELECT " + quote_string(tblName) + "FROM " + quote_string(dbName)
           + "WHERE type = 'table' AND name = " + quote_string(tblName) + ";";
  char *zErrMsg = 0;

  callback c;
  int rc = sqlite3_exec(m_db, sql.c_str(), c, (void*) this, &zErrMsg);
  if( rc != SQLITE_OK ){
    printf("SQL error: %s", zErrMsg);
    sqlite3_free(zErrMsg);
  }
  return c.hasTable;
}
};

答案 1 :(得分:1)

我这样做的方法是使回调成为一个私有的静态成员函数,基本上就是你做的。像这样:

// case: exports.myFunc = myFunc
var myModule = require('myModule');
myModule.myFunc();

如果sqlite3_exec()不能使用静态函数,您可以尝试使用如下全局函数:

class SqliteAccessor
{
public:
    bool has_table(const std::string dbName, const std::string tblName);

private:
    static int callback(void *NotUsed, int argc, char **argv, char **azColName);

    bool m_hasTable;
};

int SqliteAccessor::callback(void *NotUsed, int argc, char **argv, char **azColName)
{
     SqliteAccessor* sqlite = static_cast<SqliteAccessor*>(NotUsed);
     if(argc > 0) sqlite->m_hasTable = true;
     return 0;
}


bool SqliteAccessor::has_table(const std::string dbName, const std::string tblName)
{
    m_hasTable = false;
    string sql = "SELECT " + quote_string(tblName) + "FROM " + quote_string(dbName)
           + "WHERE type = 'table' AND name = " + quote_string(tblName) + ";";
    char *zErrMsg = 0;
    int rc = sqlite3_exec(m_db, sql.c_str(), callback, (void*) this, &zErrMsg);
    if( rc != SQLITE_OK )
    {
        printf("SQL error: %s", zErrMsg);
        sqlite3_free(zErrMsg);
    }

    return m_hasTable;
}

int caller()
{
   SqliteAccessor sqlite;
   // to check if table exist
   if (sqlite.has_table())
   {
      // do stuff :)
   }
}