Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List of template classes of different types

Tags:

c++

templates

I'm trying to make a list of template classes of variable types. So the idea is to loop of a list of objects that all have a common function, e.g. getValue, but a different type. The type could be any type, raw types or objects.

I need this because i want to have a class that has a list of attributes of different types that i want to be able to construct at runtime.

So my class would look something like:

class MyClass {
    std::list<Attribute<?>*> attributes;
};

And my attribute template:

template<typename T>
class Attribute {
public:
    Test(const T &t) : _t(t) {}

    T getValue() const { return _t; }
    void setValue(const T &t) { _t = t; }

private:
    T _t;
};

int main() {
    MyClass myClass;
    myClass.attributes.push_back(new Attribute<int>(42));
    myClass.attributes.push_back(new Attribute<double>(42.0));
}

As you can see the list of MyClass i put ? because that is my problem. I dont know how to make a list that will take different types of my Attribute template, i.e. int, double etc.

std::list<Attribute<?> *> attributes;

In Java, generics can be used for that. Is it possible in C++ to do this with somekind of construction? I tried using variadic templates but that doesnt seem to help solving my problem.

I need this but not in Java, in C++:

public class GenericAttribute<T> {

    private T value;

    public GenericAttribute (T value) {
        setValue(value);
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

public static void main(String[] args) {

    class Custom {
        public Custom() {}

        @Override public String toString() {
            return "My custom object";
        }
    }

    List<GenericAttribute<?>> attributes = new ArrayList<GenericAttribute<?>>();
    attributes.add(new GenericAttribute<Integer>(1));
    attributes.add(new GenericAttribute<Double>(3.1415926535));
    attributes.add(new GenericAttribute<Custom>(new Custom()));

    for (GenericAttribute<?> attr : attributes) {
        System.out.println(attr.getValue());
    }
}

Output:

1
3.1415926535
My custom object

Thanks for the help!

like image 682
fonZ Avatar asked Aug 03 '14 18:08

fonZ


People also ask

How many types of templates are there?

There are three kinds of templates: function templates, class templates and, since C++14, variable templates. Since C++11, templates may be either variadic or non-variadic; in earlier versions of C++ they are always non-variadic.

What are templates and its types?

Templates in c++ is defined as a blueprint or formula for creating a generic class or a function. Generic Programming is an approach to programming where generic types are used as parameters in algorithms to work for a variety of data types.In C++, a template is a straightforward yet effective tool.

What are template classes in C++?

The relationship between a class template and an individual class is like the relationship between a class and an individual object. An individual class defines how a group of objects can be constructed, while a class template defines how a group of classes can be generated.

What are the two types of templates?

There are two types of templates in C++, function templates and class templates.


1 Answers

Version 3: Very Advanced (do not try that at home :D)

class Attribute {
private:
    struct Head {
        virtual ~Head() {}
        virtual void *copy() = 0;
        const type_info& type;
        Head(const type_info& type): type(type) {}
        void *data() { return this + 1; }
    };
    template <class T> struct THead: public Head {
        THead(): Head(typeid(T)) {}
        virtual ~THead() override { ((T*)data())->~T(); }
        virtual void *copy() override {
            return new(new(malloc(sizeof(Head) + sizeof(T)))
                THead() + 1) T(*(const T*)data()); }
    };
    void *data;
    Head *head() const { return (Head*)data - 1; }
    void *copy() const { return data ? head()->copy() : nullptr; }
public:
    Attribute(): data(nullptr) {}
    Attribute(const Attribute& src): data(src.copy()) {}
    Attribute(Attribute&& src): data(src.data) { src.data = nullptr; }
    template <class T> Attribute(const T& src): data(
      new(new(malloc(sizeof(Head) + sizeof(T))) THead<T>() + 1) T(src)) {}
    ~Attribute() {
        if(!data) return;
        Head* head = this->head();
        head->~Head(); free(head); }
    bool empty() const {
        return data == nullptr; }
    const type_info& type() const {
        assert(data);
        return ((Head*)data - 1)->type; }
    template <class T>
      T& value() {
        if (!data || type() != typeid(T))
            throw bad_cast();
        return *(T*)data; }
    template <class T>
      const T& value() const {
        if (!data || type() != typeid(T))
            throw bad_cast();
        return *(T*)data; }
    template <class T>
      void setValue(const T& it) {
        if(!data)
            data = new(new(malloc(sizeof(Head) + sizeof(T)))
                THead<T>() + 1) T(it);
        else {
            if (type() != typeid(T)) throw bad_cast();
            *(T*)data = it; }}
public:
    static void test_me() {
        vector<Attribute> list;
        list.push_back(Attribute(1));
        list.push_back(3.14);
        list.push_back(string("hello world"));
        list[1].value<double>() = 3.141592;
        list.push_back(Attribute());
        list[3].setValue(1.23f);
        for (auto& a : list) {
            cout << "type = " << a.type().name()
              << " value = ";
            if(a.type() == typeid(int)) cout << a.value<int>();
            else if (a.type() == typeid(double)) cout << a.value<double>();
            else if (a.type() == typeid(string)) cout << a.value<string>();
            else if (a.type() == typeid(float))  cout << a.value<float>();
            cout << endl;
        }
    }
};

Output:

type = i value = 1
type = d value = 3.14159
type = Ss value = hello world
type = f value = 1.23

Explanation:

Attribute contains data pointer, which is initializaed by this strange placement new: new(new(malloc(sizeof(Head) + sizeof(T))) THead<T>() + 1) T(src) which first allocates enough room for the Head (should be 2*sizeof(void*) which should be just fine for any allignment of any architecture) and the type itself, constructs THead<T>() (initializes pointer to virtual method table and type info) and moves the pointer after the head = at the place we want data. The object is then constructed by another placement new using copy-constructor (or move-constructor) T(src). struct Head has two virtual functions - destructor and copy() which is implemented in THead<T> and used in Attribute(const Attribute&) copy-constructor. Finally ~Attribute() destructor calls ~Head() virtual destructor and releases the memory (if data != nullptr).

Version 1: Simple Attribute List

#include <vector>
#include <typeinfo>
#include <iostream>
#include <cstdlib>
#include <new>

using namespace std;

class Attributes {
public:
    typedef pair<const type_info&,void*> value_type;
    typedef vector<value_type> vect;
    typedef vect::const_iterator const_iterator;
    template <class T>
      void add(const T& value) {
        data.push_back(pair<const type_info&,void*>(
          typeid(T), new(malloc(sizeof(T))) T(value))); }
    const_iterator begin() const {
        return data.begin(); }
    const_iterator end() const {
        return data.end(); }
private:
    vect data;
} attrs;

int main() {
    attrs.add(1);
    attrs.add(3.14);
    for (auto a : attrs) {
        cout << a.first.name() << " = ";
        if(a.first == typeid(int))
            cout << *(int*)a.second;
        else if(a.first == typeid(double))
            cout << *(double*)a.second;
        cout << endl;
    }
}

Output:

i = 1
d = 3.14

Version 2 (named attributes):

#include <string>
#include <unordered_map>
#include <typeinfo>
#include <iostream>
#include <cstdlib>
#include <new>

using namespace std;

class Attributes {
public:
    typedef pair<const type_info&,void*> value_type;
    typedef unordered_map<string,value_type> map;
    typedef map::const_iterator const_iterator;
    template <class T>
      bool add(const string& name, const T& value) {
        auto it = data.insert(make_pair(
          name, value_type(typeid(T), nullptr)));
        if (!it.second) return false;
        it.first->second.second = new(malloc(sizeof(T))) T(value);
        return true; }
    template <class T>
      const T& get(const string& name) const {
        auto it = data.at(name);
        if (it.first != typeid(T)) throw bad_cast();
        return *(T*)it.second; }
    const_iterator begin() const {
        return data.begin(); }
    const_iterator end() const {
        return data.end(); }
    void erase(const_iterator it) {
        free(it->second.second);
        data.erase(it); }
    bool remove(const string& name) {
        auto it = data.find(name);
        if (it == data.end()) return false;
        free(it->second.second);
        data.erase(it);
        return true; }
private:
    map data;
} attrs;

int main() {
    attrs.add("one", 1);
    attrs.add("pi", 3.14);
    cout << "pi = " << attrs.get<double>("pi") << endl;
    attrs.remove("pi");
    for (auto a : attrs) {
        cout << a.first << " = ";
        if(a.second.first == typeid(int))
            cout << *(int*)a.second.second;
        else if(a.second.first == typeid(double))
            cout << *(double*)a.second.second;
        cout << endl;
    }
}
like image 172
firda Avatar answered Oct 04 '22 01:10

firda