I'm programming some classes into which I inject dependencies via class
template parameters.
I some cases, the dependency classes have, or can have, static constexpr members that declare some of their specific characteristics. In the case of the example below, classes implementing a Renderer
"concept" can define a static constexpr bool variable y_axis_bottom_up
to indicate that they expect vertical coordinates that grow in upward direction.
I could of course make it a requirement that all Renderer implementations provide this boolean member, but I'd prefer to define a default value in the query expression.
Because I need to use that information to parameterize further templates, that query expression must be constexpr.
I thought I had the solution when I found this stackoverflow answer , but when I tried to apply it to my needs, I failed.
Here's the - non-working - minimal test code I came up with. The second output line should say "1", but both outputs are "0".
Any help would be appreciated.
#include <iostream>
#include <type_traits>
template<class Config, typename = void>
struct vertical_axis_bottom_up
{
static constexpr bool value = false;
};
template<class Config>
struct vertical_axis_bottom_up<Config, decltype(Config::Renderer::y_axis_bottom_up)>
{
static constexpr bool value = Config::Renderer::y_axis_bottom_up;
};
struct Dummy_renderer {};
struct Config1
{
using Renderer = Dummy_renderer;
};
struct Dummy_renderer_2
{
static constexpr bool y_axis_bottom_up = true;
};
struct Config2
{
using Renderer = Dummy_renderer_2;
};
int main(int /*argc*/, char * /*argv*/[])
{
std::cout << "Default value for Config::Renderer::y_axis_bottom_up : " << vertical_axis_bottom_up<Config1>::value << std::endl;
std::cout << "Explicit value for Config::Renderer::y_axis_bottom_up: " << vertical_axis_bottom_up<Config2>::value << std::endl;
std::cout << "Press RETURN to terminate" << std::endl;
char ch; std::cin >> std::noskipws >> ch;
return -1;
}
This is done extensively in the standard library, allocator_traits
is a good example. Here's the basic idea:
#include <iostream>
#include <type_traits>
template <class T, class = void>
struct MyTrait
{
static constexpr bool foo = false;
};
template <class T>
struct MyTrait<T, std::enable_if_t<std::is_same<const bool, decltype(T::foo)>::value>>
{
static constexpr bool foo = T::foo;
};
struct Class1 {
};
struct Class2 {
static constexpr bool foo = true;
};
int main()
{
std::cerr << MyTrait<Class1>::foo;
std::cerr << MyTrait<Class2>::foo;
}
Basically, this is using a trick very similar to the void_t
trick (google if you are not familiar). If the templated class provides a member of the correct type, then it picks up that member. If not, it falls back to false. So when you access the foo
trait through MyTrait
, you are guaranteed for it be present.
Working example: http://coliru.stacked-crooked.com/a/d73f3674d0c53e53
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.