Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointer to member: works in GCC but not in VS2015

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++ JSON Serialization) and simplified it.

Here is how I proceed. I have a Property class:

template <typename Class, typename T>
struct Property {
    constexpr Property(T Class::* member, const char* name) : m_member(member), m_name(name) {}

    T Class::* m_member;
    const char* m_name;
};

m_member points to a specific member of Class

Let's say I want to define properties for a User class, I would like to be able to proceed like this, to be able to assign members a property name:

class User
{
public:
    int age;

    constexpr static auto properties = std::make_tuple(
        Property<User, int>(&User::age, "age")
    );
}

This code compiles and works correctly in GCC(http://coliru.stacked-crooked.com/a/276ac099068579fd) but not in Visual Studio 2015 Update 3. I get those errors:

main.cpp(19) : error C2327 : 'User::age' : is not a type name, static, or enumerator
main.cpp(19) : error C2065 : 'age' : undeclared identifier
main.cpp(20) : error C2672 : 'std::make_tuple' : no matching overloaded function found
main.cpp(20) : error C2119 : 'properties' : the type for 'auto' cannot be deduced from an empty initializer

Would there be a workaround to make it work in Visual Studio 2015 Update 3?

like image 467
Michael Avatar asked Aug 22 '16 18:08

Michael


3 Answers

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.

like image 60
Oktalist Avatar answered Oct 20 '22 17:10

Oktalist


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.

like image 3
Yakk - Adam Nevraumont Avatar answered Oct 20 '22 18:10

Yakk - Adam Nevraumont


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

like image 1
W.F. Avatar answered Oct 20 '22 18:10

W.F.