0
点赞
收藏
分享

微信扫一扫

编译期获取结构体信息

棒锤_45f2 2023-10-05 阅读 28

C++标准当前并没有提供反射能力,在yaLanTingLibs中提供了一种反射机制,结合源码对库中使用的编程手法进行简单分析,包括编译期获取结构体成员字段的个数,各个字段的类型;读取结构体字段;

编译期获取结构体字段个数

struct Test {
    int32_t v1; 
    int32_t v2; 
    int32_t v3; 
    int32_t v4; 
};

Test t {1, 2, 3, 4}; // 完成聚合初始化

Test t {1, 2, 3, 4, 5}; // 编译出错,error: too many initializers for ‘Test’

当初始化个数超过结构体字段个数时则会报错,正是利用这个约束在编译期得到字段个数

template <typename T, typename param, typename = std::void_t<>, typename ...Args>
struct IsConstructable : std::false_type {};
  
template <typename T, typename param, typename ...Args>
struct IsConstructable<T, param, std::void_t<decltype(T{ Args{}..., param{}})>, Args...> : std::true_type {};

template<typename T, typename ...Args>
constexpr size_t MemberCount()
{
    if constexpr (IsConstructable<T, int32_t, void, Args...>::value) {
        return MemberCount<T, Args..., int32_t>();
    } else {
        return sizeof...(Args);
    }
}

static_assert(MemberCount<Test>() == 4, "is not equal");

这里结构体中都会int32_t类型,如何支持不同类型?库中引入了AnyType

struct AnyType {
    template<typename T>
    operator T() {
        return T {};
    }
};

template <typename T, typename param, typename = std::void_t<>, typename ...Args>
struct IsConstructable : std::false_type {};
  
template <typename T, typename param, typename ...Args>
struct IsConstructable<T, param, std::void_t<decltype(T{ Args{}..., param{}})>, Args...> : std::true_type {};

template<typename T, typename ...Args>
constexpr size_t MemberCount()
{
    if constexpr (IsConstructable<T, AnyType, void, Args...>::value) {
        return MemberCount<T, Args..., AnyType>();
    } else {
        return sizeof...(Args);
    }
}

但对于非aggregate类型则不支持,可以通过std::is_aggregate判断;

编译期获取结构体字段类型

struct Test {
    int32_t v1;
    int32_t v2;
    int32_t v3;
    std::string v4;
};

Test t{1, 2, 3, "hello world"};
auto &&[v1, v2, v3, v4] = t;
DBG_LOG("%d %d %d %s", v1, v2, v3, v4.c_str());

通过结构化绑定可以获取到结构体成员字段,由此可得到:

template<typename Object, typename Visitor>
constexpr decltype(auto) VisitMember(Object&& obj, Visitor&& vis)
{
    constexpr auto count = MemberCount<std::decay_t<decltype(obj)>>();
    if constexpr (count == 0) {
        return vis();
    } else if constexpr (count == 1) {
        auto &&[v1] = obj;
        return vis(v1);
    } else if constexpr (count == 2) {
        auto &&[v1, v2] = obj;
        return vis(v1, v2);
    } else if constexpr (count == 3) {
        auto &&[v1, v2, v3] = obj;
        return vis(v1, v2, v3);
    } else if constexpr (count == 4) {
        auto &&[v1, v2, v3, v4] = obj;
        return vis(v1, v2, v3, v4);
    } else if constexpr (count == 5) {
        auto &&[v1, v2, v3, v4, v5] = obj;
        return vis(v1, v2, v3, v4, v5);
    } else if constexpr (count == 6) {
        auto &&[v1, v2, v3, v4, v5, v6] = obj;
        return vis(v1, v2, v3, v4, v5, v6);
    } else if constexpr (count == 7) {
        auto &&[v1, v2, v3, v4, v5, v6, v7] = obj;
        return vis(v1, v2, v3, v4, v5, v6, v7);
    } else if constexpr (count == 8) {
        auto &&[v1, v2, v3, v4, v5, v6, v7, v8] = obj;
        return vis(v1, v2, v3, v4, v5, v6, v7, v8);
    } else if constexpr (count == 9) {
        auto &&[v1, v2, v3, v4, v5, v6, v7, v8, v9] = obj;
        return vis(v1, v2, v3, v4, v5, v6, v7, v8, v9);
    } else if constexpr (count == 10) {
        auto &&[v1, v2, v3, v4, v5, v6, v7, v8, v9, v10] = obj;
        return vis(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10);
    }   
}

此处举例使用10个字段个数进行说明,通过tuple既可以获取字段类型:

using Types = decltype(VisitMember(Test {}, [](auto&&... args) constexpr {
        return std::tuple<std::decay_t<decltype(args)>...> {}; })
    );  
    std::cout << "T type:" << type_id_with_cvr<Types>().pretty_name() << std::endl;

输出为:

T type:std::tuple<int, int, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >

读取结构体字段

VisitMember(Test{1, 2, 3, "hello world"}, [](auto&&... args) {
        ((std::cout << args << " "), ...);
    });

参考资料

【1】https://github.com/alibaba/yalantinglibs

举报

相关推荐

0 条评论