我很擅长在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。
答案 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 :)
}
}