A means to access the encapsulating object within a member

64 views
Skip to first unread message

Evan Teran

unread,
Oct 19, 2016, 3:02:16 AM10/19/16
to ISO C++ Standard - Future Proposals
I had an idea the other day, and wondered what people might think of it. It can be slightly hard for me to describe the concept, but I'll do my best. I expect that there is some aspects of this that I haven't thought of that may make it undesirable, but that's what discussion is for :-)

Consider some structs like the following:

struct A {    
   
struct B {
       
int get_member() const {
           
// hmm, how would we get access to A::member without some dirty tricks?
       
}
   
};
    B b
;
   
int member = 123;
};



I have found that there are some circumstances (which I'll give some at the end) where it would be useful for a member function of an object of type B, to be able to get access to the object that it is encapsulated it. Something along the lines of this:

struct A {    
   
struct B [[encapsulated]] {
       
int get_member() const {
            A
*p = encapsulator<A*>();
           
return p->123;
       
}
   
};
    B b
;
   
int member = 123;
};

The idea being that the [[encapsulated]] attribute would cause the compiler to automatically track a pointer to the encapsulating class, which is accessible via an encapsulator() method (or whatever we choose to call this thing), that returns a pointer to an object of type A, that this is encapsulated in. Essentially it could be shorthand for the following:

struct A {    
   
struct B {
       
int get_member() const {
           
auto p = reinterpret_cast<A *>(reinterpret_cast<char *>(this) - offsetof(A, b)); //probably works? definitely not reusable...
           
return p->123;
       
}
   
};
    B b
;
   
int member = 123;
};

Which of course is both hideous, and requires that I know the name of the member represented by "this" before hand, making generic usage not very likely (if possible). These things, at least in concept, the compiler can easily track if it isn't already. The way that I conceive of this is that similar to dynamic_cast, we could have a pointer version which returns nullptr on failure, and a reference version that throws an exception. It is also possible that the compiler could identify misuse at compile time and simply throw an error.

So, here's my primary motivating example:

Properties at a library level:

template <class T, class Obj, T (Obj::*G)() const, void (Obj::*S)(T)>
struct Property  {
   
Obj& operator=(const T &value) {
       
auto p = encapsulator<Obj*>();
       
(p->*S)(value);
       
return *p; // we return the encapsulating object for intuitive chaining
   
}
   
   
operator T () const {
       
auto p = encapsulator<Obj*>();
       
return (p->*G)();
   
}
};


struct A {
public:
   
Property<int, A, &A::getMember, &A::setMember> member;

public:
   
int getMember() const { return member_; } // imagine that these are complex routines
   
void setMember(int v) { member_ = v; }    // and we couldn't get the same behavior by making member_ public


private:
   
int member_;
};


int main() {
    A a
;
    a
.member = 123;
    std
::cout << a.member << "\n";
}


Is this idea simply crazy? Any merit to it? Anyway, thanks in advance for any feedback :-)
Evan

mihailn...@gmail.com

unread,
Oct 19, 2016, 5:44:00 AM10/19/16
to ISO C++ Standard - Future Proposals
But you can always store a pointer in the contained class to the containing class

template <class T, class Obj, T (Obj::*G)() const, void (Obj::*S)(T)>
struct Property 
{
    Obj& operator=(const T &value) {
        auto p = _that;

        (p->*S)(value);
        return *p; // we return the encapsulating object for intuitive chaining
    }
   
    operator T () const {
        auto p = _that;
        return (p->*G)();
    }
   
    Obj* _that;
};


struct A {

public:
    int getMember() const { return member_; } // imagine that these are complex routines
    void setMember(int v) { member_ = v; }    // and we couldn't get the same behavior by making member_ public

public:
    Property<int, A, &A::getMember, &A::setMember> member{this};
   
private:
    int member_;
};


This compiles and runs today.

Nicol Bolas

unread,
Oct 19, 2016, 9:30:00 AM10/19/16
to ISO C++ Standard - Future Proposals
I had some ideas about inner classes in C++ that do this.

Evan Teran

unread,
Oct 19, 2016, 11:02:08 AM10/19/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
yes, you can store it via a constructor explicitly, I hadn't thought about using the in class version of the constructor which is nice.
Reply all
Reply to author
Forward
0 new messages