7

I'd like to compose member pointers. Basically I have a main class with different member. How do I create a member pointer for the main class that would point to a member of a member of that class. I hope the code below is explains what I'm trying to do:

  struct SubUnit
  {
    int   value;
  };
  struct Unit
  {
    SubUnit sub_unit;
  };

  void Test()
  {
    SubUnit Unit::* ptr1 = &Unit::sub_unit; // WORKING
    int Unit::* ptr2 = &Unit::sub_unit::value; // NOT WORKING !
  }
0x26res
  • 11,925
  • 11
  • 54
  • 108

4 Answers4

5

It seems you have to do it in two phases:

SubUnit Unit::*pSub = &Unit::sub_unit;
int SubUnit::*pValue = &SubUnit::value;

Unit u;
int theVal = (u.*pSub).*pValue;
selalerer
  • 3,766
  • 2
  • 23
  • 33
  • 1
    +1 no need to suspect otherwise, [as you are correct.](http://ideone.com/ZNQvsJ) I honestly think this deserves a better look from the OP. – WhozCraig Jan 28 '14 at 16:14
  • @WhozCraig Thanks for doing all the hard work :-). I'll remove the uncertainty from my answer. – selalerer Jan 28 '14 at 16:18
  • No, thank *you*. I've never found the occasion to do it with *data members*, as the only need i've ever had was for member *functions*. It was educational. – WhozCraig Jan 28 '14 at 16:20
  • Right, but you cannot pass this to a function that expects a 'int SubUnit::*' as an input... – 0x26res Jan 28 '14 at 16:52
  • @jules can't pass *what* to a function? Ultimately they're pointers-to-members. You need something to go *with* those members (i.e. objects) at each stage. Perhaps update your question with a specific example of what you're saying this does *not* address. It is conceivable what you ultimately want is simply not possible. – WhozCraig Jan 28 '14 at 17:18
  • @WhozCraig: I want to store the pointer and bind them later to an object to extract the value. But the object comes later... – 0x26res Jan 28 '14 at 17:22
  • @jules Sure. but *which* object? If you're wanting to bind a `SubUnit::` member the object obviously needs to be a `SubUnit` or derivation therein. If the object is a `Unit` and the binding is a `SubUnit::` then either you need to expose the `SubUnit` from `Unit` or do what the code above does. Either way, I'm still unclear what the problem is. Sry. Code speaks volumes. There is no such thing as `int Unit::*` in your code, as there is no `int` members of that type. There is only `SubUnit Unit::*`, so that is what you have to work with. – WhozCraig Jan 28 '14 at 17:26
3

The use of sub-class is confusing here, since generally sub-class is used for inheritance, so let us talk about a data member: sub_unit is a data member of Unit.

And what you are asking for is not possible, Unit::* can only represent an offset in either Unit itself or one of its base-classes:

struct SubUnit { int value; };

struct Unit: SubUnit {};

int main() { int Unit::* p = &Unit::value; }
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Sorry if the question is not clear, but in my case I'd rather use Composition over Inheritance. – 0x26res Jan 28 '14 at 15:27
  • 2
    @jules: the question is perfectly clear, and so is the answer: *not possible* (and btw, you are right to prefer Composition over Inheritance). – Matthieu M. Jan 28 '14 at 15:29
  • Better still, let's call it a *(data) member*, since that's the standard terminology. – Mike Seymour Jan 28 '14 at 15:31
2

This seems like a duplicate of this question:
Nested data member pointer - not possible?

The crux of the accepted answer says:

A pointer-to-member can only be formed by an expression of type &qualified_id, which is not your case

The C++ standard says, in paragraph 8.3.3:
Pointer to Members:

In a declaration T D where D has the form

  nested-name-specifier * attribute-specifier-seqopt cv-qualifier-seqopt D1

and the nested-name-specifier denotes a class, and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is “derived-declarator-type-list cv qualifier- seq pointer to member of class nested-name-specifier of type T”. The optional attribute-specifier-seq (7.6.1) appertains to the pointer-to-member.

It turns out, there's a way to do what you want, that seems to be working on my current compiler (apple llvm 5.0). However, it is very implementation-dependant, and I would definitely recommend not using this solution in any kind of production code:

//added a few data members to make it non-trivial
struct SubUnit {
    double d;
    int   value;
};
struct Unit {
    bool b;
    char c;
    SubUnit sub_unit;
};

intptr_t suOffset = offsetof(Unit, sub_unit);
intptr_t intOffset = offsetof(SubUnit, value);
intptr_t totalOffset = suOffset + intOffset;

// there is no way to convert this offset directly in a
// pointer-to-member AFAIK, so we have to trick a bit
int Unit::* pui = nullptr;
auto puiAddr = &pui;
intptr_t* puiAddrAsIntPtrPtr = reinterpret_cast<intptr_t*>(puiAddr);
*puiAddrAsIntPtrPtr = totalOffset;

//pui should now "point to the right offset"
//let's test it
Unit u;
u.sub_unit.value = 123456;

int val = u .* pui;
std::cout << "val: " << val << std::endl;
Community
  • 1
  • 1
Martin J.
  • 5,028
  • 4
  • 24
  • 41
  • Well that's probably the right thing to do... Except that technically, in some cases, it actually is possible ;) – Martin J. Jan 29 '14 at 01:49
0

A workaround can be constructed with a lambda:

    struct SubUnit
    {
        int   value;
    };
    struct Unit
    {
        SubUnit sub_unit;
    };

    void Test()
    {
        auto  composed_mem_ptr = [](Unit & unit) -> int&{ return  unit.sub_unit.value; };
        Unit  unit0{};
        composed_mem_ptr(unit0) = 7;
    }
user1115339
  • 509
  • 3
  • 4