Basically, I want to find a template in a parameter pack that satisfies some runtime conditions. Intuitively, I just want to iterate over my instantiations of the parameter pack and find the first which satisfies a condition. My current simplified toy implementation to demonstrate what I mean:
Find the struct of X
and Y
which satisfies their test()
first.
struct X {
bool test(int i) {
flag = i > 10;
return flag;
}
bool flag;
std::string value = "X satisfied first";
};
struct Y {
bool test(int i) {
flag = i > 11;
return flag;
}
bool flag;
std::string value = "Y satiesfied first";
};
This struct finds the first struct of X
and Y
to satisfy the condition. In this example it increases an integer up to a given limit until one of the structs reports that its test()
was successful.
template <typename... Ts> struct FindFirst {
static std::string find_first(int limit) {
return find_first_satisfying(limit, Ts{}...);
}
static std::string find_first_satisfying(int limit, Ts... ts) {
int i = 0;
bool satisfied = false;
while (i < limit && !satisfied) {
satisfied = (ts.test(i) || ...);
i++;
}
return extract(ts...);
}
template <typename T, typename... OtherTs>
static std::string extract(T t, OtherTs... ts) {
if (t.flag) {
return t.value;
} else {
if constexpr (sizeof...(OtherTs) > 0) {
return extract(ts...);
} else {
return "Nobody satiesfied condition";
}
}
}
};
This implementation generates as many different extract()
functions with different signatures as there are templates in the pack. They get "recursively" called and result in a deep call stack (depends on the position of the satisfying struct) and large bytecode.
Is there a method to construct a loop (at compile-time) which tests each instantiation of the parameter pack and stops appropriately? Also, any other suggestions on how to simplify the whole construct?
I would wrote your code something like that:
template <typename ... Ts>
std::string find_first_satisfying(int limit, Ts... ts)
{
for (int i = 0; i != limit; ++i) {
std::string res;
bool found = false;
([&](){ if (ts.test(i)) { found = true; res = ts.value; } return found;}() || ...);
if (found) { return res; }
}
return "Nobody satisfied condition";
}