Some time ago I stumbled upon a very interesting (and for me new) technique named “Design by Introspection”, which is easily possible with the concept feature of C++20.
This technique can be illustrated by the following small code snippet:
auto value( auto v )
{
// unnamed concept in constexpr if-branch!
if constexpr ( requires { v.value; } ) { // if type of v has a .value use this branch.
return v.value;
} else {
return v;
}
}
First the parameter of the function value is of type auto
, so it will be set to a concrete type per invocation during compile time.
Now the interesting part is in line 4. If the code v.value
is valid and can be compiled with the concrete type of v
, the if-branch of line 4 will be used and the return value is v.value
. Otherwise, v
will be returned from the else branch.
Line 4 uses a requires clause in the constexpr-if, which is forming a constraint, which is checked during compile time. And because the function parameter is of type auto
, the function behaves like a template-function and SFINAE is present (SFINAE = Substitution Failure Is Not An Error).
If the type of v
has no valid v.value
, SFINAE is the reason for that it will not lead to a compile error and the else branch is used instead.
Of course, if no branch is compilable, a compile error will occur.
So, the example function above could be called like this:
long long number = 123;
auto res = value( number ); // the v branch is used.
struct X {
bool x = false;
std::string name = "foo";
long long value = 42;
} my_x;
auto res2 = value( my_x ); // the v.value branch is used.
I think with “Design by Introspection” a great way of generic programming is available in C++20. There are tons of use cases imaginable. Maybe more to this in some upcoming Blog posts.