Consider
class B; class C;
class A {
using linked_from = std::tuple<B,C>; // i.e. A is "linked from" B and C.
};
class B {
using linked_to = std::tuple<A,C>; // i.e. B is "linked to" A and C.
};
class C {
using linked_to = std::tuple<A>;
using linked_from = std::tuple<B>;
};
What I want to do is redesign the above so that there is no maintenance responsibilities whenever the linked_to member types are changed. Perhaps something along the lines of
template <typename To, typename From>
struct LinkFromMap {
using linked_from = From;
};
template <typename From, typename To>
struct LinkPair : LinkFromMap<To, From> {};
template <typename T, typename... Ts>
struct Link : LinkPair<Ts, T>... {
using linked_to = std::tuple<Ts...>;
};
class B; class C;
class A {};
class B : public Link<B, A,C> {};
class C : public Link<C, A> {};
int main() {
static_assert(std::is_same<B::linked_to, std::tuple<A,C>>::value, "");
// Still need to obtain std::tuple<B,C> from A, std::tuple<B> from C, etc...
}
A and C do not need to keep their linked_from member types, as long as std::tuple<B,C> can be obtained from A and std::tuple<B> can be obtained from C. That is why I've defined LinkFromMap above, but I can't figure out how to use LinkFromMap to achieve that. Can it be used at the end somehow to form the packs <A, B,C> and <C,B> or something like that? Or is there a better way to automate type retrievals that linked_from was designed for?
Related
I wanted do design a template class with two arguments that at compile time inherited based on the template arguments one of two mutually exclusive base classes.
I wanted to keep it simple for me so came up with this working example. The inheritance condition i got with std::conditional based on the template arguments. The specialized methods for that conditional inheritance I set with std::enable_if.
class Empty {};
template<typename T>
class NonEmpty { protected: std::vector<T> mObjects; };
template< typename A, typename B = A>
class Storage : public std::conditional<std::is_same<A, B>::value, Empty, NonEmpty<B>>::type
{
public:
template<typename C = B, typename std::enable_if<std::is_same<C, A>::value>::type* = nullptr>
void doStuff()
{
// one argument or two arguments with same type
// do stuff ...
};
template<typename C = B, typename std::enable_if<std::is_same<C, A>::value>::type* = nullptr>
void doSomthingElse()
{
// one argument or two arguments with same type
// do something exclusively just for this argument constellation ...
};
template<typename C = B, typename std::enable_if<!std::is_same<C, A>::value>::type* = nullptr>
void doStuff()
{
// two arguments with different types
// do stuff with inherited variables of NonEmpty-Class ...
};
};
int main()
{
EmptyClass<int> emp;
NonEmptyClass<int, float> nonemp;
emp.doStuff();
emp.doSomethingElse();
nonemp.doStuff();
}
Is there a better way to go about it, or are there any improvements for my existing solution?
(I am using GCC 8.1.0 with C++ 14)
In my opinion you're much better of partially specializing the template, since the entire implementation for both versions are completely independent. This way you can also not inherit any class instead of inheriting an empty class.
template<typename T>
class NonEmpty {
protected:
std::vector<T> mObjects;
};
template<typename A, typename B = A>
class Storage : public NonEmpty<B>
{
public:
void doStuff()
{
std::cout << "doStuff() different types\n";
};
};
template<typename A>
class Storage<A, A>
{
public:
void doStuff()
{
std::cout << "doStuff() same types\n";
};
void doSomethingElse()
{
std::cout << "doSomethingElse()\n";
};
};
int main() {
Storage<int> emp;
Storage<int, float> nonemp;
emp.doStuff();
emp.doSomethingElse();
nonemp.doStuff();
}
I do not see the benefit of placing it all in one template. I have the impression that you are making things complicated by stuffing all in a single template but then need to select which version it is for each single method. Two seperate classes:
template <typename A>
struct inherits_from_empty : Empty {
// implement methods here, no sfinae needed
};
and
template <typename A, typename B>
struct inherits_from_nonEmpty : NonEmpty<A> {
// implement methods here, no sfinae needed
};
Can be choosen from via
template <typename A,typename B>
using Storage = std::conditional_t<std::is_same<A, B>::value,
inherits_from_empty<A>,
inherits_from_nonEmpty<A>>;
I have a class template like this:
template <typename T1, typename T2>
class ClassName {
// Members variables and Functions based on the template parameter types
};
(T1, T2) can either be (class A, class B) or (class C, class D) only.
So, T1 alone is enough to determine T2.
Is there any way to only take T1 as a parameter and write the same class? If yes, how?
If your template only depends on a single parameter, you should not declare it with 2 parameters. You can use std::conditional together with std::is_same to declare conditional aliases.
If you want:
(T1, T2) can either be (class A, class B) or (class C, class D) only
If "only" is meant as: User only ever instantiates for A or C and in that case T2 should be B / D:
#include <type_traits>
struct A {};
struct B {};
struct D {};
template <typename T>
struct Foo {
using T1 = T;
using T2 = std::conditional_t<std::is_same_v<A, T>, B, D>;
};
Foo<A>::T2 is B and Foo<C>::T2 is D. Actually Foo<T>::T2 is D for any T that is not A. In other words, as mentioned above, the premise is that it is only instantiated for either A or C. If the template itself should make sure that only instantiations for A or C are valid, you might want to add a static_assert (again using std::is_same to check that T is among the allowed types).
Make an enumeration of your configurations:
enum class ClassConfigEnum { AB, CD};
template<ClassConfigEnum config> struct ClassConfig;
template<> struct ClassConfig<AB> {
using T1 = A;
using T2 = B;
};
template<> struct ClassConfig<CD> {
using T1 = C;
using T2 = D;
};
And then have the class use the enumerated configuration
template <ClassConfigEnum config>
class ClassName {
using T1 = ClassConfig<config>::typename T1;
using T2 = ClassConfig<config>::typename T2;
};
You can write a type trait:
#include <iostream>
#include <type_traits>
struct A {};
struct B {};
struct C {};
struct D {};
template <typename Type> struct Dependent {};
template<>
struct Dependent<A> {
using type = B;
};
template<>
struct Dependent<C> {
using type = D;
};
template <typename Type>
class ClassName {
using Dependent = typename Dependent<Type>::type;
};
int main()
{
static_assert(std::is_same<Dependent<A>::type, B>::value, "");
static_assert(std::is_same<Dependent<C>::type, D>::value, "");
// Dependent<B>::type a; error: 'type' is not a member of 'Dependent<B>'
}
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.
The derived class hides the name of an overload set from the base class if the derived class has the same name defined, but we can always introduce that overload set back with using-declaration:
template <class BASE>
class A : public BASE
{
public:
using BASE::some_method;
void some_method();
}
But what if I introduce all overload sets from variadic base classes?
Would I be able to write something like this?
template <class... BASES>
class A : public BASES...
{
public:
using BASES::some_method...;
void some_method();
}
I've considered using a helper class like:
template <class... BASES>
struct helper;
template <>
struct helper<> {};
template <class OnlyBase>
struct helper<OnlyBase> : OnlyBase
{
using OnlyBase::some_method;
};
template <class Base1, class... OtherBases>
struct helper<Base1, OtherBases> : public Base1, public helper<OtherBases...>
{
using Base1::some_method;
using helper<OtherBases...>::some_method;
};
And it does work. But it requires a lot of typing (of course I can use macro but I try to use c++'s compile-time feature whenever possible), and when I want to introduce more methods, i have to change much in that piece of code.
A perfect answer would be a simple syntax, but if there's none, I will go with the helper class.
Here is a trick how to reduce handwriting:
// U<X,Y> is a binary operation on two classes
template<template<class,class>class U, class... Xs> struct foldr;
template<template<class,class>class U, class X> struct foldr<U,X> : X {};
template<template<class,class>class U, class X, class... Xs> struct foldr<U,X,Xs...> : U<X, foldr<U,Xs...>> {};
// our operation inherits from both classes and declares using the member f of them
template<class X, class Y> struct using_f : X,Y { using X::f; using Y::f; };
struct A { void f(int) {} };
struct B { void f(char) {} };
struct C { void f(long) {} };
struct D : foldr<using_f, A, B, C> {};
int main() {
D d;
d.f(1);
d.f('1');
d.f(1L);
return 0;
}
So we should write foldr once, then write simple ad-hoc operations - using_f, using_g, using_f_g
Maybe there is a way to further simplifying. Let me think a bit...
The derived class hides the name of an overload set from the base class if the derived class has the same name defined, but we can always introduce that overload set back with using-declaration:
template <class BASE>
class A : public BASE
{
public:
using BASE::some_method;
void some_method();
}
But what if I introduce all overload sets from variadic base classes?
Would I be able to write something like this?
template <class... BASES>
class A : public BASES...
{
public:
using BASES::some_method...;
void some_method();
}
I've considered using a helper class like:
template <class... BASES>
struct helper;
template <>
struct helper<> {};
template <class OnlyBase>
struct helper<OnlyBase> : OnlyBase
{
using OnlyBase::some_method;
};
template <class Base1, class... OtherBases>
struct helper<Base1, OtherBases> : public Base1, public helper<OtherBases...>
{
using Base1::some_method;
using helper<OtherBases...>::some_method;
};
And it does work. But it requires a lot of typing (of course I can use macro but I try to use c++'s compile-time feature whenever possible), and when I want to introduce more methods, i have to change much in that piece of code.
A perfect answer would be a simple syntax, but if there's none, I will go with the helper class.
Here is a trick how to reduce handwriting:
// U<X,Y> is a binary operation on two classes
template<template<class,class>class U, class... Xs> struct foldr;
template<template<class,class>class U, class X> struct foldr<U,X> : X {};
template<template<class,class>class U, class X, class... Xs> struct foldr<U,X,Xs...> : U<X, foldr<U,Xs...>> {};
// our operation inherits from both classes and declares using the member f of them
template<class X, class Y> struct using_f : X,Y { using X::f; using Y::f; };
struct A { void f(int) {} };
struct B { void f(char) {} };
struct C { void f(long) {} };
struct D : foldr<using_f, A, B, C> {};
int main() {
D d;
d.f(1);
d.f('1');
d.f(1L);
return 0;
}
So we should write foldr once, then write simple ad-hoc operations - using_f, using_g, using_f_g
Maybe there is a way to further simplifying. Let me think a bit...