Template Base Class Collection
Tags: cpp, development, metaprogramming, inheritance
Published: 2020-09-01 22:44:42

---
title: Template Base Class Collection
published: true
date: 2020-08-30 10:30:00 UTC
tags: cpp,development,metaprogramming,inheritance
canonical_url: https://cppsenioreas.wordpress.com/2020/08/30/template-base-class-collection/
---

<p><em>When designing an architecture you might come across a need to create a collection of a template class. At first it'll sound easy, but soon you'll find that it's almost impossible to archive. At the end of this article, you'll be able to archive the impossible.</em></p>

<p><strong>Previous article in series:</strong> <a href="https://cppsenioreas.wordpress.com/2020/08/27/substitution-failure-is-not-an-error-sfinae/">Substitution Failure is Not an Error – SFINAE</a></p>

<h1>The Problem</h1>

<p>A little reminder from a previous article [<a href="https://cppsenioreas.wordpress.com/2020/08/09/templates-infinity-theory-from-cpp-11-to-cpp-20-part-1/">templates infinity theory - part 1</a>] for the shape class (with an additional shape name):</p>

```cpp
template <typename ...Properties> // Variadic Template
class shape : virtual public Properties... { // Multiple properties inheritances, the virtual will be more significant in the future
public:
    explicit shape(std::string name) : name(std::move(name)) {}
    virtual ~shape() = default;
    virtual void input_data() { (Properties::input_data(), ...); }
    [[nodiscard]] virtual double area() const = 0;
    [[nodiscard]] std::string get_name() const { return name; }
protected: std::string name;
};
```

<p>After we designed our shape base class, we created some different shapes with different properties (this is a shortcut, for the full example please refer to the <a rel="noreferrer noopener" href="https://cppsenioreas.wordpress.com/2020/08/09/templates-infinity-theory-from-cpp-11-to-cpp-20-part-1/" target="_blank">original article with this example</a>):</p>

```cpp
class rectangle : public shape<width_shape_property, height_shape_property> {
public:
    rectangle() : shape("Rectangle") {}
    void input_data() override { std::cout << "Input " << name << ":" << std::endl; shape::input_data(); }
    [[nodiscard]] double area() const override { return width * height; }
};

class triangle : public shape<width_shape_property, height_shape_property> {
public:
    triangle() : shape("Triangle") {}
    void input_data() override { std::cout << "Input " << name << ":" << std::endl; shape::input_data(); }
    [[nodiscard]] double area() const override { return (width * height) / 2; }
};

class circle : public shape<radios_shape_property> {
public:
    circle() : shape("Circle") {}
    void input_data() override { std::cout << "Input " << name << ":" << std::endl; shape::input_data(); }
    [[nodiscard]] double area() const override { return M_PI * radios * radios; }
};
```

<p>Now we might want to create a collection to contain different shapes, they all inherit from the <em>same</em> class after all:</p>

```cpp
int main() {
    std::vector<std::shared_ptr<shape>> shapes;
    return EXIT_SUCCESS;
}
```

<p>And... We got an error, which is basically saying "Which shape vector do you want to create?". Well, it didn't go so easy. To understand it, we need to understand the issue.</p>

<p><code>shape</code> is not a class, it's a template class. Every specialization will be an unrelated, independent class, which means we can't contain them under the same pointer type. Moreover different specializations might not implement the same interface:</p>

```cpp
template <typename T>
class base {
public:
    void func() {}
};

template<>
class base<int> {
public:
    void my_func() {} // No func here!
}

template<>
class base<double> {
public:
    double func; // func here is a class member variable
}
```


<p>Now, with the understanding that we need to create a shared interface, which will hold for every specialization we'll create, it is the right time to build our solution.</p>

<h1>The Solution</h1>

<p><em>Note: In 2013 Sean Parent published a presentation that this solution is based on:  <a href="https://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil">Inheritance Is The Base Class of Evil</a>.</em></p>

<p>Let's start with easy steps before we get to the most generic solution. We understood that we can't create a shared template interface with a guarantee that it'll really stay a "shared". So let's separate our base template class from the actual interface:</p>

```cpp
class shape_interface {
public:
    virtual ~shape_interface() = default;
    virtual void input_data() = 0;
    [[nodiscard]] virtual double area() const = 0;
};

class shape_property {
public:
    virtual void input_data() = 0;
};

template <typename T>
concept ShapeProperty = std::is_base_of_v<shape_property, T>;

template <ShapeProperty ...Properties>
class shape : public shape_interface, virtual public Properties... {
public:
    explicit shape(std::string name) : name(std::move(name)) {}
    virtual ~shape() = default;
    void input_data() override { (Properties::input_data(), ...); };
    [[nodiscard]] std::string get_name() const { return name; }
protected: std::string name;
};
```

<p>With this <code>shape_interface</code> we can now create a vector which will hold our shapes:</p>

```cpp
int main() {
    std::vector<std::shared_ptr<shape_interface>> vec;

    vec.emplace_back(std::make_shared<rectangle>());
    vec.emplace_back(std::make_shared<triangle>());
    vec.emplace_back(std::make_shared<circle>());
    
    return EXIT_SUCCESS;
}
```

<p>Hooray! It worked just like any other interface! Let's go home and drink a cold beer!</p>

<p>When you woke up in the morning with a little headache, you noticed a new email from work:</p>

> Hi there, I'm Bob, a coworker of you.<br>I saw your solution for the shapes, and I loved it!<br>However, I just found a library which offers a lot of shapes with a <em>similar</em> interface. It has the same methods with some extra, so I thought it might be nice thing to include their shapes as well.<br>One issue: I can't see their source code, so I can't modify their interface. Any ideas how to solve this issue?<br>Cheers, Bob.

<p>The first thought that came to your mind is: Did he have to use "Cheers"? The second one is why the hell did he find this library?? So you came back to your work and started to think about the new situation.</p>

<p>You have a new class that implements the same functionality you required in your interface, but it simply uses another one:</p>

```cpp
class independent_legal_shape : another_interface {
public:
    independent_legal_shape() = default;

    void input_data() {
        std::cout << "No input needed for independent_legal_shape." << std::endl;
    }

    [[nodiscard]] double area() const {
        return 50;
    }
};

int main() {
    std::vector<std::shared_ptr<shape_interface>> vec;

    vec.emplace_back(std::make_shared<rectangle>());
    vec.emplace_back(std::make_shared<triangle>());
    vec.emplace_back(std::make_shared<circle>());
    // vec.emplace_back(std::make_shared<independent_legal_shape>()); // Obviously that won't compile
    
    return EXIT_SUCCESS;
}
```

<p>Suddenly, out of the blue, an idea came to your mind: What about creating a container which will hold any type and just call its methods of the desired interface. Something like that:</p>

```cpp
template <typename T>
class shape_interface_implementation : public shape_interface {
    T&amp; m_impl_obj; // Contain the target specialized class instance

public:
    explicit shape_interface_implementation(T&amp; impl_obj) : m_impl_obj(impl_obj) {}

    // Interface should be fully implemented here
    // region [Interface Implementation Begin]
    void input_data() override { return m_impl_obj.input_data(); };
    [[nodiscard]] double area() const override { return m_impl_obj.area(); };
    // endregion [Interface Implementation End]
};
```

<p>Now your main will look like that:</p>

```cpp
int main() {
    std::vector<std::shared_ptr<shape_interface>> vec;
    rectangle r; triangle t; circle c; independent_legal_shape my_shape;

    vec.emplace_back(std::make_shared<shape_interface_implementation<rectangle>>(r));
    vec.emplace_back(std::make_shared<shape_interface_implementation<triangle>>(t));
    vec.emplace_back(std::make_shared<shape_interface_implementation<circle>>(c));
    vec.emplace_back(std::make_shared<shape_interface_implementation<independent_legal_shape>>(my_shape));

    return EXIT_SUCCESS;
}
```

<p>It works, but something feels a little bit fishy here. It looks a lot more complicated than it was before, and something tells you that it can be simplified. A little search around the web, and you find a short video of Sean Parent: <a href="https://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil#c637341694594767780">Inheritance Is The Base Class of Evil</a>. There he presented a wrapper class which holds the pointer to the interface inside it. With little modifications to hold a direct access to the objects of the wrapper, you get the following wrapper class:</p>

```cpp
class template_base_shape_container {
public:
    template <typename T>
    explicit template_base_shape_container(T &amp;obj) : m_obj(std::make_shared<interface_implementation<T>>(obj)) {}

    // Get a pointer to the interface implementation
    std::shared_ptr<shape_interface> get() { return m_obj; }

private:
    template <typename T>
    class shape_interface_implementation : public shape_interface { // The exact same interface_implementation as before
        T&amp; m_impl_obj; // Contain the target specialized class instance

    public:
        explicit shape_interface_implementation(T&amp; impl_obj) : m_impl_obj(impl_obj) {}

        // Interface should be fully implemented here
        // region [Interface Implementation Begin]
        void input_data() override { return m_impl_obj.input_data(); };
        [[nodiscard]] double area() const override { return m_impl_obj.area(); };
        // endregion [Interface Implementation End]
    };

    std::shared_ptr<shape_interface> m_obj; // Inner pointer
};

int main() {
    std::vector<template_base_shape_container> shapes;
    rectangle r; triangle t; circle c; independent_legal_shape my_shape;

    shapes.emplace_back(r);
    shapes.emplace_back(t);
    shapes.emplace_back(c);
    shapes.emplace_back(my_shape);

    return EXIT_SUCCESS;
}
```


<p>Looks even better than the simple shared interface. The only change is the access which now looks like that:</p>


```cpp
for (auto &amp;shape : shapes) {
    shape.get()->input_data();
    std::cout << shape.get()->area() << std::endl;
}
```


<p>Another day past with a success, but this time you don't risk yourself with a beer. You fall asleep wishing to wake up without any new work emails. When you wake up, surprisingly no new emails. You come to another work day, happy that you completely solved the issue, but then it hit you. You look at the nice code you left the other day, and you just can't believe it. New 10 wrapping classes which look exactly the same, with a single change: The Interface [Coming Soon at IMDb]. Let's make the container a generic container!</p>


```cpp
// A generic interface implementation
template <typename Interface, typename T>
class interface_implementation : public Interface {
public:
    explicit interface_implementation(T&amp; impl_obj) : m_impl_obj(impl_obj) {}

    // Interface should be fully implemented here
    // region [Interface Implementation Begin]
    // endregion [Interface Implementation End]

private:
    T&amp; m_impl_obj; // Contain the target specialized class instance
};

// A generic template base container to match any future interface
template <typename Interface>
class template_base_container {
public:
    template <typename T>
    explicit template_base_container(T &amp;obj) : m_obj(std::make_shared<interface_implementation<Interface, T>>(obj)) {
        static_assert(std::is_base_of_v<Interface, interface_implementation<Interface, T>>, "In template_base_container interface_implementation<Interface, T> should inherit from Interface template param.");
    }

    // Get a pointer to the interface implementation
    std::shared_ptr<Interface> get() { return m_obj; }

private:
    std::shared_ptr<Interface> m_obj;
};
```

<p>And for the interface specialization:</p>

```cpp
// Interface implementation specialization for shape_interface
template <typename T>
class interface_implementation<shape_interface, T> : public shape_interface {
public:
    explicit interface_implementation(T&amp; impl_obj) : m_impl_obj(impl_obj) {}

    // Interface should be fully implemented here
    // region [Interface Implementation Begin]
    void input_data() override { return m_impl_obj.input_data(); };
    [[nodiscard]] double area() const override { return m_impl_obj.area(); };
    // endregion [Interface Implementation End]

private:
    T&amp; m_impl_obj; // Contain the target specialized class instance
};
```

<p>Our main stays without a change:</p>

```cpp
int main() {
    std::vector<template_base_container<shape_interface>> shapes;
    rectangle r; triangle t; circle c; independent_legal_shape my_shape;

    shapes.emplace_back(r);
    shapes.emplace_back(t);
    shapes.emplace_back(c);
    shapes.emplace_back(my_shape);

    for (auto &amp;shape : shapes) {
        shape.get()->input_data();
        std::cout << shape.get()->area() << std::endl;
    }

    return EXIT_SUCCESS;
}
```

<h1>Conclusion</h1>

<p>Templates and inheritances combinations are a huge topic with almost infinite possibilities in C++. This is the time to thank for Sean Parent for his "Inheritance Is The Base Class of Evil" amazing talk, that without it this article wouldn't be made.</p>

<p>Feel free to share your experience with templates &amp; inheritances here, and if you see any improvements that can made to make this container even more generic or written with less code duplications (e.g. interface_implementation have a code duplication for every specialization).</p>

<p><strong>Updated repository for template_base_container:</strong> <a href="https://github.com/coralkashri/CommonTemplateBase">common_template_base</a></p>

_This article originally published on my personal blog: [C++ Senioreas](https://cppsenioreas.wordpress.com/2020/08/30/template-base-class-collection/)_