2

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.
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 4
    you can put the `using ...` part just before class definition – Garf365 Mar 07 '16 at 14:57
  • 2
    @Garf365, this would introduce those names into scope, and they suppsedly have no meaning for anybody else but the class. – SergeyA Mar 07 '16 at 14:59
  • Do not be overzelaous. Those names have meaning only inside the class, so keep them inside the class. No one died of two names repeated. – SergeyA Mar 07 '16 at 15:08
  • You can do this without any kind of helper classes due to a feature called an injected-class-name. See my answer. – Simple Mar 07 '16 at 16:35

5 Answers5

6

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>
{
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

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

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • @einpoklum, nope - this example is self-contained, as provided link shows. – SergeyA Mar 07 '16 at 15:42
  • I wonder if I shouldn't prefer std::tuple_element<>, though, like in Simon Kraemer's [answer](http://stackoverflow.com/a/35847927/1593077). – einpoklum Mar 07 '16 at 15:47
  • 1
    @einpoklum, you can use tuple_element, if you want. It would be pretty much the same thing. I was just showing how to do this on it's own - because I believe there is much more value in the technique shown, rather than in the actual goal achieved (like I said, I do not advocate to use my or Simon's code in real app). – SergeyA Mar 07 '16 at 15:49
1

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;
}
Simon Kraemer
  • 5,700
  • 1
  • 19
  • 49
  • I think this is the nicest suggestion so far, although - it's not a transparent change, in the sense that subclasses of `Faa` now having to be aware that there's an intermediate parent. Also, do you also share the impression this would be unacceptable in production code? – einpoklum Mar 07 '16 at 15:57
  • @einpoklum I think it depends on the size of the project. For a small program it might be ok, but who needs this kind of templates in small programs? I think the main problem is that it's hard to read and therefore is errorprone (Just think someone changes the order of the base classes - try to find that error). I think you should accept and use @Jarod42's answer as it is easier to maintain than my solution, but use more meaningful names than `parentA1`. Maybe you could also combine it with the answer of simple - yet this has problems too. – Simon Kraemer Mar 07 '16 at 17:59
1

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(); }
};
Simple
  • 13,992
  • 2
  • 47
  • 47
0

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>
{
};
einpoklum
  • 118,144
  • 57
  • 340
  • 684