在调查一个奇怪的tcmalloc错误时,我和同事将错误追溯到一行代码:
soci::session db;
...
db << "INSERT INTO `public.tablename` (..., ..., textvalue) VALUES ('...', '...', :1);",
soci::use(std::to_string(someStruct.getUint32_t()) + "." + std::to_string(someStruct.getUint32_t()));
一切都很好,因为我们把它改为:
soci::session db;
...
std::string temp = std::to_string(someStruct.getUint32_t()) + "." + std::to_string(someStruct.getUint32_t());
db << "INSERT INTO `public.tablename` (..., ..., textvalue) VALUES ('...', '...', :1);",
soci::use(temp);
我们在第一个版本中得到了tcmalloc错误,因为第一个代码片段想要在参数列表中组装该字符串时分配大约1.8GB,而相应的系统上没有足够的可用内存。在我们外包了第二个代码片段中所示的字符串的组装之后,这个错误就不再发生了。显然,这个由2个整数和一个点构建的字符串不需要任何接近1.8GB的东西
如果有人能向我解释到底出了什么问题,我将不胜感激。我们怀疑这与一些黑社会诽谤魔法的模板推导有关,但不确定。
正如Jarod42所提到的,这个问题特别与soci::use有关。正如Soci Doku提到的:
如果用户提供的数据来自另一个临时变量,编译器可能会对它们进行排列,以便在语句有机会执行之前销毁用户数据,引用不再存在的对象。
下面的解释是一个糟糕的例子(这大概是我的第一个不起作用的代码例子(,然后是类似于我的第二个(正确的(代码例子的东西。
因此,最后,第一个代码部分是如何避免这种情况的示例。我写的错误来自访问未初始化的垃圾,因为在构建整个字符串之前,临时对象中前两个字符串中的一个已经被破坏。还有一件事在调试时让我很困惑:使用Clang构建并没有触发错误,而使用Gcc构建则触发了错误。文档也解释了这一点,因为问题与依赖编译器的优化有关。
如果使用soci::use,则应该在调用函数之前组装临时对象,然后只传递它们。无论如何,它可能更可读——但你必须知道它可能是错误的。