3

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

假设我们有:

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 的东西,这是一个例子。我只关心为每个成员存储一些额外的数据,而不是与成员一起组成。

4

3 回答 3

2

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

于 2018-06-22T14:24:01.150 回答
0

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

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

此外,如果您可以专注于特定的 GUI 框架,它们对这些东西有一些支持。例如,Qt 有自己的“元对象”编译器。

于 2018-06-22T13:38:05.267 回答
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;

我不确定我是否了解其他要求,因此这种方式可能不适合您。

于 2018-06-22T14:19:33.863 回答