简体   繁体   中英

Type map in C++

I'll begin with a context that will lead to the actual question.

I'm trying to build a class whose every instance will manage how data is tied together into objects. The class should preferably contain methods:

class DataManager {
  Object CreateObject();
  void DestoryObject();

  template<typename DataType>
  DataType* AddDataToObject(Object o)

  template<typename DataType>
  DataType* GetDataForObject(Object o)

  template<typename DataType>
  void RemoveDataFromObject(Object o)
};

Object in the code above is just some identifier - int at this point and does not contain any data or methods (this should not change). DataType used above can be basically any class, however the general situation is that this is just a struct with no methods. The complete list of things that can be used as DataType is known at compile time but should not be encoded as it changes quite often.

The two goals I try to achieve are: - Maintainability/Speed - The user should be able to add new DataType structures without modifying this code - Speed - should be as fast as possible :)

Now the best thing idea I had so far is to make a container classes:

class ContainerBase;

template<typename DataType>
class DataTypeContainer : ContainerBase;

The data structure then would be something like:

map< DataTypeType, map< Object, ContainerBase* > >

Sow how can one achieve this? Would boost::mpl::map help and how?

In essence this should be possible to do since all DataType's are known at compile time.

I think that you need the std::tuple c++11 or playing with boost::tuple for c++ 03

template<typename T>
struct Entry{
  T t;
};

int main(int argc, char **argv) {
  std::tuple< int, float, double, Entry<int> > objects;
  std::get<0>(objects) = 3;
  std::get<3>(objects).t = 5;

  //
  utils::get< Entry<int> >(object).t = 5;
  return 0;
}

get by type can be implemented like here: https://github.com/alekstheod/tnnlib/blob/master/src/Utilities/Utilities/MPL/Tuple.h

You could create a tuple with the mapping between your types like that:

template < std::size_t sz, typename... Types >
struct TypeMap {
    TypeMap (std::array< std::tuple< Types... >, sz > m) : mapping (m) {
    }

    std::array< std::tuple< Types... >, sz > mapping;
};

Then specify a function to convert

template < typename To, typename From, std::size_t sz, typename... T >
To convert (From from, TypeMap< sz, T... > m) {
    for (auto entry : m.mapping) {
        if (utils::get< From > (entry) == from) {
            return utils::get< To > (entry); //Tricky part here
        }
    }

    throw std::logic_error ("No entry in the typemap");
}

Then specify the mapping

const auto map = TypeMap{{std::make_tuple (red, "red", 1), 
                          std::make_tuple (green, "green", 2),
                          std::make_tuple (blue, "blue", 3)}};

and finally you can call your convert function and convert any type to any other type ;)

See my article here: https://cpptelepathy.wordpress.com/

You would need this file https://github.com/alekstheod/tnnlib/blob/master/src/Utilities/MPL/Tuple.h

for util::get

class DataManager {
    struct internal_base { virtual ~internal_base() {} };
    template<typename T> struct internal_data : public internal_base {
        T t;
    };
    boost::unordered_map<Object, boost::unordered_map<std::string, boost::unique_ptr<internal_base>>> data;
public:
    Object CreateObject() { return Object(); }
    void DestroyObject(Object o) { data.erase(o); }

    template<typename DataType> DataType* AddDataToObject(Object o, std::string name) {
        internal_data<T>* ptr = new internal_data<T>();
        data[o][name] = ptr;
        return &ptr->t;
    }

    template<typename DataType> DataType* GetDataForObject(Object o, std::string name) {
        internal_base* ptr = data[o][name].get();
        if (internal_data<DataType>* dptr = dynamic_cast<internal_data<DataType>*>(ptr)) {
            return &dptr->t;
        else
            return 0;
    }

    void RemoveDataFromObject(Object o, std::string name) {
        data[o][name] = 0;
    }
};

This code makes some assumptions- like default-construction of Object type, and that it is hashable. But it shouldn't be too difficult to modify. It would be substantially trickier to get defined behaviour if you want just one data member of each type associated with a specific Object, because you can't rely on RTTI to return unique names for each possible DataType.

If you want the equivalent of a map from types to values, and it can be global, you can use static members:

template <typename T>
struct DataManager {
  static std::map<void*, Object> this_type_map;
};

plus appropriate definitions of DataManager<T>::this_type_map for the various values of T (but those definitions don't need to be in the same source file). After that, you can create type map objects using (void*)(new int) , free them using delete (int*)(m) , and look up the object for an instance m and a type T using DataManager<T>::this_type_map[m] . You would want to wrap these in functions or objects, of course. Note that you can have a different type than Object as the value type in the map , including (using template specializations) having a different value type for each key type in the type map.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM