I\'m trying to implement a \"property\" system to convert C++ instances into JSON and vice versa. I took a part of the code from Guillaume Racicot\'s answer in this question (C+
My preferred workaround is just to replace the properties
member data with a properties
member function:
class User
{
public:
int age;
constexpr static auto properties() { return std::make_tuple(
Property<User, int>(&User::age, "age")
); }
};
This works because in the definition of a member function, the class is considered to be completely defined. It also has the desirable attribute that properties
doesn't need to be separately defined if odr-used.
If it is absolutely needed to properties be defined in User class maybe you could make use of helper templated constexpr function like:
#include <tuple>
template <typename Class, typename T>
struct Property {
constexpr Property(T Class::* const member) : m_member{ member } {}
T Class::* const m_member;
};
template <class T, class V>
constexpr Property<T, V> get_age_property() {
return Property<T, V>(&T::age);
}
class User
{
public:
int age;
constexpr static std::tuple<Property<User, int>> properties = std::make_tuple(
get_age_property<User, int>()
);
};
int main()
{
}
It seems to compile in webcompiler i.e. VC++ 19.00.23720.0
MSVC doesn't know enough about User
when it wants to calculate the type of properties
to know it has a member age
.
We can work around the problem.
template<class T>struct tag_t{constexpr tag_t(){};};
template<class T>constexpr tag_t<T> tag{};
template<class T>
using properties = decltype( get_properties( tag<T> ) );
class User
{
public:
int age;
};
constexpr auto get_properties(tag_t<User>) {
return std::make_tuple(
Property<User, int>(&User::age, "age")
);
}
In the JSON reflection code, simply replace std::decay_t<T>::properties
with get_properties( tag<std::decay_t<T>> )
.
This has a few advantages. First you can retrofit some classes you do not own or wish to modify with properties seamlessly. With careful namespace use and ADL enabling the point of call, you can even do so for (some) types within std
(with oublic members only; pair at the least).
Second it avoids possible ODR-use requirements on properties. Properties is now a constexpr
return value not some global data that may require storage.
Third it permits properties to be written out-of-line with the class definition like above, or inline as a friend
within the class, for maximium flexibility.