I'm trying to check if a class that I'm templating is inheriting from another templated class, but I can't find the correct way to do it.
Right now I have the following:
#include <iostream>
template <typename MemberType, typename InterfaceType>
class Combination : public InterfaceType
{
public:
Combination();
virtual ~Combination();
private:
MemberType* pointer;
};
class MyInterface {};
class MyMember {};
class MyCombination : public Combination<MyMember, MyInterface>
{
};
int main()
{
static_assert(std::is_base_of_v<Combination<MyMember, MyInterface>, MyCombination>);
std::cout << "hello";
}
Which is working fine, but in the place this template is placed, I don't have access to the template parameters definitions, and this should be quite generic. What I'd like is to test for the MyCombination class to inherit from a generic Combination without taking into account the template arguments, something like this:
static_assert(std::is_base_of_v<Combination, MyCombination>);
or
static_assert(std::is_base_of_v<Combination<whatever, whatever>, MyCombination>);
Would you know how to check for this?
In this case I can't use boost libraries or other external libraries.
Thanks!
EDIT: I don't have access to modify the Combination class, and what I can change is the assert and the My* classes.
This can easily be done with the C++20 concepts. Note that it requires a derived class to have exactly one public instantiated base class.
template <typename, typename InterfaceType>
class Combination : public InterfaceType {};
class MyInterface {};
class MyMember {};
class MyCombination : public Combination<MyMember, MyInterface> {};
template<class Derived, template<class...> class Base>
concept derived_from_template = requires (Derived& d) {
[]<typename... Ts>(Base<Ts...>&) {}(d);
};
static_assert(derived_from_template<MyCombination, Combination>);
Demo
The equivalent C++17 alternative would be
#include <type_traits>
template<template<class...> class Base, typename... Ts>
void test(Base<Ts...>&);
template<template<class...> class, class, class = void>
constexpr bool is_template_base_of = false;
template<template<class...> class Base, class Derived>
constexpr bool is_template_base_of<Base, Derived,
std::void_t<decltype(test<Base>(std::declval<Derived&>()))>> = true;
static_assert(is_template_base_of<Combination, MyCombination>);
Related
I am looking for a c++ template which finds the common parent of a set of given classes.
For example
class Animal { ... };
class Mammal : public Animal { ... };
class Fish : public Animal { ... };
class Cat : public Mammal { ... };
class Dog : public Mammal { ... };
std::unique_ptr<common_ancestor_of<Cat,Dog>::type> a = new Cat();
std::unique_ptr<common_ancestor_of<Cat,Dog>::type> b = new Dog();
std::unique_ptr<common_ancestor_of<Cat,Dog>::type> c = new Fish(); // compile error
std::unique_ptr<common_ancestor_of<Cat,Dog,Fish>::type> d = new Fish();
a and b are both std::unique_ptr<Mammal>, c is std::unique_ptr<Animal>.
How is this possible with modern C++?
Afaik no, there is no way in C++ to get the base class of a given class. There is introspection (part of reflection) in the works, but I wouldn't hold my breath for it to go into the standard any time soon.
The only way would be to make the classes cooperate. E.g. Have each class a member alias using Base = Animal. And then cook a trait that finds the common base between them. That would be a lot of work. You need to take into account multiple base classes and chains of inheritance. It's not trivial. You need to analyze your problem and see if all this complicated work is worth it or if there is another simpler way for what you are trying to achieve (which btw you din't mention). You may have an XY problem on your hand.
No, common_ancestor_of would require reflection to get the base class of any of the given classes.
If you have a concrete set of classes, you could use std::variant of all the possible classes.
If not, you could use a base class trait (Either a struct base_class that you specialise for all of your types or a member type like using super = ... or using base = ...) and manually find the common ancestor:
template<typename T>
struct type_identity {
using type = T;
};
template<typename... Types>
struct common_ancestor_of;
template<typename T>
struct common_ancestor_of<T> {
using type = T;
};
template<typename T>
struct common_ancestor_of<T, T> {
using type = T;
};
template<typename T, typename U, typename... Rest>
struct common_ancestor_of<T, U, Rest...> : common_ancestor_of<typename common_ancestor_of<T, U>::type, Rest...> {};
template<typename T, typename U>
struct common_ancestor_of<T, U> {
private:
// Base == Derived, so is in it's inheritance chain
template<typename Base, typename Derived, typename std::enable_if<std::is_same<Base, Derived>::value, int>::type = 0>
static constexpr bool in_inheritance_chain(int) {
return true;
}
// Base != Derived, but Derived has a member type `super`, so recursively check `super`
template<typename Base, typename Derived, typename std::enable_if<!std::is_same<Base, Derived>::value && (noexcept(type_identity<typename Derived::super>{}), true), int>::type = 0>
static constexpr bool in_inheritance_chain(int) {
return in_inheritance_chain<Base, typename Derived::super>(0);
}
// Base != Derived and Derived doesn't have a member type `super`, so it isn't in the inheritance chain
template<typename Base, typename Derived>
static constexpr bool in_inheritance_chain(long) {
return false;
}
// T1 is in the inheritance chain for U1, so it is the common ancestor
template<typename T1, typename U1, typename std::enable_if<in_inheritance_chain<T1, U1>(0), int>::type = 0>
static type_identity<T1> find_common_ancestor(int);
// T1 is not in the inheritance chain, so check T1::super
template<typename T1, typename U1>
static decltype(find_common_ancestor<typename T1::super, U1>(0)) find_common_ancestor(long) {}
public:
using type = typename decltype(find_common_ancestor<T, U>(0))::type;
};
template<typename... Types>
using common_ancestor_of_t = typename common_ancestor_of<Types...>::type;
class Animal { };
class Mammal : public Animal { public: using super = Animal; };
class Fish : public Animal { public: using super = Animal; };
class Cat : public Mammal { public: using super = Mammal; };
class Dog : public Mammal { public: using super = Mammal; };
static_assert(std::is_same<typename Cat::super::super, Animal>::value);
static_assert(std::is_same<common_ancestor_of_t<Cat, Dog>, Mammal>::value);
static_assert(std::is_same<common_ancestor_of_t<Cat, Fish>, Animal>::value);
static_assert(std::is_same<common_ancestor_of_t<Fish, Cat>, Animal>::value);
static_assert(std::is_same<common_ancestor_of_t<Cat, Dog, Fish>, Animal>::value);
This gets the most specialised common ancestor, but consider using the common ancestor you already have: std::unique_ptr<Animal>. If you specifically write std::unique_ptr<common_ancestor_of_t<Cat, Dog>> in your code, it is just as easy to write std::unique_ptr<Mammal>. If it's behind some templated code, std::unique_ptr<Animal> should work just as well.
I have some template class
template <typename T> class Base { };
I trying to inherit Derived class from Base and use Internal class that is nested of Derived as a generic argument of Base.
class Derived : Base<Internal> {
public: class Internal { };
}
But compiler doesn't see Internal in Base.
Is it possible to solve my problem with keeping Internal class as nested of Derived?
And if it is possible - How?
You can! Nope
(similar question)
I don't think that this can work. In order to define the class Derived you need to know the base class ("it must be a complete type") first; to instantiate the template Base into a class you need to have a type. Inner however is only a type "inside" of Derived; it's part of it's definition.
Definition of Derived ---needs---> Base<Derived::Inner>
^ |
|----------needs----------------|
Even an indirection does not help:
template<typename T>
class Base {};
template<template<class> class X, typename T>
struct UseInner : public X<typename T::Inner> {};
class Derived : UseInner<Base, Derived>
{
public: class Inner {};
};
In contrast to this, the CRTP works because there the template parameter can be an incomplete type ("no need to access internals").
Wrapping in a template doesn't work:
template<typename T>
class Base {};
template<template<class>class X>
struct Wrap
{
struct Derived : X<typename Derived::Inner> {
struct Inner {};
};
};
using RealDerived = typename Wrap<Base>::Derived;
I asked the following question in this post (pasted below for convenience). One of the comments suggested that there is a CRTP-based solution to the problem. I am not able to figure out how CRTP is relevant here (well, I never used CRTP before, so I am not used to thinking in these terms). So, how would a CRTP-based solution look like?
Here is the cited question:
Is it possible to write a template function that would possess type information about the base class of the template argument? (assuming that the template argument derives from one class only)
So, I am looking for something like this:
template <class T>
auto f(T t) -> decltype(...) { // ... is some SFINAE magic that
// catches B, the base of T
std::cout << (B)t << std::endl;
}
Some relevant background: I am writing a generic implementation of the A* algorithm. The template argument is a Node structure. So, the user might define:
struct NodeBase {
REFLECTABLE((double)g, (double)f)
// Using the REFLECTABLE macro as described here:
// https://stackoverflow.com/a/11744832/2725810
};
struct NodeData : public NodeBase {
using Base1 = NodeBase;
REFLECTABLE((double)F)
};
I would like to write a function that prints the contents of the node structure. REFLECTABLE does all the hard work of extracting the fields of the struct. However, when the user gives me a NodeData instance, my function needs to print the contents of the NodeBase component as well. I would like to later add overloads of my function for two and three base classes.
to know whether a class derives from a base class we have the std::is_base_of<> template structure, which can be used in conjunction with partial specialisation, or std::enable_if.
Here is a demonstration of using a partially specialised structure to apply a an operation depending on whether it's derived from node_base or not (in this case, it just prints the base object but you could do any other operation)
#include <iostream>
#include <type_traits>
// base class
struct node_base
{
};
std::ostream& operator<<(std::ostream& os, const node_base& nb)
{
os << "node_base_stuff";
return os;
}
// a class derived from node_base
struct node : public node_base
{
};
// a class not derived from node_base
struct not_node
{
};
// apply the general case - do nothing
template<class T, class = void>
struct report_impl
{
static void apply(const T&) {};
};
// apply the case where an object T is derived from node_base
template<class T>
struct report_impl<T, std::enable_if_t< std::is_base_of<node_base, T>::value > >
{
static void apply(const T& t) {
std::cout << static_cast<const node_base&>(t) << std::endl;
};
};
// the general form of the report function defers to the partially
// specialised application class
template<class T>
void report(const T& t)
{
report_impl<T>::apply(t);
}
using namespace std;
// a quick test
auto main() -> int
{
node n;
not_node nn;
report(n);
report(nn);
return 0;
}
expected output:
node_base_stuff
Here is my own first solution. It is not CRTP though and it suffers from a huge drawback as explained at the end of the answer:
template <class Base1_ = void, class Base2_ = void, class Base3_ = void,
class Base4_ = void>
struct ManagedNode;
// For classes that do not derive
template <> struct ManagedNode<void, void, void, void> {
using Base1 = void; using Base2 = void; using Base3 = void;
using Base4 = void;
};
// To avoid inaccessible base
// See http://stackoverflow.com/q/34255802/2725810
struct Inter0: public ManagedNode<>{};
// For classes that derive from a single base class
template <class Base1_>
struct ManagedNode<Base1_, void, void, void> : public Inter0,
public Base1_ {
using Base1 = Base1_;
};
// To avoid inaccessible base
template <class Base1_>
struct Inter1: public ManagedNode<Base1_>{};
// For classes that derive from two base classes
template <class Base1_, class Base2_>
struct ManagedNode<Base1_, Base2_, void, void> : public Inter1<Base1_>,
public Base2_ {
using Base2 = Base2_;
};
// Some user classes for testing the concept
struct A : public ManagedNode<> {
int data1;
};
struct B : public ManagedNode<> {};
struct C : public ManagedNode<A, B> {};
int main() {
C c;
std::cout << sizeof(c) << std::endl;
return 0;
}
This code produces the output of 12, which means that c contains the data1 member three times! For my purposes this drawback over-weighs the benefits of the reflection that this approach provides. So, does anyone have a suggestion for a better approach?
Is it possible to have multiple template types in C++?
For example;
template<template<typename> class BaseClass>
class MyClass {};
template<class BaseClass>
class MyClass {};
And using it;
// Using the first template (template<typename> class BaseClass)
template<typename SubClass>
class MyFirstSubClass : public MyClass<MyFirstSubClass> {};
// Using the second template (class BaseClass)
class MySecondSubClass : public MyClass<MySecondSubClass> {};
You can't have separate templates with the same name, but you can have a base template and one or more specializations of that class.
template<class BaseClass>
class MyClass
{ };
template<class T, template<typename> class BaseClass>
class MyClass<BaseClass<T>>
{ };
template<>
class MyClass<int>
{ };
No, it's not possible. You'll have to have two different template classes:
template<template<typename> class BaseClass>
class MyClassA {};
template<class BaseClass>
class MyClassB {};
Here is the (simplified) base class:
template <class T>
class SharedObject
{
protected:
QExplicitlySharedDataPointer <typename T::Data> d;
};
And here is the derived:
class ThisWontCompile : public SharedObject <ThisWontCompile>
{
private:
friend class SharedObject;
struct Data : public QSharedData
{
int id;
};
};
Is there any workaround to access ThisWontCompile::Data from SharedObject? What exactly can and what exactly cannot be done with the derived object from the base object?
This actually isn't related to the accessibility and friendship, it's related to the use of CRTP. Consider the following example that also exhibits the problem:
template <class T>
struct Base
{
typedef typename T::Data Data;
};
struct ThisWontCompile : public Base<ThisWontCompile>
{
struct Data { };
};
The problem is that ThisWontCompile is incomplete at the time it is used as a template argument to Base, so it can only be used as an incomplete type in Base.
For a handful of solutions to your specific problem, consult the answers to this other question, especially Martin's recommendation to use a traits class, which would basically look like this:
// Base
template <typename T>
struct BaseTraits;
template <typename T>
struct Base
{
typedef typename BaseTraits<T>::Data Data;
};
// Derived
struct Derived;
template <>
struct BaseTraits<Derived>
{
struct Data { };
};
struct Derived : public Base<Derived>
{
};
typename BaseTraits<Derived>::Data can be used in both Derived and in Base. If Derived is itself a template, you can use a partial specialization for the traits class.