使用 "static virtual" 函数的元对象
meta object using "static virtual" functions
我在一个项目中开发了某种元对象机制,以便将任何类型的类型名称和属性关联到对象(请参阅此处的结果)。我有一个肮脏的代码工作,我正试图使它更干净。给定以下虚拟结构:
struct A
{
using Self = A;
using Base = void;
static std::string get_type_name(){ return { "A" }; }
static std::vector<int> get_properties(){ return { 0 }; }
};
#define SELF(class_name)
using Base = Self;
using Self = class_name;
struct AA : A
{
SELF(AA)
static std::string get_type_name() { return { "AA" }; }
};
struct AAA : AA
{
SELF(AAA)
static std::string get_type_name(){ return { "AAA" }; }
static std::vector<int> get_properties(){ return { 2, 1 }; }
};
我最终用下面的代码来获取对象在其层次结构中的类型名称:
// when the type has no Base member:
template<
typename T,
typename std::enable_if<std::is_same<typename T::Base, void>::value>::type* = nullptr
>
typename std::vector<decltype(T::Self::get_type_name())> get_type_names()
{
return { T::Self::get_type_name() };
}
// when the type has a Base member:
template<
typename T,
typename std::enable_if<!std::is_same<typename T::Base, void>::value>::type* = nullptr
>
typename std::vector<decltype(T::Self::get_type_name())> get_type_names()
{
auto data = get_type_names<typename T::Base>();
data.insert(data.begin(), T::Self::get_type_name());
return data;
}
和类似的属性:
template<
typename T,
typename std::enable_if<std::is_same<typename T::Base, void>::value>::type* = nullptr
>
decltype(T::Self::get_properties()) get_properties()
{
return { T::Self::get_properties() };
}
template<
typename T,
typename std::enable_if<!std::is_same<typename T::Base, void>::value>::type* = nullptr
>
decltype(T::Self::get_properties()) get_properties()
{
auto data = get_properties<typename T::Base>();
auto self_data = T::Self::get_properties();
data.insert(data.begin(), self_data.begin(), self_data.end());
return data;
}
当使用以下代码片段测试代码时:
template<typename T>
void print()
{
std::cout << T::get_type_name() << std::endl << "thas types:" << std::endl;
for(auto type_name : get_type_names<T>())
{
std::cout << "tt" << type_name << std::endl;
}
std::cout << "thas properties:" << std::endl;
for(auto property : get_properties<T>())
{
std::cout << "tt" << property << std::endl;
}
}
int main()
{
print<A>();
print<AA>();
print<AAA>();
return 0;
}
得到以下输出:
A
has types:
A
has properties:
0
AA
has types:
AA
A
has properties:
0
0
AAA
has types:
AAA
AA
A
has properties:
2
1
0
0
第一个原型对于名称工作得很好,但是一旦对象声明没有属性,它的基类中的属性就会被复制。有人发现了解决问题的简单方法吗?
附加问题:元函数的实现非常相似,有人能提示我如何分解代码吗?这里有一个完整的实例
这里有一个不完整的解决方案,它将使您走上正确的轨道。首先,一切都取决于遍历垒表。那么让我们把它分解成它自己的元函数:
template <class... > struct typelist { };
template <class T, class... Bases>
struct get_bases
: get_bases<typename T::Base, Bases..., T>
{ };
template <class... Bases>
struct get_bases<void, Bases...>
{
using type = typelist<Bases...>;
};
template <class T>
using get_bases_t = typename get_bases<T>::type;
现在,get_bases_t<A>
是typelist<A>
, get_bases_t<AAA>
是typelist<AAA, AA, A>
。现在,你所有的其他函数都是基于遍历那个类型列表并对它做一些事情。让我们添加一个简单的方法:
template <class T> struct tag { using type = T; };
template <class... Ts>
auto for_each(typelist<Ts...>) {
return [](auto&& f){
using swallow = int[];
(void)swallow{0,
(void(f(tag<Ts>{})), 0)...
};
};
}
现在,获得所有属性的问题是将所有碱基转换为相应的get_properties()
函数,然后调用所有唯一的。您可能会在编译时挑选出独特的函数,但这也可以:
template <class T>
std::vector<int> get_properties() {
using bases = get_bases_t<T>;
using F = std::vector<int>(*)();
F last = nullptr;
std::vector<int> props;
for_each(bases{})([&](auto t){
using B = typename decltype(t)::type;
F cur = B::get_properties;
if (cur != last) {
auto next = cur();
props.insert(props.end(), next.begin(), next.end());
last = cur;
}
});
return props;
}
这种方法对于获取类型名称也非常直接:
template <class T>
std::vector<std::string> get_type_names()
{
using bases = get_bases_t<T>;
std::vector<std::string> names;
names.reserve(len_v<bases>); // TODO
for_each(bases{})([&](auto t){
names.push_back(decltype(t)::get_type_name());
});
return names;
}
或者,在这种情况下,简单地:
template <class... Bs>
std::vector<std::string> get_type_names_impl(typelist<Bs...>) {
return {Bs::get_type_name()...};
}
template <class T>
std::vector<std::string> get_type_names()
{
return get_type_names_impl(get_bases_t<T>);
}
相关文章:
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- CMake-按正确顺序将项目与C运行时对象文件链接
- 空基优化子对象的地址
- 将对象数组的引用传递给函数
- 你能重载对象变量名本身返回的内容吗
- C++使用整数的压缩数组初始化对象
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 将对象移动到std::shared_ptr
- 代理对象的常量正确性
- 提升 ASIO 无法识别计时器对象
- 将Ref对象作为类成员
- 将包含C样式数组的对象初始化为成员变量(C++)
- 如何返回一个类的两个对象相加的结果
- 为什么Virtual继承2类会增加对象大小
- 不能在没有对象的情况下调用成员函数'virtual void ThreadBase::doTask()'
- C++中“virtual”关键字在对象而不是指针上的行为
- 为了模拟对象,我们是否应该声明所有成员函数 virtual(C++)
- Virtual 关键字使派生对象中的方法无法访问
- 使用 "static virtual" 函数的元对象