我正在尝试设计一种数据结构,通过存储有关其成员的其他数据来增强/补充现有结构。
让我们说:
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的例子。我只关心为每个成员存储一些其他数据而不将其与成员组成。
答案 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;
我不确定我是否了解其他要求,因此这种方法可能对您不起作用。