1

I am trying to create a class that can map strings to some arbitrary objects or even primitive types. To clarify, I am not talking of one single generic type, but different types, so the map could look something like this: ["speed": 3, "useNormal": false, "normal": Vec3()]

One approach I have attempted was to create a BaseObject struct and derive other class like IntObject and FloatObject from it. This way I was able to create a std::map<std::string, BaseObject*>. But this approach feels clunky and getters and setters for the map need a switch case for every single type.

Someone suggested to use bytes. Basically this means to serialize and deserialize the object into bytes arrays and therefore the map would look something like this std::map<std::string, byte*>. But with this approach the big hurdle to overcome is to somehow generate an object of a certain type from a byte array, which I don't know how to do.

Another suggestion was also to use c++17 variants. But that is too new to be widely supported.

I would appreciate any kind of input!

Haeri
  • 621
  • 1
  • 12
  • 27
  • When you take values out of the map, how would you know what type to use? I.e. is there any way to know the type of, say, `your_map["key"]`. – Al.G. Oct 01 '17 at 15:15
  • 1
    take a look at http://www.boost.org/doc/libs/1_61_0/doc/html/any/s02.html – Artemy Vysotsky Oct 01 '17 at 15:18
  • use unorderedmap? http://www.cplusplus.com/reference/unordered_map/unordered_map/ – Allanckw Oct 01 '17 at 15:22
  • 3
    `std::map>` –  Oct 01 '17 at 15:26
  • @Al.G. In case of byte* there is no way of knowing what type it is. SO I have made a struct that contains the type_index and the byte*. But even if I know the type, I don't know (know in a sense of "I don't know how to do that in c++") how I can create an object from type_index. – Haeri Oct 01 '17 at 15:34
  • @ArtemyVysotsky and manni66 This is exactly what I need. But I would prefer not to use boost, since I was able to successfully avoid it throughout my project. Is there no native c++ way of making this happen? – Haeri Oct 01 '17 at 15:40
  • https://stackoverflow.com/questions/9195078/does-c11-standard-provide-something-like-boostany – Artemy Vysotsky Oct 01 '17 at 15:48
  • @Haeri - Wait for C++17. Or just write your own type eraser. The ideas behind `boost::any` aren't rocket science. Just have a look at the source. – StoryTeller - Unslander Monica Oct 01 '17 at 15:54

2 Answers2

4

There is a general consent that std::map<std::string, std::any>> would be the best solution for this. But since I am trying to avoid boost and c++17, here is a pure c++ implementation an object oriented approach to this problem:

struct SuperObject {};

template<class T>
struct BaseObject: SuperObject {
    T _value;
    BaseObject(T value) : _value(value) {}
};

std::unordered_map<std::string, SuperObject*> objectMap;


template<typename T>
void setValue(std::string name, T value) {
    objectMap[name] = new BaseObject<T>(value);
}

template <typename T>
T getValue(const std::string& name) {
    return ((BaseObject<T>*)(objectMap[name]))->_value;
}

Please mind the huge memory leak when calling setValue()

Haeri
  • 621
  • 1
  • 12
  • 27
0

Using smart pointers instead of raw would fix the memory leaks. Here is a modified version of @Haeri

#include <iostream>
#include <memory>
#include <unordered_map>

struct SuperObject {};

template<class T>
struct BaseObject : SuperObject {
    T _value;
    BaseObject(T value) : _value(value) {
        std::cout << "Constructed" << std::endl;
    }
    ~BaseObject() {
        std::cout << "Destroyed" << std::endl;
    }
};

std::unordered_map<std::string, std::shared_ptr<SuperObject>> objectMap;


template<typename T>
void setValue(std::string name, T value) {
    objectMap[name] = std::make_shared< BaseObject<T>>(value);
}

template <typename T>
T getValue(const std::string& name) {
    return ((std::static_pointer_cast<BaseObject<T>>)(objectMap[name]))->_value;
}

int main()
{

    setValue<int>("a", 5);
    setValue<double>("b", 5.62342423);

    std::cout << getValue<int>("a") << std::endl;
    std::cout << getValue<double>("b") << std::endl;
}
Indri
  • 41
  • 1
  • 6