There is multiple answers that solve the problem (as proposed above) but what bother me is that they either require too much code (not reusable) or a dependency (boost.hana et al. ).
I propose a solution that works for me and is reusable. This is C++14 (not C++11 !):
template <typename Bool>
struct CompileTimeBranch {
public:
// v2
template <typename CallIfTrue>
static constexpr CompileTimeBranch
True(CallIfTrue&& to_call_if_true) {
IfTrue(Bool{}, std::forward<CallIfTrue>(to_call_if_true));
return CompileTimeBranch{};
}
template <typename CallIfFalse>
static constexpr CompileTimeBranch
False(CallIfFalse&& to_call_if_false) {
IfFalse(Bool{}, std::forward<CallIfFalse>(to_call_if_false));
return CompileTimeBranch{};
}
// v1
template <typename CallIfTrue, typename CallIfFalse>
static constexpr void
Then(CallIfTrue&& to_call_if_true,
CallIfFalse&& to_call_if_false) {
// v2 Re-use True/False
True(std::forward<CallIfTrue>(to_call_if_true));
False(std::forward<CallIfFalse>(to_call_if_false));
// v1 Less verbose but less versatile
// Branch(Bool{},
// std::forward<CallIfTrue>(to_call_if_true),
// std::forward<CallIfFalse>(to_call_if_false));
}
constexpr operator bool() const {
return Bool::value;
}
protected:
// v2
template <typename CallIfTrue>
static constexpr void
IfTrue(std::true_type,
CallIfTrue&& to_call_if_true) {
to_call_if_true(Bool{});
}
template <typename CallIfTrue>
static constexpr void
IfTrue(std::false_type,
CallIfTrue&&) {}
template <typename CallIfFalse>
static constexpr void
IfFalse(std::true_type,
CallIfFalse&&) {}
template <typename CallIfFalse>
static constexpr void
IfFalse(std::false_type,
CallIfFalse&& to_call_if_false) {
to_call_if_false(Bool{});
}
// v1
// template <typename CallIfTrue, typename CallIfFalse>
// static constexpr void
// Branch(std::true_type, CallIfTrue&& to_call_if_true, CallIfFalse&&) {
// to_call_if_true(Bool{});
// }
// template <typename CallIfTrue, typename CallIfFalse>
// static constexpr void
// Branch(std::false_type, CallIfTrue&&, CallIfFalse&& to_call_if_false) {
// to_call_if_false(Bool{});
// }
};
template <bool kBranchLiteral>
using LiteralCompileTimeBranch = CompileTimeBranch<std::integral_constant<bool, kBranchLiteral>>;
Usable like so:
template <typename T> void AssertIfSmall() {
static_assert(sizeof(T) <= 4, "");
}
template <typename T> void test0() {
if (sizeof(T) <= 4) {
AssertIfSmall<T>(); // Wont compile
std::printf("Small stuff\n");
} else {
std::printf("Big stuff\n");
}
}
template <typename T> void test1() {
if constexpr (sizeof(T) <= 4) { // Expected C++17 behavior
AssertIfSmall<T>();
std::printf("Small stuff\n");
} else {
std::printf("Big stuff\n");
}
}
template <typename T> void test2() {
using Branch0 = LiteralCompileTimeBranch<sizeof(T) >= 1>;
using Branch = LiteralCompileTimeBranch<sizeof(T) <= 4 && Branch0{}>;
Branch::Then(
[](auto) {
AssertIfSmall<T>();
std::printf("Small stuff\n");
},
[](auto) { std::printf("Big stuff\n"); });
}
template <typename T> void test3() {
using Branch = CompileTimeBranch<std::integral_constant<bool, sizeof(T) <= 4>>;
Branch::True([](auto) { AssertIfSmall<T>();
std::printf("Small stuff\n"); });
Branch::False([](auto) { std::printf("Big stuff\n"); });
}
template <typename T> void test4() {
using Branch = CompileTimeBranch<std::integral_constant<bool, sizeof(T) <= 4>>;
Branch::True([](auto) {
AssertIfSmall<T>();
std::printf("Small stuff\n");
}).False([](auto) {
std::printf("Big stuff\n");
});
}
Available here as a playground: https://godbolt.org/z/G9snzWqEn