代码之家  ›  专栏  ›  技术社区  ›  luk32

映射类的数据成员

  •  3
  • luk32  · 技术社区  · 6 年前

    我试图设计一个数据结构,它将通过存储关于它的成员的一些附加数据来增强/补充现有的数据结构。

    假设我们有:

    class A {
     int x;
     string y;
    };
    

    我们希望有一个与之相关联的GUI组件,所以数据成员有相应的GUI元素。我想将成员映射到各自的组件。有点像

    class GuiA {
     int x;
     string y;
     map<MemberHandle, GuiElement*> guiHandles;
    }
    

    我没有任何限制,但我希望结果可以很容易地转换为原始类型。

    我知道,我可以介绍一个模板,例如。 GuiElementMember 保存原始数据和 GuiElement 指针,并将类成员交换为其修饰的对应项,因此看起来像:

    class GuiA {
     GuiElementMember<int> x;
     GuiElementMember<string> y;
    }
    

    但我想避免它,因为它完全改变了对数据成员的访问模式并使其膨胀。也就是说,它的结果是数据成员与指针交错,这些指针不容易去掉。

    理想的情况是可以写 GuiA 作为派生类 A ,或作为 一个 还有一些额外的东西。

    我在想一个类可以生成映射的模板。我可以为每个组件编写一个自定义类,但我不认为有一种简单的方法可以映射数据成员,所以在客户端,它看起来像 getGuiMember(GuiA::x) . 指向数据成员的指针包含成员原始类型。我不认为像“类型删除指向成员的指针”这样的东西可以作为 MemberHandle 键入。

    我唯一想到的是一种习俗 enum 每个组件将枚举数据成员并作为映射(在本例中是向量)的键类型,但它似乎是大量的信息复制和维护。

    是否有某种技术允许映射数据成员?

    只要界面简单,我就不太关心实现的复杂性。我欢迎boost或模板魔术。我也不关心额外数据访问的性能,这是额外的东西,但是不应该影响普通类的使用,所以不太欢迎引入无法优化的间接寻址。

    编辑:请不要依赖于GUI,这是一个例子。我只关心为每个成员存储一些额外的数据,而不与成员组合。

    3 回复  |  直到 6 年前
        1
  •  2
  •   Maxim Egorushkin    6 年前

    你可以用 BOOST_FUSION_DEFINE_STRUCT 定义可以用 for_each 循环:

    #include <boost/fusion/include/define_struct.hpp>
    #include <boost/fusion/include/for_each.hpp>
    #include <unordered_map>
    #include <string>
    #include <cstdint>
    
    BOOST_FUSION_DEFINE_STRUCT(
        (demo), employee,
        (std::string, name)
        (int, age)
        )
    
    struct GuiElement;
    GuiElement* createGuiElement(char const* name);
    
    using Mapping = std::unordered_map<size_t, GuiElement*>;
    
    template<class T>
    Mapping create_mapping(T&& t) {
        Mapping mapping;
        boost::fusion::for_each(t, [&](auto& member) {
            auto offset = reinterpret_cast<uintptr_t>(&member) - reinterpret_cast<uintptr_t>(&t);
            mapping[offset];
        });
        return mapping;
    }
    
    template<class T, class M>
    GuiElement*& get_mapping_element(Mapping& mapping, T const& t, M const& member) {
        auto offset = reinterpret_cast<uintptr_t>(&member) - reinterpret_cast<uintptr_t>(&t);
        auto found = mapping.find(offset);
        if(found == mapping.end())
            std::abort();
        return found->second;
    }
    
    int main() {
        auto employee_mapping = create_mapping(demo::employee{});
    
        demo::employee e1;
        get_mapping_element(employee_mapping, e1, e1.name) = createGuiElement("name");
        get_mapping_element(employee_mapping, e1, e1.age) = createGuiElement("age");
    }
    

    代码中有一个 Mapping ,每班一个。每个成员都是通过其与封闭类开头的偏移量来标识的。

        2
  •  0
  •   srdjan.veljkovic    6 年前

    通常,您可以使用宏来实现这些目的。它们可以生成您想要的任何类型的代码/包装器,允许您对数据进行常规访问,还可以添加您想要/需要的内容。虽然不漂亮,但很管用。

    这里有一些模板库可以提供帮助,比如Boost.Fusion或Boost.Hana,但是,如果您不使用它们的高级功能(这些功能带有很长的编译价格标签),也可以在这里使用自己的模板库。

    另外,如果你能专注于一个特定的GUI框架,他们对这些东西也有一些支持。例如,Qt有自己的“元对象”编译器。

        3
  •  0
  •   Hitobat    6 年前

    你可以试试这个模板吗? 例如

    template <typename T>
    class GuiItem : public T {
        map<MemberHandle, GuiElement*> guiHandles;
    }
    
    GuiItem<A> guiA;
    guiA.x = 123;
    guiA.y = "y";
    guiA.guiHandles[handle] = element;
    

    我不确定我是否理解其他要求,所以这种方式可能不适合你。