SOCI/SQLite调用row.get<int64_t>时遇bad_cast或溢出问题求助
我有一个基于C++的成熟项目,使用SOCI操作SQLite数据库,需要将较大的整数读取到64位整数类成员中,据我所知需要使用row.get<int64_t>(myvar)这类方式。
在Windows系统中,该显式模板get方法会抛出std::bad_cast异常;在Linux系统中,会出现int32溢出,返回值为-1。若将列类型改为BIGINT则可正常工作,但我不想这么做——我的所有值都在64位范围内。
最小可复现示例(MCVE)
#include <soci/soci.h> #include <soci/sqlite3/soci-sqlite3.h> #include <iostream> int main() { try { // 创建内存中的SQLite数据库会话 soci::session sql(soci::sqlite3, ":memory:"); sql << "CREATE TABLE test_table (id INTEGER PRIMARY KEY, myInt INTEGER);"; sql << "INSERT INTO test_table (myInt) VALUES (9223372036854775807);"; // 此方式正常工作 int64_t retrievedValue1; sql << "SELECT myInt FROM test_table WHERE id = 1", soci::into(retrievedValue1); std::cout << "隐式读取: " << retrievedValue1 << std::endl; // Windows下抛出std::bad_cast,Linux下int32溢出返回-1 soci::row r; sql << "SELECT myInt FROM test_table WHERE id = 1", soci::into(r); int64_t retrievedValue2 = r.get<int64_t>("myInt"); std::cout << "显式64位读取: " << retrievedValue2 << std::endl; } catch (const std::exception& e) { std::cerr << "错误: " << e.what() << std::endl; } return 0; }
环境配置
Windows/Visual Studio(vcpkg.json)
{ "version": "1.0", "builtin-baseline": "c82f74667287d3dc386bce81e44964370c91a289", "dependencies": [ { "name": "soci", "features": [ "sqlite3" ] } ] }
Linux(CMakeLists.txt,需将SOCI置于/usr/local/)
cmake_minimum_required(VERSION 3.10) project(CppSQLiteSociExample) # 设置C++标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 设置SOCI路径 set(SOCI_INCLUDE_DIR "/usr/local/include/soci") set(SOCI_LIBRARY "/usr/local/lib/libsoci_sqlite3.so") # 查找SQLite3包 find_package(SQLite3 REQUIRED) # 检查SOCI头文件和库是否存在 if(NOT EXISTS "${SOCI_INCLUDE_DIR}/soci.h" OR NOT EXISTS "${SOCI_LIBRARY}") message(FATAL_ERROR "未找到SOCI库或头文件!") endif() # 添加源文件 add_executable(c c.cpp) # 包含SOCI头文件并链接库 target_include_directories(c PRIVATE ${SOCI_INCLUDE_DIR}) target_link_libraries(c PRIVATE SQLite::SQLite3 /usr/local/lib/libsoci_sqlite3.so /usr/local/lib/libsoci_core.so ) # 确保链接器能找到库 link_directories(/usr/local/lib)
更新
已在GitHub的SOCI项目提交bug报告(编号1185)
临时规避方案(无需修改数据库列类型)
使用隐式读取替代显式
row.get<int64_t>
如示例中retrievedValue1的读取方式,直接通过soci::into(retrievedValue1)绑定int64_t变量,这种方式SOCI能正确处理SQLiteINTEGER类型的64位值,不会出现溢出或bad_cast问题。手动转换row中的值
如果必须使用soci::row,可以先获取值的字符串表示,再转换为int64_t:std::string valStr = r.get<std::string>("myInt"); int64_t retrievedValue2 = std::stoll(valStr);这种方式绕过SOCI的类型映射问题,直接从SQLite的字符串表示转换,适用于所有64位范围内的整数。
修改SOCI的SQLite类型映射(进阶)
可以自定义SOCI对SQLiteINTEGER类型的处理,强制将其映射为int64_t而非32位整数。需要修改SOCI的SQLite后端代码,调整类型转换逻辑,确保从SQLite读取INTEGER时优先按64位整数处理。
根本解决方向
由于该问题已提交为SOCI的bug,后续可关注SOCI项目的修复进展,待官方补丁发布后升级库版本即可彻底解决。
内容的提问来源于stack exchange,提问作者Autumn

