KG-FCL(GAME)
Sqlite3Database.h
cpp
复制代码
#pragma once
#include <iostream>
#include <atomic>
#include <iomanip>
#include <functional>
#include "sqlite3/sqlite3.h"
class Sqlite3Database {
struct PreparedStatement {
~PreparedStatement() noexcept;
sqlite3_stmt* p = NULL;
};
public:
virtual ~Sqlite3Database() noexcept;
bool Open(const char* szDatabaseFilePath) noexcept;
bool IsAvailable() noexcept { return NULL != pDB_; }
std::string GetDatabaseFilePath() noexcept { return pDatabaseFilePath_; }
int GetAffectedRows() noexcept;
public:
int ExecuteNonQuery(const char* szSQL, const char** szParams, int nParamCount) noexcept;
template <typename StringIterator>
int ExecuteNonQuery(const char* szSQL, const StringIterator& itParamTail, const StringIterator& itParamEndl) noexcept;
public:
using RowHandler = std::function<bool(Sqlite3Database*, sqlite3_stmt*)>;
template <typename StringIterator>
int ExecuteQuery(const char* szSQL, const StringIterator& itParamTail, const StringIterator& itParamEndl, const RowHandler& rows) noexcept;
private:
bool Finalize(sqlite3* pDB) noexcept;
int Internal_ExecuteQuery(const char* szSQL, void* pState, bool cbFillParams(Sqlite3Database*, sqlite3_stmt*, void*), bool (*cbRow)(Sqlite3Database*, sqlite3_stmt*, void*)) noexcept;
int Internal_ExecuteNonQuery(const char* szSQL, void* pState, bool cbFillParams(Sqlite3Database*, sqlite3_stmt*, void*)) noexcept;
template <typename StringIterator>
bool Internal_FillQueryParameters(sqlite3_stmt* pStmt, const StringIterator& itTail, const StringIterator& itEndl) noexcept;
// C++ 11/14(c++0x/1y)
template <typename T>
typename std::enable_if<std::is_pointer<typename std::decay<T>::type>::value, bool>::type
Internal_FillQueryParameters_Bind_Text(sqlite3_stmt* pStmt, int index, T&& param)
{
const char* szParam = param ? param : "";
return sqlite3_bind_text(pStmt, index, szParam, -1, SQLITE_STATIC) == SQLITE_OK;
}
template <typename T>
typename std::enable_if<!std::is_pointer<typename std::decay<T>::type>::value, bool>::type
Internal_FillQueryParameters_Bind_Text(sqlite3_stmt* pStmt, int index, T&& param)
{
return sqlite3_bind_text(pStmt, index, param.data(), param.size(), SQLITE_STATIC) == SQLITE_OK;
}
private:
std::atomic<sqlite3*> pDB_ = NULL;
std::string pDatabaseFilePath_;
};
template <typename StringIterator>
bool Sqlite3Database::Internal_FillQueryParameters(sqlite3_stmt* pStmt, const StringIterator& itTail, const StringIterator& itEndl) noexcept
{
if (itTail == itEndl) {
return true;
}
int iParamIndex = 1;
for (auto it = itTail; it != itEndl; ++it) {
using TString = typename std::remove_reference<typename std::remove_const<decltype(*it)>::type>::type;
TString szParam = std::move(*it);
Internal_FillQueryParameters_Bind_Text(pStmt, iParamIndex++, szParam); // non-c++17(1z) not using constexpr.
}
return true;
}
template <typename StringIterator>
inline
int Sqlite3Database::ExecuteNonQuery(const char* szSQL, const StringIterator& itParamTail, const StringIterator& itParamEndl) noexcept {
if (NULL == szSQL || *szSQL == '\x0') {
return -1;
}
struct ExecuteNonQueryContext {
StringIterator itTail;
StringIterator itEndl;
} stExecuteNonQueryContext{ itParamTail, itParamEndl };
return Internal_ExecuteNonQuery(szSQL, &stExecuteNonQueryContext,
[](Sqlite3Database* pDB, sqlite3_stmt* pStmt, void* p) noexcept -> bool {
auto* pCtx = static_cast<ExecuteNonQueryContext*>(p);
return pDB->Internal_FillQueryParameters(pStmt, pCtx->itTail, pCtx->itEndl);
});
}
template <typename StringIterator>
inline
int Sqlite3Database::ExecuteQuery(const char* szSQL, const StringIterator& itParamTail, const StringIterator& itParamEndl, const RowHandler& rows) noexcept {
if (NULL == szSQL || *szSQL == '\x0') {
return -1;
}
if (!rows) {
return -1;
}
struct ExecuteQueryContext {
StringIterator itTail;
StringIterator itEndl;
RowHandler fnRows;
} stExecuteQueryContext{ itParamTail, itParamEndl, rows };
auto fnFillParams =
[](Sqlite3Database* pDB, sqlite3_stmt* pStmt, void* p) noexcept -> bool {
ExecuteQueryContext* pCtx = static_cast<ExecuteQueryContext*>(p);
return pDB->Internal_FillQueryParameters(pStmt, pCtx->itTail, pCtx->itEndl);
};
auto fnRows =
[](Sqlite3Database* pDB, sqlite3_stmt* pStmt, void* p) noexcept -> bool {
ExecuteQueryContext* pCtx = static_cast<ExecuteQueryContext*>(p);
return pCtx->fnRows(pDB, pStmt);
};
return Internal_ExecuteQuery(szSQL, &stExecuteQueryContext, fnFillParams, fnRows);
}
Sqlite3Database.cpp
cpp
复制代码
#include "stdafx.h"
#include "Sqlite3Database.h"
#include "sqlite3/sqlite3.h"
//#ifdef _MSC_VER
//#pragma comment(lib, "sqlite3/sqlite3.lib")
//#endif
// 假设 LogOutput 已定义,这里不再重复包含头文件
Sqlite3Database::PreparedStatement::~PreparedStatement() noexcept {
sqlite3_stmt* stmt = p;
if (stmt) {
p = NULL;
sqlite3_finalize(stmt);
// 日志:语句被清理
LogOutput(KGLOG_DEBUG, "PreparedStatement finalized.\n");
}
}
Sqlite3Database::~Sqlite3Database() noexcept {
sqlite3* pOld = pDB_.exchange(NULL);
Finalize(pOld);
// 日志:数据库对象析构
LogOutput(KGLOG_DEBUG, "Sqlite3Database destroyed.\n");
}
bool Sqlite3Database::Open(const char* szDatabaseFilePath) noexcept {
if (NULL == szDatabaseFilePath || *szDatabaseFilePath == '\x0') {
LogOutput(KGLOG_WARNING, "Open failed: invalid file path.\n");
return false;
}
sqlite3* pDB;
int rc = sqlite3_open(szDatabaseFilePath, &pDB);
if (rc) {
LogOutput(KGLOG_WARNING, "Open failed: sqlite3_open error %d for %s\n", rc, szDatabaseFilePath);
return false;
}
Finalize(pDB_.exchange(pDB));
pDatabaseFilePath_ = szDatabaseFilePath;
LogOutput(KGLOG_INFO, "Database opened: %s\n", szDatabaseFilePath);
return true;
}
bool Sqlite3Database::Finalize(sqlite3* pDB) noexcept {
if (NULL == pDB) {
return false;
}
if (pDB) {
sqlite3_close(pDB);
LogOutput(KGLOG_DEBUG, "Database closed.\n");
return true;
}
return false;
}
int Sqlite3Database::Internal_ExecuteNonQuery(const char* szSQL, void* pState, bool cbFillParams(Sqlite3Database*, sqlite3_stmt*, void*)) noexcept {
sqlite3* pDB = pDB_;
if (NULL == pDB) {
LogOutput(KGLOG_WARNING, "Internal_ExecuteNonQuery: no database connection.\n");
return -1;
}
PreparedStatement stStmt;
if (sqlite3_prepare_v2(pDB, szSQL, -1, &stStmt.p, NULL) != SQLITE_OK) {
LogOutput(KGLOG_WARNING, "Internal_ExecuteNonQuery: prepare failed for SQL: %s\n", szSQL ? szSQL : "(null)");
return -1;
}
if (!cbFillParams(this, stStmt.p, pState)) {
LogOutput(KGLOG_WARNING, "Internal_ExecuteNonQuery: binding parameters failed for SQL: %s\n", szSQL ? szSQL : "(null)");
return -1;
}
if (sqlite3_step(stStmt.p) != SQLITE_DONE) {
LogOutput(KGLOG_WARNING, "Internal_ExecuteNonQuery: step failed for SQL: %s\n", szSQL ? szSQL : "(null)");
return -1;
}
int nChanges = sqlite3_changes(pDB);
LogOutput(KGLOG_DEBUG, "Internal_ExecuteNonQuery: executed SQL: %s, rows affected: %d\n", szSQL ? szSQL : "(null)", nChanges);
return nChanges;
}
int Sqlite3Database::GetAffectedRows() noexcept {
sqlite3* pDB = pDB_;
if (NULL == pDB) {
return -1;
}
return sqlite3_changes(pDB);
}
int Sqlite3Database::ExecuteNonQuery(const char* szSQL, const char** szParams, int nParamCount) noexcept {
if (NULL == szSQL || *szSQL == '\x0') {
LogOutput(KGLOG_WARNING, "ExecuteNonQuery: empty SQL.\n");
return -1;
}
if (nParamCount < 0 || NULL == szParams && nParamCount != 0) {
LogOutput(KGLOG_WARNING, "ExecuteNonQuery: invalid parameters (nParamCount=%d, szParams=%p)\n", nParamCount, szParams);
return -1;
}
struct ExecuteNonQueryContext {
const char** szParams;
int nParamCount;
} stExecuteNonQueryContext;
stExecuteNonQueryContext.szParams = szParams;
stExecuteNonQueryContext.nParamCount = nParamCount;
LogOutput(KGLOG_DEBUG, "ExecuteNonQuery: SQL=%s, paramCount=%d\n", szSQL, nParamCount);
return Internal_ExecuteNonQuery(szSQL, &stExecuteNonQueryContext,
[](Sqlite3Database* pDB, sqlite3_stmt* pStmt, void* p) noexcept {
ExecuteNonQueryContext* pCtx = static_cast<ExecuteNonQueryContext*>(p);
for (int i = 0; i < pCtx->nParamCount; i++) {
const char* szParam = pCtx->szParams[i];
if (!szParam) {
szParam = "";
}
if (sqlite3_bind_text(pStmt, i + 1, szParam, -1, SQLITE_STATIC) != SQLITE_OK) {
LogOutput(KGLOG_WARNING, "ExecuteNonQuery: bind parameter %d failed.\n", i);
return false;
}
}
return true;
});
}
int Sqlite3Database::Internal_ExecuteQuery(const char* szSQL, void* pState, bool cbFillParams(Sqlite3Database*, sqlite3_stmt*, void*), bool (*cbRow)(Sqlite3Database*, sqlite3_stmt*, void*)) noexcept {
sqlite3* pDB = pDB_;
if (NULL == pDB) {
LogOutput(KGLOG_WARNING, "Internal_ExecuteQuery: no database connection.\n");
return -1;
}
PreparedStatement stStmt;
int rc = sqlite3_prepare_v2(pDB, szSQL, -1, &stStmt.p, NULL);
if (rc != SQLITE_OK) {
LogOutput(KGLOG_WARNING, "Internal_ExecuteQuery: prepare failed for SQL: %s, error=%d\n", szSQL ? szSQL : "(null)", rc);
return -1;
}
if (!cbFillParams(this, stStmt.p, pState)) {
LogOutput(KGLOG_WARNING, "Internal_ExecuteQuery: binding parameters failed for SQL: %s\n", szSQL ? szSQL : "(null)");
return -1;
}
int iRowCount = 0;
while (sqlite3_step(stStmt.p) == SQLITE_ROW) {
if (!cbRow(this, stStmt.p, pState)) {
LogOutput(KGLOG_DEBUG, "Internal_ExecuteQuery: row callback stopped at row %d.\n", iRowCount);
break;
}
++iRowCount;
}
LogOutput(KGLOG_DEBUG, "Internal_ExecuteQuery: executed SQL: %s, rows returned: %d\n", szSQL ? szSQL : "(null)", iRowCount);
return iRowCount;
}
Sqlite3DatabaseLuabackend.h
cpp
复制代码
#pragma once
#include <atomic>
#include <mutex>
#include <deque>
#include "JString.h"
#include "KThread.h"
#include "KLuaScriptData.h"
struct SQLITE3_DATABASE_PROTOCOL
{
DWORD dwScriptID;
int64_t nQueryID;
SharedString strFunctionName; // 特殊用法,不要模仿
KG_BOOL bSelectFoundRows;
KG_BOOL bGetAffectedRows;
KG_BOOL bGetFiledsName;
KG_BOOL bFiledsNameAsKey;
int nUserDataLuaRef;
size_t uDataLen;
};
struct SQLITE3_DATABASE_USER_QUERY_REQUEST : SQLITE3_DATABASE_PROTOCOL
{
BYTE byData[0];
};
struct SQLITE3_DATABASE_USER_QUERY_RESPOND : SQLITE3_DATABASE_PROTOCOL
{
KG_BOOL bResult;
int nFiledsCount;
int nRowCount;
int nFoundRows;
int nAffectedRows;
BYTE byData[0];
};
class Sqlite3Database;
class Sqlite3DatabaseLuabackend : public ILuaObject
{
typedef std::mutex SynchronizedObject;
typedef std::lock_guard<SynchronizedObject> SynchronizedObjectScope;
public:
typedef enum : uint8_t
{
ERROR_NOERROR = 0,
ERROR_SQL,
ERROR_PARAM,
ERROR_RELEASE,
ERROR_NOT_AVAILABLE,
ERROR_EXECUTE_QUERY,
ERROR_EXECUTE_NON_QUERY,
} ERRORS;
Sqlite3DatabaseLuabackend() noexcept;
virtual ~Sqlite3DatabaseLuabackend() noexcept;
static int64_t GetCurrentThreadId() noexcept;
KG_BOOL PushRequest(IKG_Buffer* pRequestBuffer) noexcept;
void Activate() noexcept { ExecuteAllRespond(); }
KG_BOOL Init() noexcept;
void UnInit() noexcept { Finalize(); }
bool IsAvailable() noexcept;
public:
DECLARE_LUA_CLASS(Sqlite3DatabaseLuabackend);
int LuaQuery(lua_State* L);
private:
IKG_Buffer* PopRequest() noexcept;
IKG_Buffer* PopRespond() noexcept;
void Finalize() noexcept;
int ExecuteAllRequests() noexcept;
KG_BOOL PushRespond(IKG_Buffer* pRequestBuffer) noexcept;
private:
KG_BOOL ExecuteRequest(IKG_Buffer* pRequestBuffer) noexcept;
KG_BOOL ExecuteQuery(SQLITE3_DATABASE_USER_QUERY_REQUEST* pRequest, const char* szSQL) noexcept;
KG_BOOL ExecuteNonQuery(SQLITE3_DATABASE_USER_QUERY_REQUEST* pRequest, const char* szSQL) noexcept;
private:
IKG_Buffer* NewRespondBuffer(ERRORS eError, SQLITE3_DATABASE_USER_QUERY_REQUEST* pRequest, size_t uRespondSize) noexcept;
bool LuaRegisterObject() noexcept;
private:
int ExecuteAllRespond() noexcept;
bool ExecuteRespond(IKG_Buffer* pRespondBuffer) noexcept;
private:
SynchronizedObject m_SyncObjReq_; // 请求同步
SynchronizedObject m_SyncObjRep_; // 响应同步
bool m_bDisposed_; // 已经释放
int64_t m_nQueryID_;
std::shared_ptr<Sqlite3Database> m_DB_; // SQLITE3本地文件数据库
std::deque<IKG_Buffer*> m_RequestDeque_; // 查询
std::deque<IKG_Buffer*> m_RespondDeque_; // 响应
KThread m_WorkThread_;
SharedString m_str_sqlite3;
SharedString m_str_bResult;
SharedString m_str_nQueryID;
SharedString m_str_szError;
SharedString m_str_nFoundRows;
SharedString m_str_nAffectedRows;
SharedString m_str_nFiledsCount;
SharedString m_str_nRowCount;
SharedString m_str_Fileds;
SharedString m_str_Rows;
};
Sqlite3DatabaseLuabackend.cpp
cpp
复制代码
#include "stdafx.h"
#include "SqliteDatabase.h"
#include "SqliteDatabaseLuabackend.h"
#include "DatabaseInteAuxiliary.h"
#include "TypeTraitsAuxiliary.h"
#include "KG_Memory.h"
#include <list>
#include <sstream>
#include <vector>
#include "sqlite3/sqlite3.h"
// SQLite 数据库文件名
static constexpr const char* SQLITE_DATABASE_DB_FILE_NMAE = "game.db";
/**
* @brief 构造函数,初始化成员变量并创建 SqliteDatabase 对象
*/
SqliteDatabaseLuabackend::SqliteDatabaseLuabackend() noexcept
: m_bDisposed_(false)
, m_nQueryID_(0)
, m_str_sqlite3(SHARED_STATIC_STRING("sqlite"))
, m_str_bResult(SHARED_STATIC_STRING("bResult"))
, m_str_nQueryID(SHARED_STATIC_STRING("nQueryID"))
, m_str_szError(SHARED_STATIC_STRING("szError"))
, m_str_nFoundRows(SHARED_STATIC_STRING("nFoundRows"))
, m_str_nAffectedRows(SHARED_STATIC_STRING("nAffectedRows"))
, m_str_nFiledsCount(SHARED_STATIC_STRING("nFiledsCount"))
, m_str_nRowCount(SHARED_STATIC_STRING("nRowCount"))
, m_str_Fileds(SHARED_STATIC_STRING("Fileds"))
, m_str_Rows(SHARED_STATIC_STRING("Rows"))
{
m_DB_ = std::make_shared<SqliteDatabase>();
}
/**
* @brief 析构函数,调用 Finalize 清理资源
*/
SqliteDatabaseLuabackend::~SqliteDatabaseLuabackend()
{
Finalize();
}
/**
* @brief 释放资源,停止工作线程,清空请求/响应队列
*/
void SqliteDatabaseLuabackend::Finalize() noexcept
{
std::deque<IKG_Buffer*> dqBuffersArray[2];
m_bDisposed_ = true; // 标记已释放,阻止新请求入队
m_WorkThread_.Destroy(); // 等待工作线程退出
// 将请求队列转移到局部变量,便于释放
for (SynchronizedObjectScope _(m_SyncObjReq_);;)
{
dqBuffersArray[0] = std::move(m_RequestDeque_);
m_RequestDeque_.clear();
break;
}
// 将响应队列转移到局部变量,便于释放
for (SynchronizedObjectScope _(m_SyncObjRep_);;)
{
dqBuffersArray[1] = std::move(m_RespondDeque_);
m_RespondDeque_.clear();
break;
}
// 遍历并释放未处理的请求和响应,打印警告日志
static constexpr const char* acszUnprocessedErrors[2] = {
"Unprocessed DB request: dwScriptID=%u, nQueryID=%lld, strFunctionName=%s" ,
"Unprocessed DB respond: dwScriptID=%u, nQueryID=%lld, strFunctionName=%s"
};
for (int i = 0; i < arraysizeof(dqBuffersArray); ++i)
{
std::deque<IKG_Buffer*>& dqBuffers = dqBuffersArray[i];
for (;;)
{
auto tail = dqBuffers.begin();
auto endl = dqBuffers.end();
if (tail == endl)
{
break;
}
IKG_Buffer* pBuffer = *tail;
dqBuffers.erase(tail);
SQLITE_DATABASE_PROTOCOL* pProtocol = (SQLITE_DATABASE_PROTOCOL*)pBuffer->GetData();
// 注意:SharedString 在这里会被自动释放,因为 IUnknownAutodeleter 会调用 Release
IUnknownAutodeleter<SharedString> oSharedStringAutodeleter{ &pProtocol->strFunctionName };
LogOutput(KGLOG_WARNING, acszUnprocessedErrors[i], pProtocol->dwScriptID, pProtocol->nQueryID, pProtocol->strFunctionName.Get());
KG_COM_RELEASE(pBuffer);
}
}
}
/**
* @brief 从请求队列中弹出一个请求包(线程安全)
* @return 请求包指针,队列空时返回 NULL
*/
IKG_Buffer* SqliteDatabaseLuabackend::PopRequest() noexcept
{
IKG_Buffer* request = NULL;
for (SynchronizedObjectScope _(m_SyncObjReq_);;)
{
auto tail = m_RequestDeque_.begin();
auto endl = m_RequestDeque_.end();
if (tail == endl)
{
return NULL;
}
request = *tail;
m_RequestDeque_.erase(tail);
return request;
}
}
/**
* @brief 从响应队列中弹出一个响应包(线程安全)
* @return 响应包指针,队列空时返回 NULL
*/
IKG_Buffer* SqliteDatabaseLuabackend::PopRespond() noexcept
{
for (;;)
{
SynchronizedObjectScope _(m_SyncObjRep_);
auto tail = m_RespondDeque_.begin();
auto endl = m_RespondDeque_.end();
if (tail == endl)
{
return NULL;
}
IKG_Buffer* pBuffer = *tail;
m_RespondDeque_.erase(tail);
return pBuffer;
}
}
/**
* @brief 工作线程主循环:持续处理请求队列中的所有请求
* @return 0 表示处理了至少一个请求,10 表示队列空且线程应短暂休眠
*/
int SqliteDatabaseLuabackend::ExecuteAllRequests() noexcept
{
if (!IsAvailable())
{
return -1;
}
int events = 0;
for (;;)
{
IKG_Buffer* request = PopRequest();
if (NULL == request)
{
// 队列空,返回 0 或 10 供调度线程决定是否休眠
return (events > 0) ? 0 : 10;
}
++events;
ExecuteRequest(request);
KG_COM_RELEASE(request); // 释放请求包(引用计数减1)
}
}
/**
* @brief 获取当前线程 ID(用于调试/日志,兼容各平台)
* @return 线程 ID 的 int64 表示
*/
int64_t SqliteDatabaseLuabackend::GetCurrentThreadId() noexcept
{
auto stl_thread_id = std::this_thread::get_id();
// 将 std::thread::id 视为字节序列转换为整数,兼容各种实现
return *reinterpret_cast<const int64_t*>(&reinterpret_cast<const char&>(stl_thread_id));
}
/**
* @brief 判断字符串是否为空或 NULL
*/
static inline bool IsNullOrEmpty(const char* s) noexcept
{
return (NULL == s) || ('\x0' == *s);
}
/**
* @brief 将 Lua 发来的查询请求加入请求队列(线程安全)
* @param pRequestBuffer 包含 SQLITE_DATABASE_USER_QUERY_REQUEST 的缓冲区
* @return 成功返回 true,失败返回 false 并推送错误响应
*/
KG_BOOL SqliteDatabaseLuabackend::PushRequest(IKG_Buffer* pRequestBuffer) noexcept
{
ERRORS error = ERROR_RELEASE;
SQLITE_DATABASE_USER_QUERY_REQUEST* pRequest = NULL;
KGLOG_PROCESS_ERROR(!m_bDisposed_);
error = ERROR_NOT_AVAILABLE;
KGLOG_PROCESS_ERROR(m_DB_->IsAvailable());
error = ERROR_PARAM;
KGLOG_PROCESS_ERROR(NULL != pRequestBuffer);
BYTE* pbyData = (BYTE*)pRequestBuffer->GetData();
unsigned int uDataLen = pRequestBuffer->GetSize();
KGLOG_PROCESS_ERROR(NULL != pbyData && uDataLen >= sizeof(SQLITE_DATABASE_USER_QUERY_REQUEST));
pRequest = reinterpret_cast<SQLITE_DATABASE_USER_QUERY_REQUEST*>(pbyData);
const char* szSQL = reinterpret_cast<const char*>(pRequest->byData);
if (IsNullOrEmpty(szSQL))
{
error = ERROR_SQL;
goto Exit0;
}
pRequestBuffer->AddRef(); // 增加引用,因为队列会持有它
error = ERROR_NOERROR;
for (SynchronizedObjectScope _(m_SyncObjReq_);;)
{
m_RequestDeque_.emplace_back(pRequestBuffer);
break;
}
Exit0:
if (ERROR_NOERROR == error)
{
return true;
}
// 失败时立即构造错误响应并推送,避免请求丢失
PushRespond(NewRespondBuffer(error, pRequest, sizeof(SQLITE_DATABASE_USER_QUERY_RESPOND)));
return false;
}
/**
* @brief 初始化:打开数据库、注册 Lua 对象、创建工作线程
* @return 成功返回 true,失败返回 false
*/
KG_BOOL SqliteDatabaseLuabackend::Init() noexcept
{
if (m_bDisposed_)
{
return false;
}
if (IsAvailable())
{
return true;
}
KGLOG_PROCESS_ERROR(m_DB_->Open(SQLITE_DATABASE_DB_FILE_NMAE));
LogOutput(KGLOG_INFO, "Init: database opened successfully.\n");
KGLOG_PROCESS_ERROR(LuaRegisterObject()); // 将对象注册到 Lua 全局变量
LogOutput(KGLOG_INFO, "Init: Lua object registered.\n");
// 创建工作线程,执行 ExecuteAllRequests
int nRetCode = m_WorkThread_.Create(
[](void* pvParam) noexcept
{
SqliteDatabaseLuabackend* RCX = (SqliteDatabaseLuabackend*)pvParam;
return (NULL != RCX) ? RCX->ExecuteAllRequests() : -1;
}, this);
KGLOG_PROCESS_ERROR(nRetCode);
LogOutput(KGLOG_INFO, "Init: work thread created.\n");
return true;
Exit0:
LogOutput(KGLOG_ERR, "Init: failed.\n");
return false;
}
/**
* @brief 检查数据库是否可用(未释放且 SQLite 连接有效)
*/
bool SqliteDatabaseLuabackend::IsAvailable() noexcept
{
return (!m_bDisposed_) && m_DB_->IsAvailable();
}
/**
* @brief 主线程中调用,处理所有待处理的响应(激活 Lua 回调)
* @return 处理的响应数量,出错返回 -1
*/
int SqliteDatabaseLuabackend::ExecuteAllRespond() noexcept
{
if (!IsAvailable())
{
return -1;
}
int events = 0;
for (;;)
{
IKG_Buffer* pRespondBuffer = PopRespond();
if (NULL == pRespondBuffer)
{
break;
}
IUnknownAutodeleter<> oRespondAutodeleter{ pRespondBuffer }; // 确保最终释放
if (!ExecuteRespond(pRespondBuffer))
{
break;
}
++events;
}
return events;
}
/**
* @brief 执行一个响应包:调用 DatabaseInteAuxiliary 解析并回调 Lua
* @param pRespondBuffer 响应缓冲区
* @return 成功返回 true
*/
bool SqliteDatabaseLuabackend::ExecuteRespond(IKG_Buffer* pRespondBuffer) noexcept
{
BYTE* pbyData = (BYTE*)pRespondBuffer->GetData();
KGLOG_PROCESS_ERROR(NULL != pbyData);
size_t uDataLen = pRespondBuffer->GetSize();
KGLOG_PROCESS_ERROR(uDataLen >= sizeof(SQLITE_DATABASE_USER_QUERY_RESPOND));
SQLITE_DATABASE_USER_QUERY_RESPOND* pRespond = (SQLITE_DATABASE_USER_QUERY_RESPOND*)pbyData;
// 调用公共解析函数,将响应数据转换为 Lua 表并触发回调
DatabaseInteAuxiliary::ExecuteRespond<SQLITE_DATABASE_USER_QUERY_RESPOND>(pbyData,
uDataLen,
m_str_bResult,
m_str_nQueryID,
m_str_szError,
m_str_nFoundRows,
m_str_nAffectedRows,
m_str_nFiledsCount,
m_str_nRowCount,
m_str_Fileds,
m_str_Rows);
return true;
Exit0:
return false;
}
/**
* @brief 将响应包推入响应队列(线程安全)
* @param pRequestBuffer 响应缓冲区(已包含 SQLITE_DATABASE_USER_QUERY_RESPOND)
* @return 始终返回 true(失败时已经打印警告)
*/
KG_BOOL SqliteDatabaseLuabackend::PushRespond(IKG_Buffer* pRequestBuffer) noexcept
{
if (NULL == pRequestBuffer)
{
return false;
}
SQLITE_DATABASE_USER_QUERY_RESPOND* pRespond = (SQLITE_DATABASE_USER_QUERY_RESPOND*)pRequestBuffer->GetData();
SynchronizedObjectScope _(m_SyncObjRep_);
m_RespondDeque_.emplace_back(pRequestBuffer);
return true;
}
/**
* @brief 执行非查询 SQL(INSERT/UPDATE/DELETE 等)
* @param pRequest 原始请求结构
* @param szSQL 执行的 SQL 语句
* @return 成功返回 true,失败返回 false
*/
KG_BOOL SqliteDatabaseLuabackend::ExecuteNonQuery(SQLITE_DATABASE_USER_QUERY_REQUEST* pRequest, const char* szSQL) noexcept
{
int nonquery = m_DB_->ExecuteNonQuery(szSQL, (const char**)NULL, 0);
if (0 > nonquery)
{
PushRespond(NewRespondBuffer(ERROR_EXECUTE_NON_QUERY, pRequest, sizeof(SQLITE_DATABASE_USER_QUERY_RESPOND)));
return false;
}
IKG_Buffer* pResonpodBuffer = NewRespondBuffer(ERROR_NOERROR, pRequest, sizeof(SQLITE_DATABASE_USER_QUERY_RESPOND));
if (NULL == pResonpodBuffer)
{
return false;
}
SQLITE_DATABASE_USER_QUERY_RESPOND* pRespond = (SQLITE_DATABASE_USER_QUERY_RESPOND*)pResonpodBuffer->GetData();
pRespond->bResult = true;
pRespond->nAffectedRows = nonquery;
return PushRespond(pResonpodBuffer);
}
/**
* @brief 处理一个请求(分发到查询或非查询)
* @param pRequestBuffer 请求缓冲区
* @return 成功返回 true
*/
KG_BOOL SqliteDatabaseLuabackend::ExecuteRequest(IKG_Buffer* pRequestBuffer) noexcept
{
SQLITE_DATABASE_USER_QUERY_REQUEST* pRequest = (SQLITE_DATABASE_USER_QUERY_REQUEST*)pRequestBuffer->GetData();
const char* szSQL = reinterpret_cast<const char*>(pRequest->byData);
if (!m_DB_->IsAvailable())
{
PushRespond(NewRespondBuffer(ERROR_NOT_AVAILABLE, pRequest, sizeof(SQLITE_DATABASE_USER_QUERY_RESPOND)));
return false;
}
// 如果需要支持非查询,可在此处根据 SQL 类型调用 ExecuteNonQuery
if (ExecuteQuery(pRequest, szSQL))
{
return true;
}
LogOutput(KGLOG_ERR, "ERROR(false) <%s> at line %d in %s\n", szSQL, __LINE__, KG_FUNCTION);
return false;
}
/**
* @brief 执行查询 SQL(SELECT),将结果集转换为自定义二进制格式
* @param pRequest 原始请求结构
* @param szSQL SQL 语句
* @return 成功返回 true
*/
KG_BOOL SqliteDatabaseLuabackend::ExecuteQuery(SQLITE_DATABASE_USER_QUERY_REQUEST* pRequest, const char* szSQL) noexcept
{
struct ExecuteQueryContext
{
std::vector<uint8_t> vtPayload; // 二进制负载(字段名+行数据)
uint32_t ulRowNum = 0; // 返回行数
int32_t iColumnCount = 0; // 列数
} stExecuteQueryContext;
std::vector<std::string> vtParameters{}; // 参数列表,当前无参数
int nRowNum = m_DB_->ExecuteQuery(szSQL, vtParameters.begin(), vtParameters.end(),
[pRequest, &stExecuteQueryContext](SqliteDatabase*, sqlite3_stmt* stmt) noexcept -> bool
{
auto& vt = stExecuteQueryContext.vtPayload;
// 辅助 lambda:写入长度+数据
auto vt_write = [&vt](const void* pData, uint32_t ulDataSize) noexcept
{
vt.insert(vt.end(), reinterpret_cast<const uint8_t*>(&ulDataSize),
reinterpret_cast<const uint8_t*>(&ulDataSize) + sizeof(uint32_t));
if ((0 < ulDataSize) && (NULL != pData))
{
vt.insert(vt.end(), reinterpret_cast<const uint8_t*>(pData),
reinterpret_cast<const uint8_t*>(pData) + ulDataSize);
}
};
// 获取列数(仅第一次调用时)
int32_t iColumnCount = sqlite3_column_count(stmt);
if (0 > iColumnCount)
{
iColumnCount = 0;
}
if (0 == stExecuteQueryContext.iColumnCount)
{
stExecuteQueryContext.iColumnCount = iColumnCount;
// 如果请求了字段名,则直接写入字段名列表(每个字段名:长度+名称),不写入额外的列数前缀
// 这与原始 DataBase 的协议完全一致
if (pRequest->bGetFiledsName)
{
for (int i = 0; i < iColumnCount; ++i)
{
const char* szColumnName = sqlite3_column_name(stmt, i);
if (NULL == szColumnName)
{
szColumnName = "";
}
size_t ulColumnNameSize = strlen(szColumnName);
vt_write(szColumnName, static_cast<uint32_t>(ulColumnNameSize));
}
}
}
// 写入一行数据
char szTextBuf[127];
for (int i = 0; i < iColumnCount; ++i)
{
int iColumnType = sqlite3_column_type(stmt, i);
switch (iColumnType)
{
case SQLITE_INTEGER:
{
size_t ulColumnValueSize = snprintf(szTextBuf, sizeof(szTextBuf), "%lld", sqlite3_column_int64(stmt, i));
vt_write(szTextBuf, static_cast<uint32_t>(ulColumnValueSize));
break;
}
case SQLITE_FLOAT:
{
size_t ulColumnValueSize = snprintf(szTextBuf, sizeof(szTextBuf), "%lf", sqlite3_column_double(stmt, i));
vt_write(szTextBuf, static_cast<uint32_t>(ulColumnValueSize));
break;
}
case SQLITE_TEXT:
{
const char* szColumnText = (const char*)sqlite3_column_text(stmt, i);
if (NULL == szColumnText)
{
szColumnText = "";
}
vt_write(szColumnText, static_cast<uint32_t>(strlen(szColumnText)));
break;
}
case SQLITE_BLOB:
{
const void* pColumnBlobData = sqlite3_column_blob(stmt, i);
int iColumnValueSize = sqlite3_column_bytes(stmt, i);
if ((NULL != pColumnBlobData) && (0 < iColumnValueSize))
{
vt_write(pColumnBlobData, static_cast<uint32_t>(iColumnValueSize));
}
else
{
vt_write(NULL, 0);
}
break;
}
default: // SQLITE_NULL
vt_write(NULL, 0);
break;
}
}
++stExecuteQueryContext.ulRowNum;
return true; // 继续获取下一行
});
if (0 > nRowNum)
{
PushRespond(NewRespondBuffer(ERROR_EXECUTE_QUERY, pRequest, sizeof(SQLITE_DATABASE_USER_QUERY_RESPOND)));
return false;
}
size_t ulPayloadSize = stExecuteQueryContext.vtPayload.size();
size_t ulRespondSize = sizeof(SQLITE_DATABASE_USER_QUERY_RESPOND) + ulPayloadSize;
IKG_Buffer* pRespondBuffer = NewRespondBuffer(ERROR_NOERROR, pRequest, ulRespondSize);
if (NULL == pRespondBuffer)
{
return false;
}
SQLITE_DATABASE_USER_QUERY_RESPOND* pRespond = (SQLITE_DATABASE_USER_QUERY_RESPOND*)pRespondBuffer->GetData();
if (0 < ulPayloadSize)
{
memcpy(pRespond->byData, stExecuteQueryContext.vtPayload.data(), ulPayloadSize);
}
pRespond->bResult = true;
pRespond->nRowCount = stExecuteQueryContext.ulRowNum;
pRespond->nFiledsCount = stExecuteQueryContext.iColumnCount;
pRespond->uDataLen = ulPayloadSize;
if (pRequest->bGetAffectedRows)
{
pRespond->nAffectedRows = m_DB_->GetAffectedRows();
}
return PushRespond(pRespondBuffer);
}
/**
* @brief 创建响应缓冲区,并填充基本字段(请求信息、错误码等)
* @param eError 错误码(ERROR_NOERROR 表示成功)
* @param pRequest 原始请求
* @param uRespondSize 响应缓冲区总大小
* @return 新创建的 IKG_Buffer 对象,失败返回 NULL
*/
IKG_Buffer* SqliteDatabaseLuabackend::NewRespondBuffer(ERRORS eError, SQLITE_DATABASE_USER_QUERY_REQUEST* pRequest, size_t uRespondSize) noexcept
{
if ((NULL == pRequest) || (uRespondSize < sizeof(SQLITE_DATABASE_USER_QUERY_RESPOND)))
{
return NULL;
}
IKG_Buffer* piBuffer = CreateMemoryBuffer(uRespondSize);
if (NULL == piBuffer)
{
KGLOG_CHECK_ERROR(NULL != piBuffer);
return NULL;
}
SQLITE_DATABASE_USER_QUERY_RESPOND* pRespond = (SQLITE_DATABASE_USER_QUERY_RESPOND*)piBuffer->GetData();
pRespond->dwScriptID = pRequest->dwScriptID;
pRespond->nQueryID = pRequest->nQueryID;
// 危险操作:直接拷贝 SharedString 对象,必须配合引用计数管理。
// 接收方(ExecuteRespond)会通过 IUnknownAutodeleter 释放一次,此处也需保持引用计数平衡。
// 由于 pRequest 的 strFunctionName 已经 AddRef 过(在 LuaQuery 中),这里拷贝后两者共享同一份引用。
memcpy(&pRespond->strFunctionName, &pRequest->strFunctionName, sizeof(pRespond->strFunctionName));
pRespond->bResult = true;
pRespond->bSelectFoundRows = pRequest->bSelectFoundRows;
pRespond->bGetAffectedRows = pRequest->bGetAffectedRows;
pRespond->bGetFiledsName = pRequest->bGetFiledsName;
pRespond->bFiledsNameAsKey = pRequest->bFiledsNameAsKey;
pRespond->nFiledsCount = 0;
pRespond->nRowCount = 0;
pRespond->nAffectedRows = 0;
pRespond->nUserDataLuaRef = pRequest->nUserDataLuaRef;
pRespond->uDataLen = uRespondSize - sizeof(SQLITE_DATABASE_USER_QUERY_RESPOND);
return piBuffer;
}
/**
* @brief 将 SqliteDatabaseLuabackend 对象注册到 Lua 全局变量
* @return 成功返回 true
*/
bool SqliteDatabaseLuabackend::LuaRegisterObject() noexcept
{
int nResult = false;
lua_State* L = NULL;
KGLOG_PROCESS_ERROR(g_pLuaScriptEx);
L = g_pLuaScriptEx->GetLuaState();
KGLOG_PROCESS_ERROR(NULL != L);
// 注册类,Luna 会生成元表并绑定方法
Luna<SqliteDatabaseLuabackend>::Register(L);
// 将全局变量 sqlite3 设置为该对象
GetLuaObj(L);
m_str_sqlite3.SetGlobal(L);
nResult = true;
Exit0:
if (!nResult)
{
LogOutput(KGLOG_ERR, "LuaRegisterObject: registration failed.\n");
}
return nResult;
}
/**
* @brief Lua 可调用的查询接口,模拟 DataBase:Query
* @param L Lua 状态机
* @return Lua 返回值数量(1:nQueryID)
*/
int SqliteDatabaseLuabackend::LuaQuery(lua_State* L)
{
SharedString strFunctionName;
int nResult = 0;
int nTopIndex = lua_gettop(L);
bool bAddFuncNameRef = false;
IKG_Buffer* piBuffer = NULL;
const char* pcszSql = NULL;
KGLOG_PROCESS_ERROR(4 <= nTopIndex); // 至少需要 4 个参数: self, scriptData, funcName, sql
LuaScriptData* pLuaScriptData = Luna<LuaScriptData>::ToObject(L, 2);
KGLOG_PROCESS_ERROR(NULL != pLuaScriptData);
size_t uSqlLength = 0;
strFunctionName.Set(L, 3);
pcszSql = lua_tolstring(L, 4, &uSqlLength);
KGLOG_PROCESS_ERROR(NULL != pcszSql);
++uSqlLength; // 包含结尾 '\0'
piBuffer = CreateMemoryBuffer(uSqlLength + sizeof(SQLITE_DATABASE_USER_QUERY_REQUEST));
KGLOG_PROCESS_ERROR(NULL != piBuffer);
SQLITE_DATABASE_USER_QUERY_REQUEST* pRequest = (SQLITE_DATABASE_USER_QUERY_REQUEST*)piBuffer->GetData();
pRequest->dwScriptID = pLuaScriptData->m_dwID;
pRequest->nQueryID = m_nQueryID_++;
pRequest->nUserDataLuaRef = LUA_NOREF;
// 增加 SharedString 引用计数,以便后续拷贝使用
strFunctionName.AddRef();
bAddFuncNameRef = true;
memcpy(&pRequest->strFunctionName, &strFunctionName, sizeof(pRequest->strFunctionName));
// 解析 Lua 传入的可选参数
pRequest->bSelectFoundRows = false;
pRequest->bGetFiledsName = false;
pRequest->bFiledsNameAsKey = false;
if (4 < nTopIndex)
{
pRequest->bSelectFoundRows = lua_toboolean(L, 5);
if (5 < nTopIndex)
{
pRequest->bGetFiledsName = lua_toboolean(L, 6);
if (6 < nTopIndex)
{
pRequest->bFiledsNameAsKey = lua_toboolean(L, 7);
if (7 < nTopIndex)
{
lua_pushvalue(L, 8);
pRequest->nUserDataLuaRef = luaL_ref(L, LUA_REGISTRYINDEX);
}
}
}
}
pRequest->uDataLen = uSqlLength;
memcpy(pRequest->byData, pcszSql, uSqlLength);
// 将请求推入队列,队列内部会 AddRef,因此我们稍后需要释放本地的 piBuffer
int nRetCode = PushRequest(piBuffer);
KGLOG_PROCESS_ERROR(nRetCode);
// 成功:返回 nQueryID 给 Lua
lua_pushinteger(L, pRequest->nQueryID);
nResult = 1; // 返回值个数
Exit0:
// 失败时,需显示减少函数名引用计数
bool bExcept = 1 != nResult;
if (bExcept && bAddFuncNameRef)
{
strFunctionName.Release(); // luaS_release_cstr
}
KG_COM_RELEASE(piBuffer); // 释放本地缓冲区(队列已持有引用,释放后引用计数减1,仍由队列持有)
if (bExcept && (NULL != pcszSql))
{
LogOutput(KGLOG_ERR, "ERROR(false) <%s> at line %d in %s\n", pcszSql, __LINE__, KG_FUNCTION);
}
return nResult; // 失败返回 0(未向栈推值)
}
// 注册 Lua 方法
DEFINE_LUA_COLON_FUNC(SqliteDatabaseLuabackend, Query)
DEFINE_LUA_CLASS_BEGIN(SqliteDatabaseLuabackend)
REGISTER_LUA_COLON_FUNC(SqliteDatabaseLuabackend, Query)
DEFINE_LUA_CLASS_END(SqliteDatabaseLuabackend)
DatabaseInteAuxiliary.h
cpp
复制代码
#pragma once
#include "JString.h"
#include "KLuaScriptData.h"
/**
* @brief 数据库交互辅助类,提供通用的响应解析功能
* @note 该类与具体数据库实现无关,仅负责将二进制响应转换为 Lua 表并调用回调
*/
class DatabaseInteAuxiliary
{
public:
/**
* @brief 执行数据库响应:解析二进制数据,构建 Lua 表,并调用 Lua 函数
* @tparam T_DATABASE_USER_QUERY_RESPOND 响应结构体类型(必须包含 bResult、nFiledsCount、nRowCount、byData 等成员)
* @param pbyData 响应数据首地址
* @param uDataLen 数据总长度
* @param m_str_bResult Lua 字符串键 "bResult"
* @param m_str_nQueryID Lua 字符串键 "nQueryID"
* @param m_str_szError Lua 字符串键 "szError"
* @param m_str_nFoundRows Lua 字符串键 "nFoundRows"
* @param m_str_nAffectedRows Lua 字符串键 "nAffectedRows"
* @param m_str_nFiledsCount Lua 字符串键 "nFiledsCount"
* @param m_str_nRowCount Lua 字符串键 "nRowCount"
* @param m_str_Fileds Lua 字符串键 "Fileds"
* @param m_str_Rows Lua 字符串键 "Rows"
*/
template <typename T_DATABASE_USER_QUERY_RESPOND>
static void ExecuteRespond(
BYTE* pbyData,
size_t uDataLen,
SharedString m_str_bResult,
SharedString m_str_nQueryID,
SharedString m_str_szError,
SharedString m_str_nFoundRows,
SharedString m_str_nAffectedRows,
SharedString m_str_nFiledsCount,
SharedString m_str_nRowCount,
SharedString m_str_Fileds,
SharedString m_str_Rows) noexcept
{
int nRetCode = false;
int nTopIndex = 0;
const char* pszResult = NULL;
LuaScriptEx* pLuaScriptEx = g_pLuaScriptEx;
lua_State* L = NULL;
SharedStringArray strFileds;
T_DATABASE_USER_QUERY_RESPOND* pRespond = (T_DATABASE_USER_QUERY_RESPOND*)pbyData;
// 自动管理 SharedString 的引用,确保函数退出时释放
IUnknownAutodeleter<SharedString> oFunctionNameAutodeleter{ &pRespond->strFunctionName };
KGLOG_PROCESS_ERROR(NULL != pLuaScriptEx);
L = pLuaScriptEx->GetLuaState();
KGLOG_PROCESS_ERROR(NULL != L);
bool bSafeCallStarted = false; // 是否已成功进入安全调用
bool bSafeCallEnded = false; // 是否已结束安全调用
nRetCode = pLuaScriptEx->BeginSafeCallFunction(pRespond->dwScriptID, pRespond->strFunctionName, &nTopIndex);
if (nRetCode)
{
bSafeCallStarted = true;
}
else
{
goto Exit0;
}
if (pRespond->bResult)
{
// 创建结果表
lua_createtable(L, 0, 8);
lua_pushinteger(L, pRespond->nQueryID);
m_str_nQueryID.SetField(L, -2);
lua_pushboolean(L, pRespond->bResult);
m_str_bResult.SetField(L, -2);
lua_pushinteger(L, pRespond->nFiledsCount);
m_str_nFiledsCount.SetField(L, -2);
lua_pushinteger(L, pRespond->nRowCount);
m_str_nRowCount.SetField(L, -2);
if (pRespond->bSelectFoundRows)
{
lua_pushinteger(L, pRespond->nFoundRows);
m_str_nFoundRows.SetField(L, -2);
}
if (pRespond->bGetAffectedRows)
{
lua_pushinteger(L, pRespond->nAffectedRows);
m_str_nAffectedRows.SetField(L, -2);
}
pszResult = (const char*)pRespond->byData;
int nFields = pRespond->nFiledsCount;
bool bFiledsNameAsKey = pRespond->bFiledsNameAsKey;
strFileds.Reserve(nFields);
// 如果请求了字段名,则读取字段名列表(格式:每个字段名长度+数据,无额外列数前缀)
if (pRespond->bGetFiledsName)
{
for (int i = 0; i < nFields; ++i)
{
// 边界检查
if ((pszResult + sizeof(uint32_t)) > ((const char*)pRespond->byData + pRespond->uDataLen))
{
goto Exit0;
}
uint32_t uLen = *((uint32_t*)pszResult);
pszResult += sizeof(uint32_t);
if ((pszResult + uLen) > ((const char*)pRespond->byData + pRespond->uDataLen))
{
goto Exit0;
}
strFileds.Append(SharedString(pszResult, uLen));
pszResult += uLen;
}
}
// 构建 Rows 表(行列表)
lua_createtable(L, pRespond->nRowCount, 0);
for (int row = 1; row <= pRespond->nRowCount; ++row)
{
lua_createtable(L, 0, nFields);
for (int col = 1; col <= nFields; ++col)
{
// 读取每个单元格的长度和数据
if ((pszResult + sizeof(uint32_t)) > ((const char*)pRespond->byData + pRespond->uDataLen))
{
goto Exit0;
}
uint32_t uLen = *((uint32_t*)pszResult);
pszResult += sizeof(uint32_t);
if ((pszResult + uLen) > ((const char*)pRespond->byData + pRespond->uDataLen))
{
goto Exit0;
}
const char* pData = pszResult;
pszResult += uLen;
if (bFiledsNameAsKey && pRespond->bGetFiledsName)
{
// 使用字段名作为键(兼容原始 DataBase 的 bFiledsNameAsKey 模式)
strFileds[col - 1].SetField(L, -2);
lua_pushlstring(L, pData, uLen);
lua_settable(L, -3);
}
else
{
// 使用数字索引作为键
lua_pushlstring(L, pData, uLen);
lua_seti(L, -2, col);
}
}
lua_seti(L, -2, row);
}
m_str_Rows.SetField(L, -2);
// 如果请求了字段名且不以字段名作为键,则填充 Fileds 表(字段名数组)
if (pRespond->bGetFiledsName && (!bFiledsNameAsKey))
{
lua_createtable(L, nFields, 0);
for (int i = 1; i <= nFields; ++i)
{
lua_pushlstring(L, strFileds[i - 1].Get(), strFileds[i - 1].GetLength());
lua_seti(L, -2, i);
}
m_str_Fileds.SetField(L, -2);
}
}
else
{
// 错误响应:仅包含 bResult, nQueryID, szError
lua_createtable(L, 0, 3);
lua_pushinteger(L, pRespond->nQueryID);
m_str_nQueryID.SetField(L, -2);
lua_pushboolean(L, pRespond->bResult);
m_str_bResult.SetField(L, -2);
lua_pushlstring(L, (const char*)pRespond->byData, pRespond->uDataLen);
m_str_szError.SetField(L, -2);
}
pLuaScriptEx->AddParamCount(); // 增加参数计数(结果表)
if (LUA_NOREF != pRespond->nUserDataLuaRef)
{
pLuaScriptEx->PushRef(pRespond->nUserDataLuaRef); // 推送用户数据
}
nRetCode = pLuaScriptEx->SafeCallFunction(0, pRespond->strFunctionName);
pLuaScriptEx->EndSafeCallFunction(nTopIndex);
bSafeCallEnded = true; // 已主动结束
KGLOG_PROCESS_ERROR(nRetCode);
Exit0:
// 释放用户数据 Lua 引用
if (LUA_NOREF != pRespond->nUserDataLuaRef)
{
luaL_unref(L, LUA_REGISTRYINDEX, pRespond->nUserDataLuaRef);
}
// 如果安全调用已开始但尚未结束,则必须结束以恢复栈
if (bSafeCallStarted && (!bSafeCallEnded))
{
pLuaScriptEx->EndSafeCallFunction(nTopIndex);
}
}
};
TypeTraitsAuxiliary.h
cpp
复制代码
#pragma once
#include <iostream>
#include <type_traits>
#include "KGPublic.h"
template <typename T = IUnknown>
class IUnknownAutodeleter
{
template <typename, typename = void>
struct has_release : std::false_type {};
template <typename T>
struct has_release<T, std::void_t<decltype(&T::Release)>>
: std::is_member_function_pointer<decltype(&T::Release)> {};
static_assert(std::is_base_of<IUnknown, T>::value || has_release<T>::value,
"T must be derived from IUnknown or has Release function.");
public:
IUnknownAutodeleter() noexcept : p_(NULL) {}
IUnknownAutodeleter(T* p) noexcept : p_(p) {}
IUnknownAutodeleter(const IUnknownAutodeleter&) = delete;
IUnknownAutodeleter(IUnknownAutodeleter&& other) noexcept
: p_(other.p_)
{
other.p_ = NULL;
}
~IUnknownAutodeleter() noexcept
{
KG_COM_RELEASE(p_);
}
IUnknownAutodeleter& operator=(IUnknownAutodeleter&& other) noexcept
{
if (this != &other)
{
KG_COM_RELEASE(p_);
p_ = other.p_;
other.p_ = NULL;
}
return *this;
}
IUnknownAutodeleter& operator=(const IUnknownAutodeleter&) = delete;
T* get() const noexcept { return p_; }
// 交换(不释放旧指针)
T* exchange(T* p = NULL) noexcept
{
T* old = p_;
p_ = p;
return old;
}
// 重置并释放旧指针
void reset(T* p = NULL) noexcept
{
KG_COM_RELEASE(p_);
p_ = p;
}
private:
T* p_;
};
template <typename T, int N>
constexpr int arraysizeof(T(&)[N]) noexcept {
return N;
}