Question

I would like to extract the type of the member a member pointer points to.

template<someType myClass::*member>
void demo(myClass& instance, void* ptr) {
    instance.*member = *reinterpret_cast<someType*>(ptr);  // can the someType in this line be deduced from member?
}

I tried using decltype as suggested in the comments, however I have issues with this:

instance.*member= static_cast<decltype(instance.*member)>((*buffer)[offset]);

buffer is a std::shared_ptr<std::vector<uint8_t>>,
someType is uint32_t

I get the following error message:

error: invalid static_cast from type ‘__gnu_cxx::__alloc_traits >::value_type {aka unsigned char}’ to type ‘uint32_t& {aka unsigned int&}’

As far as I understand decltype(instance.*member) with member defined as uint32_t instance::*member yields a reference uint32_t& rather than uint32_t. I tried to pass instance by value and the error remains. I am aware of std::remove_reference however, I do not understand how the reference gets to be there in the first place.

A further improvement would be if I could extract the someType without a class instance. However I have no clue how to achieve this, while I can get the class without a pointer by having the std lib like:

template <T*>
struct removePointer {
  typedef T type;
}

I have no Idea how to write this in a form where I can get the someType part of the class, without knowing the class first. I could write something like the following however I would still have to pass the class naem and typename explicitly, is there a way to extract these automatically? Furthermore the following doe not compile in the first place (http://ideone.com/8VlKO4): #include using namespace std;

template <class C,typename T, T C::*v>
struct getPointerType {
typedef T type;
};

class Test {
int value;
};

int main() {
int Test::*member=nullptr;
cout << typeid(getPointerType<Test, int, decltype(member)>::type) << std::endl;
return 0;
}
Was it helpful?

Solution

Frankly, it's a bit hard to understand what you're trying to achieve, so I will focus on the updated part.

Clearly, you can not pass types (derived from decltype) as value arguments to the template. Moreover, you can not pass non constexpr values as template arguments (so you can not just stick the member variable into the template argument and expect it to compile).

However, you can rely on compiler to be able to deduce a correct function to call on non costexpr variable:

template <class C, typename T>
T getPointerType(T C::*v);

class Test {
    int value;
};

int main() {
    int Test::*member=nullptr;
    cout << typeid(decltype(member)).name() << std::endl;
    cout << typeid(decltype(getPointerType(member))).name() << std::endl;
    return 0;
}

The above will print:

M4Testi //int Test::*
i       //int

It is, of course, possible to "abuse" the template substitution even more:

template <typename M>
struct getPointerType {
   template <typename C, typename T>
   static T get_type(T C::*v);

   typedef decltype(get_type(static_cast<M>(nullptr))) type;
};

class Test {
    int value;
};

int main() {
    int Test::*member=nullptr;
    cout << typeid(getPointerType<decltype(member)>::type).name() << std::endl;
    return 0;
}

The output will be the expected "i".

OTHER TIPS

Here's a solution using template specialization technique :

#include <type_traits>

template <class T>
struct member_type_helper;

template <class C, class T>
struct member_type_helper<T C::*> { using type = T; };

template <class T>
struct member_type
    : member_type_helper<typename std::remove_cvref<T>::type> {};

// Helper type
template <class T>
using member_type_t = member_type<T>::type;

Example of usage :

#include <iostream>

struct Foo { int i; };

int main()
{
    auto                ptr1 = &Foo::i;
    auto const&         ptr2 = &Foo::i;
    volatile auto const ptr3 = &Foo::i; // let's go crazy

    // prints true true true
    std::cout << std::boolalpha
              << std::same_as<int, member_type_t<decltype(ptr1)>> << '\n'
              << std::same_as<int, member_type_t<decltype(ptr2)>> << '\n'
              << std::same_as<int, member_type_t<decltype(ptr3)>> << '\n';
}

I guess std::remove_cvref might be an overkill for most usage, but hey, it's free. If your compiler isn't >C++20 compliant, you could use std::remove_cv instead (> C++11).


Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top