映射类的数据成员

时间:2018-06-22 13:26:02

标签: c++ c++17 data-members

我正在尝试设计一种数据结构,通过存储有关其成员的其他数据来增强/补充现有结构。

让我们说:

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的派生类,或者写成A以及其他一些东西。

我当时在考虑类可以生成地图的模板之类的东西。我可以屈服于为每个组件编写一个自定义类,但是我认为没有简单的方法来映射数据成员,因此在客户端看起来像getGuiMember(GuiA::x)。指向数据成员的指针包含成员原始类型。我认为不可能有像“类型擦除的成员指针”这样的东西可以用作MemberHandle类型。

我唯一想到的是每个组件的自定义enum,它将枚举数据成员并用作地图(在这种情况下为矢量)的键类型,但是看起来很多信息复制和维护。

是否存在一些允许映射数据成员的技术?

只要接口简单,我就不太在意实现的复杂性。我欢迎增强或模板魔术。我也不在乎附加数据访问的性能,它是多余的东西,但是普通类的使用不会受到影响,因此引入了无法优化的间接访问是不太受欢迎的。

编辑:请不要依赖GUI的例子。我只关心为每个成员存储一些其他数据而不将其与成员组成。

3 个答案:

答案 0 :(得分:2)

您可以使用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,每个类一个。每个成员通过其从其封闭类的开头的偏移量来标识。

答案 1 :(得分:0)

通常,您将宏用于此类目的。他们可以生成您想要的任何类型的代码/包装器,使您可以正常访问数据,还可以添加所需的东西。它不是很漂亮,但是可以。

有一些模板库可以在这里提供帮助,例如Boost.Fusion或Boost.Hana,但是,如果您没有使用它们的高级功能,则也可以在此处滚动自己的模板库(长时间编译后会附带这些功能)价格标签)。

此外,如果您可以专注于特定的GUI框架,那么它们将对此类事物有所支持。例如,Qt有自己的“元对象”编译器。

答案 2 :(得分:0)

您可以尝试使用此模板吗? 例如

template <typename T>
class GuiItem : public T {
    map<MemberHandle, GuiElement*> guiHandles;
}

GuiItem<A> guiA;
guiA.x = 123;
guiA.y = "y";
guiA.guiHandles[handle] = element;

我不确定我是否了解其他要求,因此这种方法可能对您不起作用。