Using default Traits apart from the specializations - c++

What I want to do is to create a generic traits class with the default logic and then write code to specialize each specific case with only the things that are different from the generic case. My goal is to remove code duplication and avoid writing unnecessary code.
I'll give you an example:
int genericFunction(); // defined somewhere else
int specialFunction(); // defined somewhere else
template<int id>
struct IdTraits
{
using MyType = int;
using AnotherType = double;
static constexpr auto&& f = genericFunction;
};
template<>
struct IdTraits<1>
{
// Using MyType and AnotherType of IdTraits generic case [how?]
static constexpr auto&& f = specialFunction;
};
template<>
struct IdTraits<2>
{
// Using MyType and f of IdTraits generic case [how?]
using AnotherType = char;
};
template<int id, class Traits = IdTraits<id>>
struct General
{
void foo(int arg)
{
Traits::MyType myType;
Traits::AnotherType anotherType;
Traits::f(arg);
// Do stuff with myType and anotherType
}
};
Do you think is theoretically possible to do something like this?

You can have a second Trait to use instead that will do this work. Its purpose will be to check the presence of each element inside the current IdTrait<id> and set the default type/function if not.
Using current experimental detection for the two types, and member getF for the function:
template<int id>
struct MyIdTraits {
template <typename T> using MyType_t = typename T::MyType;
using MyType = std::experimental::detected_or_t<int, MyType_t, IdTraits<id>>;
template <typename T> using AnotherType_t = typename T::AnotherType;
using AnotherType = std::experimental::detected_or_t<double, AnotherType_t, IdTraits<id>>;
template <typename T, typename = decltype(T::f)>
static constexpr auto getF(int) { return T::f; }
template <typename T>
static constexpr auto getF(unsigned) { return genericFunction; }
static constexpr auto&& f = getF<IdTraits<id>>(42);
};
Then replace your trait by this one:
template<int id, class Traits = MyIdTraits<id>>
struct General { ... };
Demo

Yes. Put your generic case in a base class:
namespace details
{
struct IdTraits_generic
{
using MyType = int;
using AnotherType = double;
static constexpr auto&& f = genericFunction;
};
}
template<int id> struct IdTraits : details::IdTraits_generic
{
};
template<> struct IdTraits<1> : details::IdTraits_generic
{
static constexpr auto&& f = specialFunction;
};
template<> struct IdTraits<2> : details::IdTraits_generic
{
using AnotherType = char;
};

Related

Extracting the underlying type in the template

I am new to C++20. The intention here is to have a template class which has value whose type would be the underlying type of T that's passed in.
So in case of T being:
std::optional<char>, it's char value
int, it's just int value.
Is there any better way to extract the types than through struct TypeExtract? More or a generic solution in C++20 perhaps? Given if the class could take more than just std::optional<int> or just a primitive type?
Can the condition in foo be improved specially with the way val is initialized?
template<typename T>
constexpr bool is_optional = false;
template<typename T>
constexpr bool is_optional<std::optional<T>> = true;
template<typename T>
struct TypeExtract
{
using type = T;
};
template<typename T>
struct TypeExtract<std::optional<T>>
{
using type = T;
};
template <typename T>
concept is_integral = std::is_integral_v<typename TypeExtract<T>::type>;
template <is_integral T>
class A
{
using Type = typename TypeExtract<T>::type;
Type val;
void foo(T value)
{
if constexpr (is_optional<T>)
{
val = *value;
}
else
{
val = value;
}
}
};
int main()
{
A<char> a1;
A<std::optional<int>> a2;
// A<double> a3; // fails
}
It looks like you're trying to extract first template parameter from a class template and keep on unwinding templates until you get to a non-template type. In that case you could make a type trait that is specialized for types instantiated from templates:
// primary template
template<class T, class...>
struct type {
using value_type = T;
};
// specialization for template instantiated types
template<template<class, class...> class T, class F, class... Rest>
struct type<T<F, Rest...>> {
using value_type = typename type<F>::value_type;
};
// helper alias
template<class... Ts>
using type_t = typename type<Ts...>::value_type;
You could then use it like so:
int main() {
type_t<char> a1;
type_t<std::optional<int>> a2;
type_t<double, int> a3;
static_assert(std::is_same_v<decltype(a1), char>);
static_assert(std::is_same_v<decltype(a2), int>);
static_assert(std::is_same_v<decltype(a3), double>);
}
There is no good or bad here, it's a matter of style and convention, but personally I would get rid of if constexpr and take advantage of trailing requires for the sake of reducing function's cyclomatic complexity. On the other hand, that add some boilerplate. The choice is yours.
Not much can be done about type extraction, though I would probably use a templated base and import its member(s) instead of importing the type into the class. Not a big deal, but it feels more idiomatic to me.
As for concepts, I'd probably use more library-provided ones in the place of type traits.
Side note: consider using assert, .value() or similar function when assigning from the optional to ensure it's not empty.
All in all, I'd probably write your code somewhat this way:
#include <concepts>
#include <type_traits>
#include <optional>
template<typename T>
concept StdOptional = std::same_as<std::optional<typename T::value_type>, T>;
template<typename T>
concept OptionalIntegral = StdOptional<T> and std::integral<typename T::value_type>;
template<typename T>
concept OptionalOrOptionalIntegral = std::integral<T> or OptionalIntegral<T>;
template<typename>
struct ABase;
template<std::integral T>
struct ABase<T>
{
T value;
};
template<OptionalIntegral T>
struct ABase<T>
{
typename T::value_type value;
};
template<OptionalOrOptionalIntegral T>
class A : ABase<T>
{
using ABase<T>::value;
public:
void setValue(T val) requires(std::integral<T>)
{
value = val;
}
void setValue(T val) requires(OptionalIntegral<T>)
{
value = val.value();
}
};
Demo: https://godbolt.org/z/dzvr9xbGr

ignore template parameter in type matching

This is my current program:
#include <type_traits>
template<class Feature, class... FeatureList>
struct has_feature {
static constexpr bool value = (std::is_same_v<Feature, FeatureList> || ...);
};
template<class Feature, class... FeatureList>
inline constexpr bool has_feature_v = has_feature<Feature, FeatureList...>::value;
template<class Feature, class ...FeatureList>
static constexpr bool isConfiguredWith() {
return has_feature_v<Feature, FeatureList...>;
}
struct CanWalk {
};
struct CanNotWalk {
};
template<class... FeatureList>
struct Robot {
static auto configure() {
return Robot<WalkFeature < FeatureList...>>
();
}
private:
template<typename ...Config>
using WalkFeature =
std::conditional_t<isConfiguredWith<CanWalk, Config...>(), CanWalk, CanNotWalk>;
};
int main() {
Robot<CanWalk> robot_A = Robot<CanWalk>::configure();
Robot<CanNotWalk> robot_B = Robot<>::configure();
return 0;
}
Basically, Robot is a struct that can be configured with many other struct (they are used as token here), then Robot<T...>::configure() trim and organize the template parameters passed into to Robot. In the end we have:
Robot<CanWalk> robot_A = Robot<CanWalk, CanWalk>::configure();
Robot<CanNotWalk> robot_B = Robot<>::configure();
Although duplicated features CanWalk are passed into as template parameters, they are all deleted when constructing Robot via function configure().
This is working well until I add a template parameter to feature CanWalk:
template <int Speed>
struct CanWalk {
};
Now everything breaks since CanWalk is no longer a legit type, it needs a template parameter.
For error error: use of class template 'CanWalk' requires template arguments occured from:
template<typename ...Config>
using WalkFeature =
std::conditional_t<isConfiguredWith<CanWalk>(), CanWalk, CanNotWalk>;
How do I fix it?
How can I define them as:
Robot<CanWalk<5>> robot_A = Robot<CanWalk<5>>::configure();
Robot<CanNotWalk> robot_B = Robot<>::configure();
?
live code: https://godbolt.org/z/4K9TxohWq
I see three challenges here:
you need to extract a specific trait (CanWalk), regardless of its parameters, from a parameter pack
you need to ignore extra copies of the same trait
you need to set a default trait (CanNotWalk) if that trait is not present
I don't know a better way to do this than recursively:
// definition
template<template<auto...> class FeatureType, class DefaultFeature, class... FeatureList>
struct find_feature;
// next trait matches, stop recursing and return it
template<template<auto...> class FeatureType, class DefaultFeature, auto... Param, class... RemainingFeatures>
struct find_feature<FeatureType, DefaultFeature, FeatureType<Param...>, RemainingFeatures...> {
using type = FeatureType<Param...>;
};
// next trait does not match, skip by inheriting from rest of list
template<template<auto...> class FeatureType, class DefaultFeature, class FirstFeature, class... RemainingFeatures>
struct find_feature<FeatureType, DefaultFeature, FirstFeature, RemainingFeatures...>
: find_feature<FeatureType, DefaultFeature, RemainingFeatures...> { };
// no more traits, return default trait
template<template<auto...> class FeatureType, class DefaultFeature>
struct find_feature<FeatureType, DefaultFeature> {
using type = DefaultFeature;
};
// alias
template<template<auto...> class FeatureType, class... FeatureList>
using find_feature_t = typename find_feature<FeatureType, FeatureList...>::type;
this should work for any feature that is a class template and any number of non-type parameters.
usage:
template <int speed>
struct CanWalk {};
struct CanNotWalk {};
template<class... FeatureList>
struct Robot {
static auto configure() {
return Robot<WalkFeature < FeatureList...>>
();
}
private:
template<typename ...Config>
using WalkFeature = find_feature_t<CanWalk, CanNotWalk, Config...>;
};
auto robot_A = Robot<CanWalk<42>, CanWalk<25>>::configure();
static_assert(std::is_same_v<decltype(robot_A), Robot<CanWalk<42>>>, "");
auto robot_B = Robot<>::configure();
static_assert(std::is_same_v<decltype(robot_B), Robot<CanNotWalk>>, "");
Removing some genericity, you might do something like:
template <typename T> struct Tag { using type = T; };
template <std::size_t> struct CanWalk {};
struct CanNotWalk {};
template <std::size_t N> Tag<CanWalk<N>> has_walk(Tag<CanWalk<N>>); // No impl
template <typename T> Tag<CanNotWalk> has_walk(Tag<T>); // No impl
template<class... FeatureList>
struct Robot {
static auto configure() {
struct all_features : Tag<FeatureList>..., Tag<struct Empty> {};
return Robot<typename decltype(has_walk(all_features{}))::type>{};
}
};
int main() {
[[maybe_unused]] Robot<CanWalk<42>> robot_A = Robot<CanWalk<42>>::configure();
[[maybe_unused]] Robot<CanNotWalk> robot_B = Robot<>::configure();
}
Demo
That simple way doesn't handle duplicates, but can with some extra work (idea is to have struct all_features : Tag<Ts, Is>... with std::index_sequence)

How to detect whether there is a specific PRIVATE member variable in class?

This question is based on this post.
Goal: I would like to know if a class has the member variable x. I would like to receive true regardless whether or not this variable is private, public or protected.
Approach: You can get the information if a class has a member variable using the following code:
template <typename T, typename = int>
struct HasX : std::false_type { };
template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
Use it with
if constexpr (HasX<my_class>::value) {
// do stuff with x
} else {
// ...
}
The above code does not work in this case
struct my_class {
private:
int x;
};
How can I make this work? I would like HasX<my_class>::value to be true.
Ideas:
Use a friend class which has access to T::x. This does not seem to work. Check out this live example.
Well... not sure about correctness and limits of this solution... but...
If you define an helper struct with an x element accessible
struct check_x_helper
{ int x; };
you can write a template struct that inherit from both check_x_helper and the class you want to see if contain a x member
template <typename T>
struct check_x : public T, check_x_helper
Inside check_x you can declare (declare only: are used inside a decltype()) as follows
template <typename U = check_x, typename = decltype(U::x)>
static constexpr std::false_type check (int);
static constexpr std::true_type check (long);
Observe the first one, the template one: when the checked class (T) contains an x member, the decltype(U::x) is ambiguous because x is inherited from both T and check_x_helper, so this function is SFINAE discarded.
On contrary, when T doesn't contains an x member, there isn't an ambiguity, the decltype(U::x) is the type of check_x_helper::x (int) and the first check() function remain enabled.
Now you need something as
using type = decltype(check(0));
static constexpr auto value = type::value;
to call check(0) (the int parameter express the preference to the template version) and save the detected value in a static constexpr variable.
The following is a full compiling example
#include <iostream>
#include <utility>
class foo
{ int x; };
struct bar
{ };
struct check_x_helper
{ int x; };
template <typename T>
struct check_x : public T, check_x_helper
{
template <typename U = check_x, typename = decltype(U::x)>
static constexpr std::false_type check (int);
static constexpr std::true_type check (long);
using type = decltype(check(0));
static constexpr auto value = type::value;
};
int main()
{
std::cout << check_x<foo>::value << std::endl;
std::cout << check_x<bar>::value << std::endl;
}
Drawback of this solution: decltype(U::x) fail (ambiguity) also when T declare x as a method or as a using type. So given
class foo
{ int x () { return 0;} ; };
or
class foo
{ using x = int; };
from check_x<foo>::value you obtain 1.
The following seems to work. Please tell me if it needs improvement. Live example.
class Haser {
public:
template <typename T, typename = int>
static constexpr bool HasX = false;
template <typename T>
static constexpr bool HasX<T, decltype((void) T::x, 0)> = true;
};
struct A {
private:
int x;
friend Haser;
};
Haser::HasX<A> is true.

How to avoid "template parameters not deducible in partial specialization"

I would like to have access to a nested class using templates, and can't figure out how to do it: A sample code:
template <typename T> class mything {
typedef unsigned key;
T data;
public:
template <typename subT> class mysubthing { typedef subT value_type; };
using subthing = mysubthing<T>;
using subthing_ro = mysubthing<const T>;
};
template<typename T> struct X; // a container for all value_types
#ifdef MAKE_IT_FAIL
// this should automatically set the X<mything<T>::mysubthing<subT>
template<typename T,typename subT> struct X<typename mything<T>::template mysubthing<subT>> {
using value_type = subT;
};
#endif
typedef mything<int> intthing;
#ifndef MAKE_IT_FAIL
template<> struct X<mything<int>::subthing> { using value_type = int; };
template<> struct X<mything<int>::subthing_ro> { using value_type = const int; };
#endif
int main(void) {
intthing t;
X<intthing::subthing>::value_type data = 1; // a data object
X<intthing::subthing_ro>::value_type data_ro = 1; // read-only data object
return 0;
}
This compiles without -DMAKE_IT_FAIL, but of course it completely misses the point about templates, since what I wanted was entered manually. How can I make it work with -DMAKE_IT_FAIL?
You can't specialize like that:
template<typename T,typename subT>
struct X<typename mything<T>::template mysubthing<subT>> {
since deduction of T from types like outer<T>::anything_after is impossible (not supported) in C++.
You don't really need specialization in this general case at all. Just define the default X, and then only specialize the other cases:
template <typename T> class mything {
typedef unsigned key;
T data;
public:
template <typename subT> struct mysubthing
{
typedef subT value_type;
};
using subthing = mysubthing<T>;
using subthing_ro = mysubthing<const T>;
};
template<typename T> struct X
{
using value_type = typename T::value_type;
};
// this should automatically set the X<mything<T>::mysubthing<subT>
typedef mything<int> intthing;
template<> struct X<mything<int>::subthing> { using value_type = int; };
template<> struct X<mything<int>::subthing_ro> { using value_type = const int; };
int main(void) {
intthing t;
X<intthing::subthing>::value_type data = 1; // a data object
X<intthing::subthing_ro>::value_type data_ro = 1; // read-only data object
return 0;
}
Addendum
According to one of the comments, X is actually std::iterator_traits, which is already defined. In that case, the only way around it is to define the iterator class outside the mything class:
template <typename T, typename subT>
class mything_iterator {
typedef subT value_type;
};
template <typename T> class mything {
typedef unsigned key;
T data;
public:
using iterator = mything_iterator<T, T>;
using const_iterator = mything_iterator<T, const T>;
};
namespace std {
template<typename T, class subT>
class iterator_traits<mything_iterator<T, subT>>{
using value_type =typename mything_iterator<T, subT>::value_type;
// etc...
};
template<> struct iterator_traits<mything<int>::iterator>
{ using value_type = int; };
template<> struct iterator_traits<mything<int>::const_iterator>
{ using value_type = int; };
}

Templates arguments not fulfulling all requirements

This is possible:
struct A {
//void f(); < not declared in struct A
};
template<typename T>
struct Wrapper {
T t;
void call_f() { t.f(); }
};
int main() {
Wrapper<A> w;
}
This compiled fine, as long as w.call_f() is not called. w.call_f() can not be instantiated because A::f does not exist.
I'm having a situation with such a wrapper template that gets used with different T types, which do not always implement all parts of the interface. (Mainly to avoid code duplication).
This does not work:
struct A {
//using i_type = int; < not declared/defined
};
template<typename T>
struct Wrapper {
using its_i_type = typename T::i_type;
// compile error, even if `i_type` gets never used
};
int main() {
Wrapper<A> w;
}
neither does this:
struct A {
//using i_type = int; < not declared/defined
};
template<typename T>
struct Wrapper {
typename T::i_type call_f() { return 0; }
// does not compile, even if `call_f()` is never instantiated
};
int main() {
Wrapper<A> w;
}
Is there a good way to handle these situations, without a lot of code duplication (like a specialization for Wrapper, etc.)?
You can defer the type deduction of its_i_type. Basically, you create a simple wrapper that you must go through.
To extend it to other types you require, (I wanted to suggest type_traits-like solution, but since you don't want specializations) you could define all the types you need:
template<typename T>
struct Wrapper {
private:
template<typename U> struct i_typper { using type = typename U::i_type; };
template<typename U> struct k_typper { using type = typename U::k_type; };
template<typename U> struct p_typper { using type = typename U::p_type; };
public:
using i_trait = i_typper<T>;
using k_trait = k_typper<T>;
using p_trait = p_typper<T>;
};
Example:
struct A { using i_type = int; };
struct B { using i_type = int; using k_type = float; };
int main() {
Wrapper<A> w; //Works now.
Wrapper<A>::i_trait::type mk1; //Works
Wrapper<A>::k_trait::type mk2; //Fails, not defined
Wrapper<B>::i_trait::type mk3; //Works
Wrapper<B>::k_trait::type mk4; //Works
}
For the case of:
template<typename T>
struct Wrapper {
typename T::i_type call_f() { return 0; }
// does not compile, even if `call_f()` is never instantiated
};
You have few options here:
make that function a member function template
Use some form of type_traits mechanism, which will still involve specialization
Go the way of abstracting common Wrapper stuff in a base class WrapperBase;
For the first option, you'll have to modify it a bit to further defer deduction
template<typename T>
struct Wrapper {
private:
template<typename U, typename> struct i_typper { using type = typename U::i_type; };
template<typename U, typename> struct k_typper { using type = typename U::k_type; };
template<typename U, typename> struct p_typper { using type = typename U::p_type; };
public:
using i_trait = i_typper<T, void>;
using k_trait = k_typper<T, void>;
using p_trait = p_typper<T, void>;
template<typename U = void>
typename k_typper<T, U>::type call_f() { return 0; }
};
I'll leave the second option as an exercise: (it may end up being something like:
template<typename T>
struct wrapper_traits {
....
};
template<>
struct wrapper_traits<A>{
using ....
};
template<typename T>
struct Wrapper {
....
public:
using i_trait = wrapper_traits<T>;
using k_trait = wrapper_traits<T>;
using p_trait = wrapper_traits<T>;
};
Jarod's answer is simpler. But this will work if you do not have access to std::experimental, or your company code policy forbids you...
With std::experimental::is_detected, you may do
template<typename T>
using i_type_t = typename T::i_type;
template<typename T>
struct Wrapper {
using its_i_type = typename std::experimental::detected_t<i_type_t, T>;
// would be T::i_type or std::experimental::nonesuch
};
Or to better handle case, something like:
template<typename T, bool = std::experimental::is_detected<i_type_t, T>::value>
struct WrapperWithIType {
// Empty for false case.
};
template<typename T>
struct WrapperWithIType<T, true> {
using its_i_type = i_type_t<T>;
its_i_type call_f() { return 0; }
};
and then
template<typename T>
struct Wrapper : WrapperWithIType<T> {
// Common stuff
};