Suppose I have a
template <typename T>
class A :
class_with_long_name<T, and_many_other, template_arguments, oh_my_thats_long>,
anotherclass_with_long_name<and_many_other, template_arguments_that, are_also_annoying, including_also, T> { ... }
Now, in class A's definition, and/or in its methods, I need to refer to the two superclasses (e.g. to access members in the superclass, or types defined in it etc.) However, I want to avoid repeating the superclass names. At the moment, what I'm doing is something like:
template<typename T>
class A :
class_with_long_name<T, and_many_other, template_arguments, oh_my_thats_long>,
anotherclass_with_long_name<and_many_other, template_arguments_that, are_also_annoying, including_also, T>
{
using parent1 = class_with_long_name<T, and_many_other, template_arguments, oh_my_thats_long>;
using parent2 = anotherclass_with_long_name<and_many_other, template_arguments_that, are_also_annoying, including_also, T>;
...
}
which works, obviously, and reduces the number of repetitions to 2; but I would rather avoid even this repetition, if possible. Is there a reasonable way to do this?
Notes:
- "Reasonable" e.g. no macros except with very very good justification.
If you inherit with the same access specifier for all classes you could use something like this:
template <typename...S>
struct Bases : public S... {
template <size_t I>
using super = typename std::tuple_element<I, std::tuple<S...>>::type;
};
This will give you access to all base classes in the order you inherit from them via super<index>
.
Short example:
#include <iostream>
#include <tuple>
template <typename...S>
struct Bases : public S... {
template <size_t I>
using super = typename std::tuple_element<I, std::tuple<S...>>::type;
};
class Foo
{
public:
virtual void f()
{
std::cout << "Foo";
}
};
class Fii
{
public:
virtual void f()
{
std::cout << "Fii";
}
};
class Faa : private Bases<Foo, Fii>
{
public:
virtual void f()
{
std::cout << "Faa";
super<0>::f(); //Calls Foo::f()
super<1>::f(); //Calls Fii::f()
std::cout << std::endl;
}
};
int main()
{
Faa faa;
faa.f(); //Print "FaaFooFii"
return 0;
}
Before A, you may do
namespace detail
{
template <typename T>
using parentA1 = class_with_long_name<T,
and_many_other,
template_arguments,
oh_my_thats_long>;
template <typename T>
using parentA2 = anotherclass_with_long_name<and_many_other,
template_arguments_that,
are_also_annoying,
including_also,
T>;
}
And then
template<typename T>
class A : detail::parentA1<T>, detail::parentA2<T>
{
};
I think, the best option is what you are already doing. However, if you feel like you absoluletely can not tolerate this, here some funny code (if I would ever see anything like this during code review in production, I'd fight tooth and nail to get rid of it).
template<class... INHERIT_FROM>
struct inherit_publicly : public INHERIT_FROM... {
struct parent_types {
template <int N, class ARG, class... ARGS> struct get {
using type = typename get<N-1, ARGS...>::type;
};
template <class ARG, class... ARGS> struct get<0, ARG, ARGS...> {
using type = ARG;
};
};
template <int N> using parent = typename parent_types::template get<N, INHERIT_FROM...>::type;
};
// **example usage**
struct X { static constexpr const char* const name = "X"; };
struct Y { static constexpr const char* const name = "Y"; };
struct Z { static constexpr const char* const name = "Z"; };
#include <iostream>
struct A : inherit_publicly<X, Y, Z> {
void print_parents() {
std::cout << "First parent type: " << parent<0>::name << "; second: " << parent<1>::name << "; third: " <<parent<2>::name<< "\n";
}
};
int main() {
A a;
a.print_parents();
}
Live demo: http://coliru.stacked-crooked.com/a/37cacf70bed41463
You can just use A::class_with_long_name
or A::anotherclass_with_long_name
.
template<typename T>
class A
: class_with_long_name<T, and_many_other, template_arguments, oh_my_thats_long>
, anotherclass_with_long_name<and_many_other, template_arguments_that, are_also_annoying, including_also, T>
{
// If you still want the typedefs.
using parent1 = typename A::class_with_long_name;
using parent2 = typename A::anotherclass_with_long_name;
// If you don't.
void foo() { A::class_with_long_name::bar(); }
};
Based on @Jarod's solution: How about an inside-the-detail subnamespace?
namespace detail { namespace A {
template <typename T>
using parent1 = class_with_long_name<T,
and_many_other,
template_arguments,
oh_my_thats_long>;
template <typename T>
using parent2 = anotherclass_with_long_name<and_many_other,
template_arguments_that,
are_also_annoying,
including_also,
T>;
} // namespace A
} // namespace detail
And then
template<typename T>
class A : detail::A::parent1<T>, detail::A::parent2<T>
{
};