以前给客户写了个小程序, 处理sqlite执行sql时, 给定回调, 等sql执行完, 处理返回的行集.
那时候, 时间紧, 回调整的不太好, 只是能用而已.
这次给自己写账单分析程序, 关于用回调处理行集的部分, 想到了比以前好的方法.
可以只使用一个类静态回调函数(回调分发函数), 然后通过参数, 可以将具体干活的普通类成员函数, 赋值给回调参数. 程序简洁实用很多.
试过了, 好使.
demo就不单独写了, 从现有工程中, 摘取程序片段来说明.
关于回调这块, 因为有语法的细节, 即使知道这回事, 如果重新写一个新回调, 也会试错好多次.
让自己以后看, 能明白就行了.
bool COrderProcBase::db_proc_excel_to_db()
{
bool b_rc = false;
int ArySize = 0;
CStringArray strAry;
int i = 0;
int id = 0;
CString strOrgFile;
CString strCpyEnFile;
CFileOperation file_opt;
CString cs_drive;
CString cs_dir;
CString cs_name;
CString cs_ext;
CString csCsvToExcel;
CString csTmp;
do {
// 建立数据库中的表
// !! 在建立表之前, 要先检查表是否存在, 如果不存在, 才去建立表
// 否则在表存在的情况下, 再建立表, 就会返回错误, 错误原因是表已经存在
// 检查表是否存在是一段sql, 返回的行集是2行(列名称, 列值),1列
if (!is_db_tbl_exist()) // 这里要执行sql, 就会返回一个行集, 就用到了回调.
{
if (!db_create_tbl())
{
break;
}
}
bool COrderProcJH::is_db_tbl_exist()
{
return COrderProcBase::is_db_tbl_exist(_T("TBL_FILE_JH"));
}
bool COrderProcBase::is_db_tbl_exist(TCHAR* pszTblName)
{
// 表在数据中是否存在?
bool b_rc = false;
string str_utf8_sql;
string str_sql;
char* zErrMsg = NULL; //错误信息
const char* sql = NULL;
int rc = 0;
CString csMsg;
char szBuf[4096];
TAG_PARAM_CALLBACK_FOR_SQLITE3_EXEC param;
do {
_ASSERT(NULL != pszTblName);
if (NULL == pszTblName)
{
break;
}
memset(szBuf, 0, sizeof(szBuf));
sprintf(szBuf, "SELECT EXISTS(SELECT name FROM sqlite_schema WHERE type='table' AND name='%s');", getLogicProxy()->my_W2A(pszTblName).c_str());
str_sql = szBuf;
str_utf8_sql = getLogicProxy()->UnicodeToUTF8(getLogicProxy()->my_A2W(str_sql));
param.pThis = this;
param.pFn = &COrderProcBase::callback_for_sqlite3_exec_proc_tbl_exist;
param.row_set.clear();
// 回调的赋值 为参数3, 参数4
// 这样填写回调, 回调分发函数(类静态成员函数)只有一个, 不用变.
// 具体的回调处理函数(类普通成员函数), 赋值在参数中.
// 如果具体的回调处理函数不是这个类的this, 也可以在参数中指定. 这样就可以用任何类实例的任何普通成员函数来处理回调了, 很方便.
// 这个参数中要赋值的类实例指针, 可以是一个基类的指针, 让处理回调的类都是这个基类的子类, 有基类中规定的处理回调的纯虚接口就好.
rc = sqlite3_exec(get_db_handle(), str_utf8_sql.c_str(), COrderProcBase::callback_for_sqlite3_exec_dispatch, ¶m, &zErrMsg);
if (SQLITE_OK != rc) {
csMsg.Format(_T("错误 : %s"), getLogicProxy()->UTF8ToUnicode(zErrMsg).c_str());
getLogicProxy()->addTips(csMsg);
break;
}
// 等sql执行完, 只要执行的对, 就可以用另外一个函数来处理返回的结果集
// 针对不同类型的sql, 实现不同的结果集处理函数
b_rc = procResultSet_isTblExist(param.row_set, pszTblName);
} while (false);
if (NULL != zErrMsg) {
sqlite3_free(zErrMsg);
zErrMsg = NULL;
}
return b_rc;
}
// 类成员函数指针类型的定义
class COrderProcBase;
// PFN_CALLBACK_FOR_SQLITE3_EXEC是sqlite3_exec回调要求的函数样式
typedef int (COrderProcBase::* PFN_CALLBACK_FOR_SQLITE3_EXEC)(void* data, int argc, char** argv, char** azColName);
class COrderProcBase
{
public:
// ...
// 这个结构在类中直接定义的
typedef struct _tag_param_callback_for_sqlite3_exec{
COrderProcBase* pThis; // 本类的指针, 用来从回调分发函数(类静态成员函数) 执行到类的普通成员函数
PFN_CALLBACK_FOR_SQLITE3_EXEC pFn; // 本类中实际的回调处理函数指针(类的普通成员函数)
C_my_row_set row_set; // 执行sql后, 返回的行集 这里是装在返回行集的类
}TAG_PARAM_CALLBACK_FOR_SQLITE3_EXEC;
int COrderProcBase::callback_for_sqlite3_exec_dispatch(void* data, int argc, char** argv, char** azColName)
{
int i_rc = 0;
TAG_PARAM_CALLBACK_FOR_SQLITE3_EXEC* param;
do {
if (NULL == data)
{
break;
}
param = (TAG_PARAM_CALLBACK_FOR_SQLITE3_EXEC*)data;
if ((NULL != param->pThis) && (NULL != param->pFn))
{
// 这里就可以执行sqlite3_exec指定的回调函数了(任意类, 任意类的回调实现(类的普通成员函数))
// 这个回调分发函数基本不用再改.
i_rc = ((param->pThis)->*(param->pFn))(data, argc, argv, azColName);
}
} while (false);
return i_rc;
}
这个根据具体要执行的sql具体处理, 最普通的就是将行集先存起来.
等sqlite3_exec完事了, 再拿存的行集来判断结果.
以判断库中表是否存在为例
如果在回调中, 只是将行集存起来, 那么下面的回调就是一个通用的实现.
int COrderProcBase::callback_for_sqlite3_exec_proc_tbl_exist(void* data, int argc, char** argv, char** azColName)
{
int i_rc = 0;
TAG_PARAM_CALLBACK_FOR_SQLITE3_EXEC* param;
int i = 0;
do {
if (NULL == data)
{
break;
}
param = (TAG_PARAM_CALLBACK_FOR_SQLITE3_EXEC*)data;
C_my_row row;
C_my_key_val key_val;
for (i = 0; i < argc; i++)
{
key_val.m_cs_key = azColName[i];
key_val.m_cs_val = argv[i];
row.m_list.AddTail(key_val);
}
// 回调进来一次, 就是一行
param->row_set.m_list.AddTail(row);
} while (false);
return i_rc;
}
以判断库中表是否存在为例
这个实现应该和每种不同的sql相关, 拿回调中存好的行集做不同的判断.
bool COrderProcBase::procResultSet_isTblExist(C_my_row_set& row_set, const TCHAR* pszTblName)
{
bool b_rc = false;
POSITION pos;
POSITION pos1;
C_my_row row;
C_my_row row1;
C_my_key_val key_val;
TCHAR szBuf[4096];
bool b_stop = false;
do {
if (NULL == pszTblName)
{
break;
}
if (1 != row_set.m_list.GetSize())
{
break;
}
for (pos = row_set.m_list.GetHeadPosition(); (NULL != pos); row_set.m_list.GetNext(pos))
{
row = row_set.m_list.GetAt(pos);
if (1 != row.m_list.GetSize())
{
break;
}
for (pos1 = row.m_list.GetHeadPosition(); (NULL != pos1); row.m_list.GetNext(pos1))
{
b_stop = true;
key_val = row.m_list.GetAt(pos1);
memset(szBuf, 0, sizeof(szBuf));
_stprintf(szBuf, _T("EXISTS(SELECT name FROM sqlite_schema WHERE type='table' AND name='%s')"), pszTblName);
if (key_val.m_cs_key != szBuf)
{
break;
}
if (key_val.m_cs_val != _T("1"))
{
break;
}
b_rc = true;
break;
}
if (b_stop)
{
break;
}
}
} while (false);
return b_rc;
}