我试图有效地推断哪些条件导致程序忽略了if语句,而无需使用if语句序列来单独验证每个变量的相对完整性。
这可能吗?
bool state = false;
int x = 0;
int y = 1;
int z = 3;
if(x == 0 && y == 1 && z == 2) {
// Do something...
state == true;
}
if(state == false) {
std::cout << "I did not execute the if statement because the following
conditions were not met: " << std::endl;
/*Find a way to make the program output that z != 3 stopped the
conditional from running without directly using if(z != 2)*/
}
您可以在if
中的每个条件之间引入一个计数器作为"条件",以查看运算符&&
的短路计算何时禁止执行后一个条件:
int nrOfConditionFailing = 1;
if(x == 0 &&
nrOfConditionFailing++ && y == 1 &&
nrOfConditionFailing++ && z == 2) {
state = true;
}
if (!state) {
cout << "failed due to condition nr " << nrOfConditionFailing << endl;
}
如果要检查所有条件,则不能在单个if语句中执行此操作;操作员&&的短路评估将阻止在前一个条件之一评估为假时甚至检查/评估后一个条件。
但是,您可以执行这样的检查,作为表达式,该表达式在未满足的每个条件的无符号 int 中标记一个位:
int x = 1;
int y = 1;
int z = 3;
unsigned int c1 = !(x == 0);
unsigned int c2 = !(y == 1);
unsigned int c3 = !(z == 2);
unsigned int failures =
(c1 << 0)
| (c2 << 1)
| (c3 << 2);
if (failures) {
for(int i=0; i<3; i++) {
if (failures & (1 << i)) {
cout << "condition " << (i+1) << " failed." << endl;
}
}
}
else {
cout << "no failures." << endl;
}
如果这是您希望向最终用户显示的内容,而不仅仅是在调试时显示,如注释中所建议的那样,您可以为自己设计一个简单的数据结构。它将是一个条目的列表/向量/数组,每个条目都包含 a) 要比较的值,b) 要测试的值,以及可选的 c) 测试描述。
然后简单地迭代列表,并检查是否相等适用于所有列表。如果没有,您可以停止程序的流程并打印出说明。
更直接地回答您的问题:不,C++中没有任何内容可以让您检查先前陈述的结果。您在源代码中看到的语句和操作被编译,甚至可能无法在汇编指令中轻松识别。能够检查结果意味着数据必须存储在某个地方,这将极大地浪费内存和处理时间。这就是为什么你必须自己做这件事。
吗?
以你思考问题的方式是不可能的。您可以通过单独运行每个测试,存储结果,然后确定哪些测试false
来解决您的问题:
std::vector<std::tuple<std::string,bool> > tests = {
{"x==0",x==0}, // test name as a string followed by the actual test
{"y==1",y==1},
{"z==2",z==2}
};
if(!all_of(tests.begin(),tests.end(),[](std::tuple<std::string,bool> &t) { return std::get<1>(t); }))
{
std::cout << "The following tests failed: ";
//remove all tests that passed
tests.erase(
std::remove_if(tests.begin(),tests.end(),[](std::tuple<std::string,bool> &t) { return std::get<1>(t); }),
tests.end());
//This will only print out the tests that failed
std::transform(tests.begin(),tests.end(),std::ostream_iterator<std::string>(std::cout, " "),[](std::tuple<std::string,bool> &t) { return std::get<0>(t); });
std::cout << std::endl;
} else {
//what to do if all tests were true
}
这将评估所有测试(即,它不会使用&&
的短路)并打印所有失败的测试。您可能会将其包装到class
中,以使其更具通用性和用户友好性。
原始代码单独测试每个变量。&&
系列完全等同于一系列如果...否则声明。与另一个相比,一个没有什么低效的,使用一些棘手的解决方案来实现与简单代码相同的最终结果也没有什么"聪明"的。
我可能会写:
char const *reason = nullptr;
if(x != 0)
reason = "x failed";
else if (y != 1)
reason = "y failed";
else if (z != 2 )
reason = "z failed";
if ( reason )
std::cout << reason << 'n';
else
{
// success code here...
}
我通常会执行以下操作来确定一系列有效性检查是否有效并标记哪些检查失败。
unsigned long ulFlags = 0;
int x = 0;
int y = 1;
int z = 3;
ulFlags |= (x == 0) : 0 ? 0x0001; // if bit set then condition failed.
ulFlags |= (y == 1) : 0 ? 0x0002; // if bit set then condition failed.
ulFlags |= (z == 2) : 0 ? 0x0004; // if bit set then condition failed.
if(ulFlags == 0) {
// Do something since all conditions are met and valid ...
} else {
std::cout << "I did not execute if statement because: " << std::hex << ulFlags << std::endl;
/* Find a way to make the program output that z != 3 stopped the
conditional from running without directly using if(z != 2) */
}
这与其他一些答案的想法相同,但有一个模板来简化使用它的语法。将所有单个检查存储在一个std::array<bool, N>
和一个额外的 bool 中,以便能够重新检查完整的语句,而无需再次检查单个结果。
没有动态分配也是一个加分项。
#include <iostream>
#include <array>
#include <type_traits>
template <typename... N>
struct what_failed {
what_failed(N... n) : arr{n...}, status{(... && n)} {
static_assert(std::conjunction_v<std::is_same<N, bool>...>, "Only pass bools");
}
std::array<bool, sizeof...(N)> arr;
bool status;
operator bool() { return status; }
};
int main() {
auto check = what_failed(2 == 5, 2 < 5, 2 > 5, 1 == 1);
if (check)
std::cout << "Check: All true";
else {
std::cout << "Check: ";
for (auto c : check.arr)
std::cout << c << ' ';
}
return 0;
}
由于构造函数中的折叠表达式和模板推导,这需要 c++17,但这可以通过几个额外的帮助模板来解决 c++11。