使用“静态虚拟”的元对象。功能 [英] meta object using "static virtual" functions
问题描述
我在项目中开发了某种元对象机制,以便将类型名称和任何类型的属性与对象相关联(请参见结果此处)。我有一个肮脏的代码,正在尝试使它更整洁。给定以下虚拟结构:
I developed some kind of meta object mechanism in a project, in order to associate type names and properties of any type to an object (see the result here). I have a dirty code working and I'm trying to make it cleaner. Given the following dummy structures:
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 }; }
};
我最终得到了这段代码,以获取对象在其层次结构中的类型名称:
I ended up with this code to get the type names of an object across its hierarchy:
// 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;
}
使用此代码段测试代码时:
When testing the code with this snippet:
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 << "\t\t" << type_name << std::endl;
}
std::cout << "\thas properties:" << std::endl;
for(auto property : get_properties<T>())
{
std::cout << "\t\t" << 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
第一个原型对于名称来说效果很好,但是一旦声明了一个没有属性的对象,它的基础对象就会被复制。
This first prototype works well for the names, but as soon as an object is declared without properties, the ones from its base are duplicated. Does somebody see a simple way to correct the problem?
奖励问题:元函数的实现非常相似,有人暗示我如何分解代码吗? ?
Bonus question: implementations for the meta functions are very similar, does somebody have an hint on how I could factorize the code?
推荐答案
这是一个不完整的解决方案,它将使您走上正确的道路。首先,一切都取决于步行基地的名单。因此,让我们将此因素纳入其自身的元函数中:
Here's an incomplete solution that will put you on the right track. First, everything is dependent on walking the list of the bases. So let's factor that into its own metafunction:
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>
。现在,您所有其他功能都只是基于向下键入列表并对其进行处理。因此,让我们添加一个简单的方法来做到这一点:
So now, get_bases_t<A>
is typelist<A>
and get_bases_t<AAA>
is typelist<AAA, AA, A>
. Now, all of your other functions are just based on walking down that typelist and doing stuff to it. So let's add an easy way to do that:
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()
函数,然后调用所有唯一的函数。您也许可以在编译时挑选出唯一的函数,但这也可以:
Now, getting all the properties is a matter of turning all the bases into their corresponding get_properties()
functions, and then calling all the unique ones. You could probably pick out the unique functions at compile-time, but this works too:
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;
}
这种方法对于获取类型名称也非常简单:
This approach is very straightforward for getting your type names too:
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;
}
或者,在这种情况下,只需:
Or, in this case, simply:
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>);
}
这篇关于使用“静态虚拟”的元对象。功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!