Get deepest class in CRTP inheritance chain - c++

I would like to know how to solve the following problem (C++17):
suppose there are several template classes, inherited from each other in CRTP-like fashion (single inheritance only). For a given instantiated template base class, find the class that is furthest from it down the inheritance chain.
I first thought that is should be pretty easy, but was not able to accomplish this.
To simplify, suppose that every root and every intermediate class has using DerivedT = Derived in its public area.
Example:
template <class T>
struct GetDeepest {
using Type = ...;
};
template <class T>
struct A {
using DerivedT = T;
};
template <class T>
struct B : public A<B<T>> {
using DerivedT = T;
};
struct C : B<C> {
};
struct D : A<D> {
};
GetDeepest<A<D>>::Type == D;
GetDeepest<B<C>>::Type == C;
GetDeepest<A<B<C>>>::Type == C;
...
First implementation I've tried:
template <class T>
struct GetDeepest {
template <class Test, class = typename Test::DerivedT>
static std::true_type Helper(const Test&);
static std::false_type Helper(...);
using HelperType = decltype(Helper(std::declval<T>()));
using Type = std::conditional_t<std::is_same_v<std::true_type, HelperType>,
GetDeepest<typename T::DerivedT>::Type,
T>;
};
Second implementation I've tried:
template <class T>
struct HasNext {
template <class Test, class = typename Test::DerivedT>
static std::true_type Helper(const Test&);
static std::false_type Helper(...);
using HelperType = decltype(Helper(std::declval<T>()));
static const bool value = std::is_same_v<std::true_type, HelperType>;
};
template <class T>
auto GetDeepestHelper(const T& val) {
if constexpr(HasNext<T>::value) {
return GetDeepestHelper(std::declval<typename T::DerivedT>());
} else {
return val;
}
}
template <class T>
struct GetDeepest {
using Type = decltype(GetDeepestLevelHelper(std::declval<T>()));
};
None of them compile.
First one -- because of incomplete type of GetDeepest<T> in statement using Type = ..., second because of recursive call of a function with auto as a return type.
Is it even possible to implement GetDeepest<T> class with such properties? Now I'm very curious, even if it might be not the best way to accomplish what I want.
Thanks!

I'm not sure if I fully understand the question so feel free to ask me in comments.
But I think this should work:
#include <type_traits>
template<typename T>
struct GetDeepest
{
using Type = T;
};
template<template<typename> class DT, typename T>
struct GetDeepest<DT<T>>
{
using Type = typename GetDeepest<T>::Type;
};
template <class T>
struct A {
using DerivedT = T;
};
template <class T>
struct B : public A<B<T>> {
using DerivedT = T;
};
struct C : B<C> {
};
struct D : A<D> {
};
int main()
{
static_assert(std::is_same<GetDeepest<A<D>>::Type, D>::value);
static_assert(std::is_same<GetDeepest<B<C>>::Type, C>::value);
static_assert(std::is_same<GetDeepest<A<B<C>>>::Type, C>::value);
}

Related

Resolving CRTP initialization order

I have some CRTP dependency that I am not sure how to resolve. Ideally I want to put as many things as possible in the base class, like functions, so I do not have to redefine those for every class that inherits those. This seems to cause an issue with the initialization order, where result_type is dependent on the type that is yet to be initialized. Here is an example: https://godbolt.org/z/YpfcPB
And here is the code:
template<typename T>
struct CRTP_Derived;
template<typename Derived>
struct CRTP
{
using result_type = typename Derived::result_type;
};
template<typename T>
struct CRTP_Derived : public CRTP<CRTP_Derived<T>>
{
using result_type = T;
};
int main()
{
CRTP_Derived<int> a;
return 0;
}
I've also used a separate traits type for issues like this. You can reduce the needed boilerplate a little if you make the traits a second template parameter, instead of requiring users to specialize a separate template:
template<typename Derived, typename Traits>
struct CRTP
{
using result_type = typename Traits::result_type;
};
template<typename T>
struct CRTP_Derived_Traits
{
using result_type = T;
};
template<typename T>
struct CRTP_Derived : public CRTP<CRTP_Derived<T>, CRTP_Derived_Traits<T>>
{
};
int main()
{
CRTP_Derived<int> a;
return 0;
}
A workaround I found is taking out the typedef in a separate class, still I would be glad to see other solutions.
https://godbolt.org/z/a7NCE2
template<typename T>
struct CRTP_Derived;
template<typename Derived>
struct traits;
template<typename T>
struct traits<CRTP_Derived<T>>
{
using result_type = T;
};
template<typename Derived>
struct CRTP
{
using result_type = typename traits<Derived>::result_type;
};
template<typename T>
struct CRTP_Derived : public CRTP<CRTP_Derived<T>>
{
using result_type = T;
};
int main()
{
CRTP_Derived<int> a;
return 0;
}

Parent template argument deduction in nested class constructor

I am trying to write the "promotion" constructor of a nested class that can deduce the parent class template. It works fine for the parent class, but not in the nested class. Here is a code example.
template <class T>
struct potato {
struct baked {
template <class O>
baked(const typename potato<O>::baked& p)
: something(static_cast<T>(p.something)) {
}
baked() = default;
T something;
};
template <class O>
potato(const potato<O>& p)
: mybaked(p.mybaked) {
}
potato() = default;
baked mybaked;
};
int main(int, char**) {
potato<int> potato1;
potato<short> potato2(potato1);
}
Is this legal?
Various compilers output various errors. Clang has the most readable in my mind. It states :
candidate template ignored: couldn't infer template argument 'O'
https://godbolt.org/z/y_IZiE
So I'm guessing either I've messed up the signature, or this isn't a c++ supported feature.
I don't know of any way to deduce the template argument T for a baked's parent potato<T>. You can know T using decltype(p.something) but that doesn't seem to help solve the problem with calling the constructor. One workaround is to change baked's constructor to take any O and assume it has a something :
struct baked {
template <class O>
baked(const O & p) : something(static_cast<T>(p.something))
{ }
baked() = default;
T something;
};
This will work but it is less type-safe than your original code seems to intend. One workaround for that problem could be to introduce a static_assert that checks that O is actually a potato<U>::baked :
#include <type_traits>
template <class T>
struct potato {
struct baked {
template <class O>
baked(const O & p) : something(static_cast<T>(p.something))
{
using t_parent = potato<decltype(p.something)>;
static_assert(std::is_same<O, typename t_parent::baked>::value, "Not a baked potato!");
}
baked() = default;
T something;
};
template <class O>
potato(const potato<O>& p)
: mybaked(p.mybaked) {
}
potato() = default;
baked mybaked;
};
This should compile fine for the intended usage but fail with "Not a baked potato!" if you try to pass anything else with a something. This would fail :
struct foo {
int something = 0;
};
int main(int, char**) {
foo bar;
potato<int>::baked baz(bar); // Error: Not a baked potato!
}
As state by the compiler O is not deducible from const typename potato<O>::baked& (on left side of ::).
You have several workarounds:
Move baked outside parent and make it template:
// Possibly in namespace details
template <typename T>
struct baked_impl {
template <class O>
baked_impl(const typename baked_impl<O>& p)
: something(static_cast<T>(p.something)) {
}
baked_impl() = default;
T something;
};
template <class T>
struct potato {
using baked = baked_impl<T>;
// ...
};
Add parent info in baked and use SFINAE:
template <class T> struct potato;
// traits for SFINAE
template <class T> struct is_potato : std::false_type {};
template <class T> struct is_potato<potato<T>> : std::true_type {};
template <class T>
struct potato {
using value_type = T;
struct baked {
using parent = potato;
template <class O, std::enable_if_t<is_potato<typename O::parent>::value, int> = 0>
baked(const O& p)
: something(static_cast<typename O::parent::value_type>(p.something)) {
}
baked() = default;
T something;
};
// ...
};

Partial specialized template class (for container class type) is not called

I am still working on this problem I posted some hours before:
[How to overload/specialize template class function to handle arithmetic types and a container-class
I tried to implement this solution. It compiles but the object is created with the DerivedClass-Constructor instead of the partial specialized template class DerivedClass< Eigen::ArrayBase >
Do you have an Idea where I made a (or some) misstakes?
template <typename T> class BaseClass
{
protected:
T mem;
public:
BaseClass(T arg) : mem(arg){};
};
template <typename T> class DerivedClass : public BaseClass<T>
{
public:
DerivedClass(T arg): BaseClass<T>(arg){};
};
template <typename T>
class DerivedClass<Eigen::ArrayBase<T> >
: public DerivedClass<Eigen::ArrayBase<T> >
{
public:
DerivedClass(Eigen::ArrayBase<T> arg):BaseClass<Eigen::ArrayBase<T> >(arg){};
};
int main
{
...
Eigen::Array3d arg = Array3d::Random(3);
DerivedClass<Eigen::Array3d> o(arg);
....
}
template<template<class...>class Z>
struct template_instance_test {
static std::false_type test(...);
template<class...Ts>
static std::true_type test( Z<Ts...> const* );
template<class X>
using tester = decltype(test( std::declval<X*>() ) );
};
template<template<class...>class Z, class T>
using is_derived_from_template = typename template_instance_test<Z>::template tester<T>;
we can now ask if something is an instance of a particular template, or derived from an instance of a particular template.
template<class X>
struct Base {};
template<class X>
struct Derived:Base<X> {};
template<class T>
struct Storage {
T data;
};
template<class T, class=void>
struct Instance:Storage<T> {
enum {is_special = false};
};
template<class T>
struct Instance<T, std::enable_if_t< is_derived_from_template<Base, T>{} > >:
Storage<T> {
enum { is_special = true };
};
int main() {
Instance<int> i; (void)i;
static_assert(!Instance<int>::is_special);
Instance<Derived<int>> j; (void)j;
static_assert(is_derived_from_template<Base, Base<int>>{});
static_assert(is_derived_from_template<Base, Derived<int>>{});
static_assert(Instance<Derived<int>>::is_special);
}
and we are done. Live example.
Your code should works if Eigen::Array3d is an alias (through using or typedef) of Eigen::ArrayBase<T> for some T.
But I suppose that Eigen::Array3d inherit from Eigen::ArrayBase<T>. So isn't a ``Eigen::ArrayBase`, so doesn't matches the partial specialization, so matches the main template.
If you want a specialization that intercept all classes derived from Eigen::ArrayBase, a possible solution is add an additional template parameter with a default value and activate the specialization only it T derive from some Eigen::ArrayBase.
Something as follows (caution: code not tested)
constexpr std::false_type isArray (...);
template <typename T>
constexpr std::true_type isArray (Eigen::ArrayBase<T> const *);
template <typename T, typename = std::true_type>
class DerivedClass : public BaseClass<T>
{
public:
DerivedClass(T arg): BaseClass<T>(arg){};
};
template <typename T>
class DerivedClass<T, decltype(isArray(std::declval<T*>())>
: public DerivedClass<T>
{
public:
DerivedClass (T arg) : BaseClass<T>(arg){};
};

Is there an idiom/design pattern for restricting templates?

How do one restrict the typename T to specific type?
Consider this:
template <typename T>
struct Worker {
// T can only be certain type allowing specific functionality.
// i.e T needs to be a product of some interface, support some functions, say T::toString(), T::print(), T::get().
// Do something with T
};
This is what I usually end up doing:
struct WorkableType {
std::string toString() { return ""; }
int get() { return 0;}
}
struct WorkabelTypeA : WorkableType {
std::string toString() { return "A"; }
int get() { return 1;}
};
//Similarly
struct WorkableTypeB : WorkableType;
And use static assert and std::is_base_of:
template <typename T>
struct Worker {
static_assert(std::is_base_of<WorkableType, T>::value, "Needs workable type");
// Do something with T
};
Is there any other design pattern, a more C++ way to restrict accidental instantiation of bad typed templates?
Edit: Seems like this would be better solved with C++ Concepts when it becomes the standard. Until then i guess, static_assert is probably more cleaner and verbose than enable_if.
You could use SFINAE and template specialisation:
// type trait that evaluates always to false to use in the primary template
template<typename ... T> struct always_false : std::false_type { };
// primary template
template<typename T, typename Enable = void>
struct Worker {
static_assert(always_false<T, Enable>::value, "Needs workable type");
};
// specialisation
template<typename T>
struct Worker<T, std::enable_if_t<std::is_base_of<WorkableType, T>::value>> {
...
};
You can create traits and check that in your class, So, no need of inheritance. For example:
template <typename T>
using toString_t = decltype(std::declval<T>().toString());
template <typename T>
using get_t = decltype(std::declval<T>().get());
// Use C++17, but can be done in C++11
template <typename T>
using has_toString = std::is_detected<toString_t, T>;
template <typename T>
using has_get = std::is_detected<get_t, T>;
And then
template <typename T>
struct Worker {
static_assert(has_toString<T>::value, "T should have toString");
static_assert(has_get<T>::value, "T should have get");
};
Demo
If you know exactly which types you want to allow, then a traits class is a succinct way to do it:
#include <utility>
// by default nothing is workable
template<class T>
struct is_workable : std::false_type
{
};
template <typename T>
struct Worker {
static_assert(is_workable<T>(), "not a workable type");
// T can only be certain type allowing specific functionality.
// i.e T needs to be a product of some interface, support some functions, say T::toString(), T::print(), T::get().
// Do something with T
};
// define a worker
struct A {};
// make it workable
template<> struct is_workable<A> : std::true_type {};
// define another worker but forget to make it workable
struct B {};
int main()
{
Worker<A> wa{};
// Worker<B> wb{}; // compile error - not workable
};
You can use specializations as it follows:
#include<string>
struct WorkableType {
std::string toString() { return ""; }
int get() { return 0; }
};
struct A {};
struct B {};
template<typename> struct Worker;
template<> struct Worker<A>: WorkableType {};
int main() {
Worker<A> wa;
// this won't compile
// Worker<B> wb;
}

Several levels of nested templates. How do I get this to work?

I'm doing some template meta programming and I have a situation like this, first I have a few classes like :-
template <typename Q>
struct Object {
public:
Q data;
};
template <typename P>
class CircleObject : public Object<const typename P::Circle> {
};
template <typename P>
class SquareObject : public Object<const typename P::Circle> {
};
template <typename P>
class Storage {
public:
typedef CircleObject<P> MyCircle;
typedef SquareObject<P> MySquare;
};
Now, I'm trying to define some traits of these objects as such :-
template <typename P>
struct CircleTraits<Storage<P> > {
template <typename otype>
struct IsCircle {
static const bool VALUE = false;
};
};
template <typename P>
struct CircleTraits<Storage<P> >::IsCircle<Storage<P>::MyCirlce> {
static const bool VALUE = true;
};
However, this is incorrect (compile errors). I've tried a trial and error method of putting typenames and template parameters everywhere but without a firm understanding of template specializations, I'm not really able to fix this. Can someone help here?
What I'm hoping to achieve in a later function is something like :-
typedef Storage<RedObjects> RedStorage;
template <typename SpecializedStorage>
class Processor {
typedef CircleTraits<typename SpecializedStorage> MyCircleTraits;
template <typename ObjectType>
void foo(ObjectType& data);
};
template <typename SpecializedStorage>
template <typename ObjectType>
void foo(ObjectType& data) {
if (MyCircleTraits::template IsCircle<ObjectType>::VALUE) {
// do something about the damn circles
}
}
I think you cant do it like that, You probably should use SFINAE to solve something like this:
//C++11 version
template<typename T>
struct IsCircle
{
private:
template<typename Z>
constexpr static bool _is(typename Z::MyCirlce*) //if Z dont have `MyCirlce` then this function is removed
{
return true;
}
template<typename Z>
constexpr static bool _is(...) //fallback function
{
return false;
}
public:
static const bool VALUE = _is<T>(nullptr);
};
//C++98 version
template<typename T>
struct IsCircle
{
private:
struct a { char a; }; //size ~1
struct b { char a[8]; }; //size ~8
template<typename Z>
static b _is(typename Z::MyCirlce*);
template<typename Z>
static a _is(...);
public:
static const bool VALUE = sizeof(_is<T>(0)) == sizeof(b);
};