【发布时间】:2020-08-05 17:18:52
【问题描述】:
我在虚幻引擎 4 的上下文中使用 sqlite3 C/C++ 合并时遇到问题...
我可以打开与数据库的连接,将数据写入其中,使用 DB Browser 验证这一点,但使用 sqlite3_prepare_v2 和 sqlite3_step 运行 SELECT 语句会出现意外行为。我正在检查返回值并没有看到任何错误代码,但是当我希望看到sqlite3_step 返回的SQLITE_ROW 时,我看到了SQLITE_DONE。如果我注销 sqlite3_expanded_sql 的结果,我会得到正确插入所有参数的预期语句,如果我将其复制并粘贴到 DB Browser 中,我会得到预期的结果。
要提取的数据特定于程序的每次运行,因此我知道我没有在 DB Browser 中看到旧的或不相关的数据,并且我知道我的“写入”函数也按预期运行。
特别奇怪的是,在注释掉所有对SQLPreparedStatementString 的引用(我在尝试调试问题时使用它)之后,代码的执行方式有所不同:我现在确实得到了一行(而不是预期的 4)。
这几乎让我怀疑存在内存问题,也许 sqlite3 和 Unreal 都在写入相同的内存部分而没有相互通信?我倾向于依赖 UE4 的内存管理,并且在 UE4 之外没有很多 C/C++ 经验,所以我对 malloc 之类的知识几乎仅限于“这里是龙”等一般感觉。我也知道sqlite3_prepare_v2 和sqlite3_step 是往往会进行大量内存分配的函数,但如果这是导致问题的原因,我很惊讶它很少发生,因为我成功地调用了它们几个每次游戏运行一千次(添加数据)。
我知道可以为 sqlite 设置编译时选项,为它预先分配一个内存块,以便它在运行时根本不使用 malloc,但我还没有完全弄清楚如何在在 UE4 中包含合并的上下文。
相关代码如下:
TArray<FName> UOzyBrainLogger::GetLoggedCivsForTurn(const int32 TurnNumber)
{
TArray<FName> rVal;
sqlite3_stmt *SQLStatement;
FString SQLCommand = "SELECT DISTINCT CivName FROM Decision WHERE (GameID = ? AND TurnNumber = ?) ;";
int rc;
rc = sqlite3_prepare_v2(DB, TCHAR_TO_UTF8(*SQLCommand), -1, &SQLStatement, NULL);
if (rc == SQLITE_OK)
{
//FString SQLPreparedStatementString;
UE_LOG(OzyBrainLogger, Log, TEXT("Getting Civs logged for turn %d in game %s"), TurnNumber, *GameID);
if(sqlite3_bind_text( SQLStatement, 1, TCHAR_TO_UTF8(*GameID), -1, SQLITE_STATIC) == SQLITE_OK &&
sqlite3_bind_int( SQLStatement, 2, TurnNumber) == SQLITE_OK)
{
//SQLPreparedStatementString = UTF8_TO_TCHAR(sqlite3_expanded_sql(SQLStatement));
//UE_LOG(OzyBrainLogger, Log, TEXT("Successfully created statement: %s"), *SQLPreparedStatementString);
rc = sqlite3_step(SQLStatement);
if (rc == SQLITE_DONE)
{
UE_LOG(OzyBrainLogger, Log, TEXT("No results found?"));
}
else if (rc != SQLITE_ROW)
{
UE_LOG(OzyBrainLogger, Log, TEXT("Return code was: %d"), rc);
}
while (rc == SQLITE_ROW)
{
FName CivName = FName(*FString(UTF8_TO_TCHAR((const char*) sqlite3_column_text(SQLStatement, 0))));
//UE_LOG(OzyBrainLogger, Log, TEXT("Returning %s for turn %d via statement: %s"), *CivName.ToString(), TurnNumber, *SQLPreparedStatementString);
UE_LOG(OzyBrainLogger, Log, TEXT("Returning %s for turn %d"), *CivName.ToString(), TurnNumber);
rVal.Add(CivName);
rc = sqlite3_step(SQLStatement);
}
}
else
{
UE_LOG(OzyBrainLogger, Warning, TEXT("%s"), UTF8_TO_TCHAR(sqlite3_errmsg(DB)));
}
}
else
{
UE_LOG(OzyBrainLogger, Warning, TEXT("%s"), UTF8_TO_TCHAR(sqlite3_errmsg(DB)));
}
sqlite3_finalize(SQLStatement);
return rVal;
}
我可能遗漏了一些非常明显的东西,但我不确定如何进一步调查以取得进展,因此任何关于探索途径的建议都会非常有帮助。
感觉有点失落!
【问题讨论】:
-
我建议两件事:(1)将代码重写为“向左推”:而不是if/then/else,更像:如果错误则返回。这往往使代码更容易理解和推理。 (2) 检查并对所有错误代码做出反应,例如有时代码会记录并继续运行,尽管出现了错误。另一个例子是调用 sqlite3_finalize(),缺少错误检查。
-
啊,关于
sqlite3_finalize的错误检查的要点。不过似乎什么都抓不到。我最初避免了“如果失败则返回”模式,以减少创建一个没有自行清理的返回分支的机会。正如你所描述的那样“向左移动”,我总是愿意听取关于最佳实践等的看法。 -
我正在尝试使用
sqlite3_config(SQLITE_CONFIG_HEAP, SQLiteDBMemoryHeap, 2^25, 64);,其中SQLiteDBMemoryHeap是通过FMemory::Malloc(2^25, 8);分配的,但是当我尝试打开数据库时会得到SQLITE_NOMEM返回码。数据库目前在磁盘上只有大约 57kb,所以我很惊讶它说它没有足够的内存。有什么东西阻止 sqlite 使用 UE4 函数分配的内存吗?我认为至少如果我使用 FMemory,它会确保至少一个人会知道另一个人...... -
2^25是 27 字节......这太小了,无法使用内存缓冲区。 -
我不熟悉你正在使用的这个框架,但是使用
SQLITE_STATIC和TCHAR_TO_UTF8()闻起来很有趣。
标签: c++ sqlite unreal-engine4