Mihai Galos
Mihai Galos

Reputation: 1897

Non-intrusive design of generic C++ class

Using C++ 14, templates and a Bridge pattern, I'm trying to create a generic way of adding objects to a container named Shapes (vector of Shape objects). I would like to be able to automatically support new datatypes which are added to the container and print them without modifying the original implementation of the Shape class. Instead, I would like to only provide a new print(T) function, and everything should work out-of-the-box.

Below is my code, I still have some problems getting it to compile. Can anyone please help me out? Many thanks.

#include <iostream>
#include <memory>
#include <vector>
#include <map>
#include <string>

using namespace std;

void print(const int toPrint) {
    cout << " " << toPrint;
    cout << endl;
}

void print(const double toPrint) {
    cout << " " << toPrint;
    cout << endl;
}

void print(const vector<int> & toPrint) {
    for (auto & it : toPrint) {
        cout << " " << it;
    }
    cout << endl;
}

void print(const map<int, string> & toPrint) {
    for (auto & it : toPrint) {
        cout << "     " << it.first << " : " << it.second << endl;
    }
    cout << endl;
}

class Shape {
public:
    template<typename T>
    Shape(T &&t) {
        pimpl_ = make_unique<Specialization<T>>(t);
    }
    void print() const {
        pimpl_->print();
    }
private:
    struct Base {
        virtual void print() const = 0;
        virtual ~Base() = default;
    };

    template<typename T>
    struct Specialization: public Base {
        Specialization(T &t) :
                internalObject_ { std::move(t) } {
        }
        void print() const override {
            ::print(internalObject_);
        }

        T internalObject_;
    };

    unique_ptr<Base> pimpl_; 

};

typedef vector<Shape> Shapes;

void print(Shapes const & shapes) {
    for (auto & shape : shapes) {
        shape.print();
    }
}

int main() {
    Shapes shapes;

    shapes.push_back(1);
    shapes.push_back(2.0);
    shapes.push_back(0.3);

    shapes.push_back(vector<int> { 10, 11, 12 });
    shapes.push_back(map<int, string> { { 0, "elmo" }, { 1, "leom" } });

    print(shapes);
    return 0;
}

Upvotes: 1

Views: 218

Answers (1)

Emerald Weapon
Emerald Weapon

Reputation: 2540

Here is a patched up version of your code which compiles (on clang). As pointed out in the comments there are several issues that need to be addressed (this code leaks memory for example), but this should get you back on track.

#include <iostream>
#include <vector>

using namespace std;

void print(int toPrint) {
    cout << " " << toPrint;
    cout << endl;
}

void print(double toPrint) {
    cout << " " << toPrint;
    cout << endl;
}

void print(vector<int> toPrint) {
    for (auto & it : toPrint) {
        cout << " " << it;
    }
    cout << endl;
}

class Shape {
public:
    template<typename T>
    Shape(T t) {
        pimpl_ = new Specialization<T>(t);
    }
    void print() const{
        pimpl_->print();
    }
private:
    struct Base {
        virtual void print() const = 0;
    };

    template<typename T>
    struct Specialization: public Base {
        Specialization(T t) {
            internalObject_ = new T(t);
        }
        void print() const{
            ::print(*internalObject_);
        }

        T* internalObject_;
    };

    Base * pimpl_;
};

typedef vector<Shape> Shapes;

void print(Shapes const & shapes) {
    for (auto & shape : shapes) {
        shape.print();
    }
}

int main() {
    Shapes shapes;

    shapes.push_back(1);
    shapes.push_back(2.0);
    shapes.push_back(0.3);

    shapes.push_back(vector<int> { 10, 11, 12 });
    print(shapes);
    return 0;
}

Upvotes: 2

Related Questions