I am interested in evaluating a boolean function, F
that takes in N number of boolean arguments. N corresponds to the number of elements in a tuple, Levels
. I want to perform a compile-time computation that ensures that F
always evaluates to false. The base case is F(false, false, ..., false)
, where each argument passed in is false
. However, for each element in Levels
, if it meets a certain condition, I want to also evaluate F
with true
input corresponding to the index of that element in Levels
.
For example, say Levels = (A, B, C, D). And A and D meet the condition. We want to check that the output is always false for F evaluated:
F(false, false, false, false) must == false
F(true, false, false, false) must == false
F(false, false, false, true) must == false
If any F
evaluations returns true
, then this should raise a static_assert
error. Some pseudocode for the structure would be:
template <class F, class... Levels>
class Coiterate<F, std::tuple<Levels...>>
{
public:
explicit inline Coiterate(F f, Levels&... levels)
{
// run the validation with respect to F and Levels
compile_time_validate<F, Levels>();
}
}
I am sure a pattern like this has a name in C++ template-meta-programming, but I had a hard time googling on SO. If anyone has any links to other posts that address this concept, then that would also be helpful. I would like to implement the compile_time_check
function. My attempt at some pseudocode that would solve this at runtime is:
// I want to perform the compile-time check here F and Levels, something along these lines
// first evaluate with all inputs `false`
static_assert(F(false, false, ..., false) == false);
for idx, level in enumerate(Levels):
if constexpr meet_condition(level):
input_arg = true;
static_assert(F(false, ..., input_arg, false, ..., false) == false);
else:
// do nothing because we only need to evaluate it with `true` at index idx if the condition is met.
[Update - details on function F] The function F
is a boolean function that basically defines a set of conjunctions/disjunctions always.
For example:
// this is a conjunction (A \cap B) over two input Levels
auto fn1 = [](std::tuple<bool, bool> t) constexpr { return std::get<0>(t) && std::get<1>(t); };
// this is a combined disjunction and conjunction over four input levels
auto fn2 = [](std::tuple<bool, bool, bool, bool> t) constexpr
{ (return (std::get<0>(t) && std::get<2>(t) && std::get<1>(t)) || std::get<3>(t)); };
Let's take F
as fn2
, then say the inputs correspond to Levels = (A, B, C, D)
and A and D meet some condition, say A::condition_met
, then I would want to check the following:
1. static_assert(F(false, false, false, false) == false)
2. static_assert(F(true, false, false, false) == false)
// note that in this case, the static_assert would fail because
// there is an "OR" operation with a `true` value.
3. static_assert(F(false, false, false, true) == false)
It is assumed that condition_met
is defined during compile-time for each level in Levels
, so this is just a matter of expanding and evaluating F
for a series of different boolean input arguments.
If I understand your requirements correctly, this would work:
template <typename F, typename... Levels>
void coiterate(F f, Levels const&...) {
static_assert(f(std::make_tuple((Levels{}, false)...)) == false);
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
constexpr auto make_single_level_tuple = []<std::size_t I>(std::integral_constant<std::size_t, I>) constexpr {
constexpr auto levels_tuple = std::make_tuple(Levels{}...);
constexpr auto false_tuple = std::make_tuple((Levels{}, false)...);
return std::make_tuple(std::get<Is>(I == Is ? levels_tuple : false_tuple)...);
};
static_assert(((f(make_single_level_tuple(std::integral_constant<std::size_t, Is>{})) == false) && ...));
}(std::index_sequence_for<Levels...>{});
}
levels_tuple
is a tuple of "condition met" for each level. false_tuple
is a tuple of false of the same size. make_single_level_tuple
makes a tuple of false except at index I
, where it takes from levels_tuple
instead. The rest is just a matter of calling f
with the correct tuples, and asserting the conjunction of the results.