我知道这可能相当令人困惑,但我正在使用Boost测试框架编写单元测试。我试图简单地增加一个变量来测试特定的回调是否按预期执行。
这是测试代码摘录:
uint32_t nSuccessCallbacks = 0;
uint32_t nFailureCallbacks = 0;
auto successCallback = [&nSuccessCallbacks, this] {
std::cout << "Running success callback...\n";
++nSuccessCallbacks;
};
auto failureCallback = [&nFailureCallbacks, this] (const std::string& str) {
std::cout << "Error code: " << str << "\n";
std::cout << "Running failure callback...\n";
++nFailureCallbacks;
};
dest.advertise(rr, successCallback, failureCallback);
advertise
的定义:
void
NfdRibReadvertiseDestination::advertise(nfd::rib::ReadvertisedRoute& rr,
std::function<void()> successCb,
std::function<void(const std::string&)> failureCb)
{
m_controller.start<ndn::nfd::RibRegisterCommand>(
ControlParameters().setName(rr.getPrefix()).setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT).setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT),
[&] (const ControlParameters& cp) { successCb(); },
[&] (const ControlResponse& cr) { failureCb(cr.getText()); });
}
仅供参考,dest
在测试夹具中定义。
我无法修改nSuccessCallbacks
。每当调用回调时,我们都会正确地执行它,但是在回调退出并且我们在dest.advertise()
之后的代码中之后,值仍为0.我们成功到达回调lambda,但gdb报告没有范围内的这种变量。我已经尝试了this
中所有捕获,特定捕获,混合以及删除等的所有合理组合。我不知道捕获子句错误捕获变量的原因。我最好的猜测是,由于lambda被传递到另一个lambda,第一个的capture子句会丢失吗?
编辑:当接口对象接收数据时执行回调。我们在测试的后期嘲笑它,并且不重要,所以我选择不包括它。
答案 0 :(得分:1)
使用水晶球,你的lambda是在许多范围中的一个之后运行的,你可以通过引用捕获一些东西(advertise
或你的&#34;测试代码exerpt&#34;)已经退出。因此,by-reference捕获变量已经离开了范围,而UB结果,你会看到垃圾。
您发布的代码实际上并没有运行lambda,所以发布的代码显然没有包含垃圾的lambda的问题。
作为一般规则,如果您的lambda或其任何副本可能比当前范围更长,则永远不要通过引用捕获。通过复制捕获,或者(在C ++ 14中)通过移动捕获。这条规则有例外,但它们很容易出现错误。
作为第二条规则,如果您的lambda超过当前范围,则显式捕获您捕获的所有内容。没有默认捕获。这样你就不会被被捕获的东西感到惊讶,这些东西的生命周期(或指向生命周期)不够长,比如this
或某些指针或某些东西。
至少这样做:
[successCb] (const ControlParameters& cp) { successCb(); },
[failureCb] (const ControlResponse& cr) { failureCb(cr.getText()); }
然后确保这个,而不是这个的副本:
auto successCallback = [&nSuccessCallbacks, this] {
std::cout << "Running success callback...\n";
++nSuccessCallbacks;
};
不会超出其范围。如果是,请更改捕获方式。
答案 1 :(得分:1)
start
调用将在给定名称的情况下启动异步线程来处理请求。不幸的是,这意味着lambda中通过引用捕获的变量在被访问时已经被释放。
C ++只允许您按副本捕获(并且您没有生命周期问题)或通过引用捕获,但您必须确保lambda不会超过引用的变量。
正确解决&#34;向上的问题&#34;问题(一个lambda捕获一个变量 - 而不是一个值 - 来自一个上下文并超过上下文)需要一个垃圾收集器(一个堆栈不够),C ++不能提供一个。
解决方案(如果你注意避免循环)是按值shared_ptr
捕获所需的可变共享状态。