编译期获取结构体信息
  ZXL8ALkrtBPG 2023年11月12日 29 0
c++

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

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月12日 0

暂无评论

推荐阅读
  gBkHYLY8jvYd   2023年11月19日   27   0   0 #includecic++
  ZXL8ALkrtBPG   2023年11月02日   66   0   0 c++
  ZXL8ALkrtBPG   2023年12月06日   21   0   0 字面量c++
  lh6O4DgR0ZQ8   2023年11月24日   17   0   0 cii++c++
  ZXL8ALkrtBPG   2023年11月12日   30   0   0 c++
  gBkHYLY8jvYd   2023年11月19日   25   0   0 十进制高精度c++
  ZXL8ALkrtBPG   2023年11月02日   49   0   0 c++
  ZXL8ALkrtBPG   2023年11月02日   52   0   0 c++
  gBkHYLY8jvYd   2023年12月11日   20   0   0 cic++最小值
  gBkHYLY8jvYd   2023年11月19日   22   0   0 测试点cic++
ZXL8ALkrtBPG