1

The code below gives the following compilation error:

main.cpp:5:48: error: invalid use of non-static data member 'id'
    static constexpr int Type::* mem_ptr_id = &id;
                                               ^~
main.cpp:5:34: error: default initialization of an object of const type 'int Type::*const'
    static constexpr int Type::* mem_ptr_id = &id;
                                 ^
                                            = nullptr
main.cpp:6:46: error: invalid use of non-static data member 'id'
    static constexpr auto mem_ptr_id_auto = &id;
                                             ^~
main.cpp:6:27: error: declaration of variable 'mem_ptr_id_auto' with deduced type 'const auto' requires an initializer
    static constexpr auto mem_ptr_id_auto = &id;
                          ^
4 errors generated.

which is kind of expected.

#include <iostream>

struct Type {
    int id;
    static constexpr int Type::* mem_ptr_id = &id; // &Type::id required
    static constexpr auto mem_ptr_id_auto = &id; // &Type::id required
};

int main() {
    Type test;
    test.*Type::mem_ptr_id = 5; // same as test.id = 5
    test.*Type::mem_ptr_id_auto = 5; // same as test.id = 5
    std::cout << test.id << std::endl; // expected: 5
    return 0;
}

I need a way to have static pointer-to-member variable of my class/structure without explicitly naming the type (class/struct) name. Any suggestions?

Note: In order to avoid auto to become int&, I did pointer-to-member wrapper:

template<typename T, class E>
struct Pointer2Member {
    using var_t = T;
    using entity_t = E;
    using mem_ptr_t = T E::*;

    T E::* data;

    constexpr Pointer2Member() {}

    constexpr Pointer2Member(T E::* val) : data(val) {}

    constexpr operator T E::* () {
        return data;
    }
};

template<auto ptr>
struct Pointer2MemberOf;

template<typename T, class E, T E::* ptr>
struct Pointer2MemberOf<ptr> : Pointer2Member<T, E> {
    constexpr Pointer2MemberOf() : Pointer2Member<T, E>(ptr) {}

    constexpr operator Pointer2Member<T, E>() {
        return *this;
    }
};

struct Type {
    int id;
    static constexpr auto mem_ptr_id = Pointer2MemberOf<&id>(); // Pointer2MemberOf<&Type::id>() required
};

but it gives same error:

main.cpp:34:58: error: invalid use of non-static data member 'id'
    static constexpr auto mem_ptr_id = Pointer2MemberOf<&id>();
                                                         ^~
main.cpp:34:27: error: declaration of variable 'mem_ptr_id' with deduced type 'const auto' requires an initializer
    static constexpr auto mem_ptr_id = Pointer2MemberOf<&id>();
                          ^
main.cpp:40:17: error: no member named 'mem_ptr_id_auto' in 'Type'
    test.*Type::mem_ptr_id_auto = 5; // same as test.id = 5
          ~~~~~~^
3 errors generated.

Note 2: Follow-up on the comment "What do you need this for":

Unfortunately, it is very complicated variadic template solution which I can't talk a lot about.

What I want to achieve is to create a template class Result<...> which can store custom member variables of different classes.

Result<User::id, User::username, Post::id> and Result<User, Post::id> should both be viable syntaxes and should have Result<User, Post::id>::get<PROPERTY> which should be able to have both Result<User, Post::id>::get<User> and Result<User, Post::id>::get<User::id> (yes, User, not Post).

Imagine Result<...> class in a library that will be used by beginner C++ programmers so I don't want to use the &User::id syntax as it might be too complicated to comprehend for them.

Also, the static member will be auto-generated via macros like

#define Member(TYPE, NAME) TYPE _##NAME; static constexpr auto NAME = Pointer2Member(&_##NAME)

struct User {
    Member(int, id);
    Member(std::string, username);
};

Result<User::id> result1;
result1.get<User::id>() = 5;

Result<User> result2;
result2.get<User::id>() = 6;
result2.get<User::username>() = "John";
  • 2
    why do you need it ? Whats wrong with mentioning the `Type` ? – 463035818_is_not_an_ai Nov 10 '22 at 09:54
  • `&id` is simply not a pointer to member. Its a pointer to `int`, or more correct, without an instance it doesnt make sense, without an instance there is no `id`. Are you actually asking how to get a pointer to member? I really dont understand the question. Looks like [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – 463035818_is_not_an_ai Nov 10 '22 at 09:56
  • If you do not want the obvious solution `// &Type::id required` you should explain why, because suggesting an alternative requires to know what else would be allowed – 463035818_is_not_an_ai Nov 10 '22 at 09:58
  • Doesn't say optional [Built-in address-of operator (2)](https://en.cppreference.com/w/cpp/language/operator_member_access#Built-in_address-of_operator) on the `class` – Richard Critten Nov 10 '22 at 09:58
  • Unfortunately, it is very complicated variadic template solution for a company. What I want to achieve is to create a template class ``Result`` which can store custom member variables to different classes. ``Result`` and ``Result`` should both be viable syntaxes and should have ``Result::get`` which should be able to have both ``Result::get`` and ``Result::get`` (yes, ``User``, not ``Post``). Imagine ``Result<...>`` class in a library that will be used by noobies. – Alex Tsvetanov Nov 10 '22 at 10:06
  • @RichardCritten ofc it doesn't say optional, that's why I am asking for a work-around. – Alex Tsvetanov Nov 10 '22 at 10:32
  • "it is very complicated variadic template solution which I can't talk a lot about.". Sorry, but you won't get any answers if we don't have more details. While we can all appreciate NDAs and confidentiality, talking about some magical code that might not even be possible for an app requirement which we do not know anything about, trying to achieve something that might not be possible in C++ and asking us to somehow know how, what and why to do it is just wishful thinking. Give us at least some part of the code regarding Result and a use case with the class name. – TheNomad Dec 05 '22 at 18:12

1 Answers1

0

Yes, but not in static. Generally you can replace the reference to class/struct name such as Test by std::remove_reference_t<decltype(*this)>, but only if this is available.

For example you can replace decltype(&Test::somefunc) by decltype(&std::remove_reference_t<decltype(*this)>::somefunc), which can be useful in templates.

If you modify your example to something like this, then it will also work:

#include <iostream>

struct Type {
    int id;
    int Type::* mem_ptr_id = &std::remove_reference_t<decltype(*this)>::id; // same as &Type::id
};

int main() {
    Type test;

    test.*test.mem_ptr_id = 5;

    int Type::* mem_ptr_id_local = &std::remove_reference_t<decltype(test)>::id;
    test.*mem_ptr_id_local = 5;

    auto mem_ptr_id_auto = &std::remove_reference_t<decltype(test)>::id;
    test.*mem_ptr_id_auto = 5;

    std::cout << test.id << std::endl; // expected: 5
    return 0;
}

As you can see none of those are static anymore (mem_ptr_id has default initialization), and access to mem_ptr_id is a bit awkward. But otherwise yes, it is possible in this way as long as this is available, and it can be very useful for templates/macros. Minimum C++14 required.


Also just for fun I will paste an example code snippet where it can be useful in practice:

template <typename T> struct getvfunc_wrapper_autotype;
template<typename T, typename RT, typename... Args>
struct getvfunc_wrapper_autotype<RT(T::*)(Args...)>
{
    typedef RT(__fastcall* FT)(PVOID, Args...);
    inline static FT get(const void* inst, size_t index, size_t offset = 0)
    {
        return reinterpret_cast<FT>(getvtable(inst, offset)[index]);
    }
};

#define DECLARE_VFUNC(index, return_type, fname, arg_types, arg_invokation) \
inline return_type fname arg_types \
{ \
    auto* _ = this; \
    return getvfunc_wrapper_autotype< decltype(&std::remove_reference_t<decltype(*this)>:: fname ) >::get(_, index) arg_invokation; \
}

class IVEngineClient
{
public:
    void* m_vtable;
    DECLARE_VFUNC(18, void, GetScreenSize, (int& wide, int& tall), (_, wide, tall)
    DECLARE_VFUNC(21, void, ClientCmd, (const char* szCmdString), (_, szCmdString))
};

As we can see, in the macro above it would be quite annoying to have to pass IVEngineClient as one of the parameters manually every time. And this trick does well to avoid that.

p0358
  • 139
  • 1
  • 8