【问题标题】:meta object using "static virtual" functions使用“静态虚拟”函数的元对象
【发布时间】:2016-09-01 14:59:19
【问题描述】:

我在一个项目中开发了某种元对象机制,以便将任何类型的类型名称和属性与对象相关联(参见结果here)。我有一个肮脏的代码在工作,我正在努力让它更干净。给定以下虚拟结构:

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;
}

当用这个 sn-p 测试代码时:

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

第一个原型对名称很有效,但是一旦声明了一个没有属性的对象,它的基础对象就会被复制。有人看到解决问题的简单方法吗?

额外问题:元函数的实现非常相似,有人提示我如何分解代码吗?

完整的实时示例可用here

【问题讨论】:

  • 问题发生在属性而不是类型?
  • @imreal 类型也会出现问题,但在我的用例中,类型总是会被声明。另一方面,属性是可选的
  • 好吧,只是get_type_nameget_type_names的名字有区别,而get_properties是一样的,可能调用错了。

标签: c++ c++14


【解决方案1】:

这是一个不完整的解决方案,可让您走上正轨。首先,一切都取决于遍历基地列表。因此,让我们将其纳入它自己的元函数中:

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&lt;A&gt;typelist&lt;A&gt;get_bases_t&lt;AAA&gt;typelist&lt;AAA, AA, A&gt;。现在,您的所有其他功能都只是基于遍历该类型列表并对其进行处理。所以让我们添加一个简单的方法来做到这一点:

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>);
}

【讨论】:

  • 我花了几个小时才完全理解代码,但它运行良好。 for_each 中使用的返回类型推导暗示了 C++14。对我来说没关系,我将标签添加到了 OP。非常感谢:)
猜你喜欢
  • 2016-12-14
  • 1970-01-01
  • 2014-08-04
  • 1970-01-01
  • 2013-03-26
  • 1970-01-01
  • 2017-07-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多