【问题标题】:Type map in C++C++ 中的类型映射
【发布时间】:2011-02-03 20:44:07
【问题描述】:

我将从一个会导致实际问题的上下文开始。

我正在尝试构建一个类,它的每个实例都将管理如何将数据绑定到对象中。 该类最好包含方法:

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)
};

上面代码中的对象只是一些标识符 - 在这一点上是 int 并且不包含任何数据或方法(这不应该改变)。上面使用的 DataType 基本上可以是任何类,但一般情况是这只是一个没有方法的结构。可用作 DataType 的完整列表在编译时是已知的,但不应对其进行编码,因为它经常更改。

我尝试实现的两个目标是: - 可维护性/速度 - 用户应该能够在不修改此代码的情况下添加新的 DataType 结构 - 速度 - 应该尽可能快:)

到目前为止,我最好的想法是创建一个容器类:

class ContainerBase;

template<typename DataType>
class DataTypeContainer : ContainerBase;

然后数据结构将类似于:

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

如何实现这一目标? boost::mpl::map 有帮助吗?

本质上应该可以这样做,因为所有 DataType 在编译时都是已知的。

【问题讨论】:

  • 您在寻找从类型到值的映射吗?您希望该地图是全局的,还是需要多个实例?
  • @Jeremiah Willcock - 是的,最好从类型映射到值,不幸的是我需要多个实例。
  • 我刚刚编辑了我的答案,为每种数据类型保留了一个从 void*Object 的全局映射(表示从类型到 void* 键的映射,表示映射实例到值)。不过,您可能需要考虑在一个包含所有成员列表的公共头文件中使用boost::fusion::map(甚至是struct);需要更新代码但将信息存储在一个地方。

标签: c++ templates boost types metaprogramming


【解决方案1】:
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;
    }
};

这段代码做了一些假设——比如 Object 类型的默认构造,并且它是可散列的。但是修改起来应该不会太难。如果您只希望每种类型的一个数据成员与特定对象相关联,那么获得定义的行为会非常棘手,因为您不能依赖 RTTI 为每种可能的 DataType 返回唯一名称。

【讨论】:

  • 我有点喜欢这种方法,除了每个函数签名中的 name 参数。它与如何使用这个类无关,坦率地说,我认为它破坏了封装。
  • @Bartlomiej:如果您有其他东西可以关联多个数据对象,或者只想要一个数据对象/对象,那么就放弃它。它不会破坏任何封装——它只是识别不同的数据元素。
  • 这不是我想要的,但我会接受它:)
【解决方案2】:

如果您想要从类型到值的 map 等效,并且它可以是全局的,您可以使用 static 成员:

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

T 的各种值加上适当的DataManager&lt;T&gt;::this_type_map 定义(但这些定义不需要在同一个源文件中)。之后,您可以使用(void*)(new int) 创建类型映射对象,使用delete (int*)(m) 释放它们,并使用DataManager&lt;T&gt;::this_type_map[m] 在对象中查找实例m 和类型T。当然,您可能希望将它们包装在函数或对象中。请注意,您可以使用与 Object 不同的类型作为 map 中的值类型,包括(使用模板特化)为类型映射中的每个键类型具有不同的值类型。

【讨论】:

    【解决方案3】:

    我认为您需要 std::tuple c++11 或使用 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;
    }
    

    按类型获取可以像这里一样实现: https://github.com/alekstheod/tnnlib/blob/master/src/Utilities/Utilities/MPL/Tuple.h

    【讨论】:

      【解决方案4】:

      您可以像这样使用类型之间的映射创建一个元组:

      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;
      };
      

      然后指定要转换的函数

      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");
      }
      

      然后指定映射

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

      最后你可以调用你的转换函数并将任何类型转换为任何其他类型;)

      在这里查看我的文章: https://cpptelepathy.wordpress.com/

      你需要这个文件 https://github.com/alekstheod/tnnlib/blob/master/src/Utilities/MPL/Tuple.h

      对于 util::get

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-09
        • 2013-07-28
        • 2019-09-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多