原文
最近在搞iguana.struct_pb动态反射功能时,遇见一个奇怪的问题.
struct person {std::string name;int64_t age;
};
REFLECTION(person, name, age);
struct persons {std::vector<person> list;
};
REFLECTION(persons, list);
//#1
static_assert(iguana::is_public_reflection_v<persons>);
#1代码是符合期望的,因为person和人们都是iguana的可反射对象,is_reflection_v的实现也比较简单:
template <typename T, typename = void>
struct is_public_reflection : std::false_type {};
template <typename T>
struct is_public_reflection<T, std::void_t<decltype(iguana_reflect_members(std::declval<T>()))>>: std::true_type {};
template <typename T>
constexpr bool is_public_reflection_v = is_public_reflection<T>::value;
void_t来检查T对象是否匹配iguana_reflect_members函数的调用,函数签名如下:
template<typename T>
inline static auto iguana_reflect_members(const T& t);
只要结构定义了REFLECTION宏,就一定匹配iguana_reflect_members调用函数,目前一切正常,但是给结构加一行代码后就有问题了.
struct persons {persons(std::vector<person> s) : list(std::move(s)) {}std::vector<person> list;//..
};
REFLECTION(persons, list);
给人们加了一个构造器,然后这样静态检查:
static_assert(iguana::is_public_reflection_v<std::vector<person>>);
惊讶的是通过了该检查,是的!为什么?因为隐式转换.
原来可按人们隐式转换std::vector,而人们则是个可反射对象,所以错误的通过了.
变通是避免隐式转换,在构造器前加explicit,但该方法治标不治本.
另外或将ADL函数的参数改成指针,std::vector是无法转换成人们指针的.但是仍不够,如果有个从人们继承的类,继承类指针可隐式转换为基类指针的,仍有漏洞.
最好是用个identity来包装一下ADL参数,这样,外面怎么隐式转换,也无法转换到identity了.
template<typename T>
struct identity{};
template<typename T>
inline static auto iguana_reflect_members(const identity<T>& t);
至此解决问题,隐式转换的坑还真不小,需要特别注意.
另外预告一下iguana.struct_pb的动态反射功能:
1,根据类型名创建对象实例
2,取对象所有的字段名
3,根据字段名和实例取置字段的值.