Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type erasure on a pointer-to-member for a data type [closed]

Tags:

c++

c++17

Assuming a struct containing a number of variables:

struct A {
  int a;
  bool b;
  string c;
};

Supposedly, I need to map them out to store in a single collection

auto ptr_= reinterpret_cast<UniversalType>(&A::a);

What would be the universal type to cast to and from a pointer to any of the members in order to emulate type erasure?

Can uintptr_t be used for that?

PS. It's emebdded project with no RTTI support. So assumptions:

  1. It's an XY-problem
  2. You can use big 3-rd party library
  3. You can use wrappers with dynamic_cast

are wrong. It's the original problem existing with portability of pre-existing codebase. Dynamic dispatch is used in some cases and is used currently but it's manual (using function pointers) and ineffective. Compile-time dispatch is possible.

like image 312
Swift - Friday Pie Avatar asked Dec 13 '25 03:12

Swift - Friday Pie


1 Answers

You may simply invent your own type erasure wrapper, like this:

class MemberPtr {
private:
    struct AbstractMemberPtr {
        virtual ~AbstractMemberPtr() = default;
    };

    template<typename R, typename T>
    struct MemberPtrWrapper: AbstractMemberPtr {
        R T::* ptr;
        explicit MemberPtrWrapper(R T::* ptr) noexcept : ptr{ ptr } {}
        R T::* operator*() const noexcept { return ptr; }
    };

    std::unique_ptr<AbstractMemberPtr> m_ptr;

public:
    template<typename R, typename T>
    MemberPtr(R T::* memberPtr): m_ptr{ std::make_unique<MemberPtrWrapper<R,T>>(memberPtr) }
    {
    }

    template<typename R, typename T>
    R T::* RawPtr() const {
        if (const auto* const ptr = dynamic_cast<MemberPtrWrapper<R, T>*>(m_ptr.get())) {
            return *(*ptr);
        } else {
            return nullptr;
        }
    }
};

In a collection it can be used like this:

std::vector<MemberPtr> ptrs;
ptrs.push_back(MemberPtr{&A::a});
ptrs.push_back(MemberPtr{&A::c});

And then extracted like this:

if (const auto strPtr = ptrs.back().RawPtr<std::string, A>()) {
    A a;
    a.*(strPtr) = "some string";
}

Alternatively (if you don't need to deduce all members automagically), you can simply use std::variant:

using MembersVariant = std::variant<decltype(&A::a), decltype(&A::b), decltype(&A::c)>;
std::vector<MembersVariant> ptrs;
ptrs.emplace_back(&A::a);
ptrs.emplace_back(&A::b);

A a;
// Visitor is a helper utility class, which looks as follows:
// template<typename... T> struct Visitor : T... { using T::operator()...; };
std::visit(Visitor{
    [&a](std::string A::* memberPtr) {
        a.*memberPtr = "some string";
    },
    [&a](bool A::* memberPtr) {
        a.*memberPtr = true;
    },
    [&a](int A::* memberPtr) {
        a.*memberPtr = 2;
    }
}, ptrs.back());
like image 79
The Dreams Wind Avatar answered Dec 15 '25 16:12

The Dreams Wind



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!