pushing the limits of use-before-declaration within a class

Discussion in 'C++' started by Walt Karas, Sep 3, 2004.

  1. Walt Karas

    Walt Karas Guest

    Is this code legal under the standard?

    struct A
    {
    int i;

    struct C
    {
    unsigned offset_of_c(void) { return((unsigned) &(((A *) 0)->c)); }

    A * ptr_to_containing_A_instance(void)
    { return((A *) (((char *) this) - offset_of_c())); }

    void set(void)
    { ptr_to_containing_A_instance()->i = 10; }
    }
    c;

    };

    #include <stdio.h>

    int main(void)
    {
    A a;

    a.c.set();

    // Should print 10.
    printf("%d\n", a.i);

    return(0);
    }

    The version of GCC I am using compiles and links it,
    and the resulting executable produces the expected
    output when run.

    Is there a less ugly way to implement a class that
    is not only a member class but whose instance must
    be in the containing class and access the containing
    class's members?

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    Walt Karas, Sep 3, 2004
    #1
    1. Advertising

  2. (Walt Karas) writes:

    > Is this code legal under the standard?
    >
    > struct A
    > {
    > int i;
    >
    > struct C
    > {
    > unsigned offset_of_c(void) { return((unsigned) &(((A *) 0)->c)); }
    >
    > A * ptr_to_containing_A_instance(void)
    > { return((A *) (((char *) this) - offset_of_c())); }
    >
    > void set(void)
    > { ptr_to_containing_A_instance()->i = 10; }
    > }
    > c;
    >
    > };
    >
    > #include <stdio.h>
    >
    > int main(void)
    > {
    > A a;
    >
    > a.c.set();
    >
    > // Should print 10.
    > printf("%d\n", a.i);
    >
    > return(0);
    > }

    [...]
    > Is there a less ugly way to implement a class that
    > is not only a member class but whose instance must
    > be in the containing class and access the containing
    > class's members?



    struct A {

    struct C {
    friend class A;
    void set() { a.i = 10; }
    private:
    C(A& anA): a(anA) {}
    A& a;
    };

    A(): c(*this) {}

    C c;
    int i;
    };

    HTH.

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    Sergei Organov, Sep 4, 2004
    #2
    1. Advertising

  3. Walt Karas

    Matthew Hall Guest

    Walt Karas wrote:
    > Is this code legal under the standard?
    >
    > struct A
    > {
    > int i;
    >
    > struct C
    > {
    > unsigned offset_of_c(void) { return((unsigned) &(((A *) 0)->c)); }
    >
    > A * ptr_to_containing_A_instance(void)
    > { return((A *) (((char *) this) - offset_of_c())); }
    >
    > void set(void)
    > { ptr_to_containing_A_instance()->i = 10; }
    > }
    > c;

    (From my experience, 'struct C {...} c; is not very idiomatic C++. It is
    more common to split this into two statements - one defining the type
    and another declaring the member variable. See below for example)

    >
    > };
    >
    > #include <stdio.h>
    >
    > int main(void)
    > {
    > A a;
    >
    > a.c.set();
    >
    > // Should print 10.
    > printf("%d\n", a.i);
    >
    > return(0);
    > }
    >
    > The version of GCC I am using compiles and links it,
    > and the resulting executable produces the expected
    > output when run.
    >
    > Is there a less ugly way to implement a class that
    > is not only a member class but whose instance must
    > be in the containing class and access the containing
    > class's members?


    First, without questioning the design:
    Your code relies on the layout of your class. Since A is a POD in this
    case, this is OK (well, you use C-style casts and I/O). However, it is
    error prone and is not guaranteed to work once you add in virtual
    methods or multiple inheritence.

    My preferred way would use constructors, and go something like this:

    struct A
    {
    int i;

    class C
    {
    public:
    C(A* parent) : m_parent(parent) {}
    void set(void) { m_parent->i = 10;}
    A* m_parent;
    };
    C c;

    A() : i(0), c(this){}
    };

    Nice, portable code. (though not very good at data hiding)

    //Variants:
    // if A is a class with private data that c should access, add
    // 'friend class C' to A's declaration
    // Use a reference to A as opposed to a pointer.

    Granted, this does add some overhead in both execution speed of the
    construction, as well as in size, so if you need to create a billion of
    these things, you might want to take a different approach. In that case
    you might want to re-evaluate your use of a nested class/struct.

    I realize that you have probably provided a minimal example, but why not
    just use move the 'set' method into A, and use 'a.set()' instead of
    'a.c.set()'.
    In fact, since A is a struct, why not create a free function set:
    void set(A& a) { a.i=10;}
    (Though the name 'set' should probably be changed, so as not to confuse
    humans and/or compilers who might first think of std::set)

    -matt

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    Matthew Hall, Sep 4, 2004
    #3
  4. Walt Karas

    Conrad Weyns Guest

    "Walt Karas" <> wrote in message
    news:...
    > Is this code legal under the standard?
    >
    > struct A
    > {
    > int i;
    >
    > struct C
    > {
    > unsigned offset_of_c(void) { return((unsigned) &(((A *)

    0)->c)); }
    >
    > A * ptr_to_containing_A_instance(void)
    > { return((A *) (((char *) this) - offset_of_c())); }
    >
    > void set(void)
    > { ptr_to_containing_A_instance()->i = 10; }
    > }
    > c;
    >
    > };
    >
    > #include <stdio.h>
    >
    > int main(void)
    > {
    > A a;
    >
    > a.c.set();
    >
    > // Should print 10.
    > printf("%d\n", a.i);
    >
    > return(0);
    > }
    >
    > The version of GCC I am using compiles and links it,
    > and the resulting executable produces the expected
    > output when run.
    >
    > Is there a less ugly way to implement a class that
    > is not only a member class but whose instance must
    > be in the containing class and access the containing
    > class's members?



    struct A
    {
    int i;

    struct C
    {
    A& owner;

    C(A& i_Owner) :
    owner(i_Owner)
    {
    }
    void set()
    {
    owner.i = 10;
    }

    } c;

    A() : c(*this) {}
    };

    Regards,
    Conrad Weyns


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    Conrad Weyns, Sep 4, 2004
    #4
  5. Walt Karas

    Conrad Weyns Guest

    "Matthew Hall" <> wrote in message
    news:cha6vp$718$...
    > Walt Karas wrote:

    [...]
    >
    > struct A
    > {
    > int i;
    >
    > class C
    > {
    > public:
    > C(A* parent) : m_parent(parent) {}
    > void set(void) { m_parent->i = 10;}
    > A* m_parent;
    > };
    > C c;
    >
    > A() : i(0), c(this){}
    > };
    >
    > Nice, portable code. (though not very good at data hiding)
    >
    > //Variants:
    > // if A is a class with private data that c should access, add
    > // 'friend class C' to A's declaration


    Hi,
    I practiced this friendship for years, but I was recently put right in a
    thread on comp.std.c++.
    The threads subject was "Private Methodes declared outside of the class"
    Evidently, the friend declaration is a misconseption. Here is an snippet
    from Jim Hyslop's respons:

    > Recapping the code in question:
    > > class A
    > > {
    > > private:
    > > int x;
    > >
    > > class Impl;
    > > friend class Impl;

    >
    > It is not possible to write a well-formed declaration that allows
    > A::Impl access to A's private members. 11.4 para 1 states "A friend of a
    > class is a function or class that is not a member of the class ...."
    > Impl *is* a member of A, therefore A cannot grant it friendship.
    >
    > Obviously, that's a mistake in the standard. Some compilers get around
    > the mistake by allowing the friend declaration, some get around it by
    > implicitly declaring nested classes friends.
    >
    > DR 45 addresses this by allowing Impl access to all members to which A
    > has access (this has the effect of making friendship transitive).


    The following appears to be well formed and accepted by msvc 7.1, mwcw 9.2
    and online Comeau:

    class A
    {
    private:
    int x;

    class B
    {
    A& a;

    public:
    B(A& _a) : a(_a) {}

    void setX(int _x) { a.x = _x; }
    } b;

    public:
    A() : b(*this) {}
    void setX(int _x) { b.setX(_x); }
    };

    B can access everything in its parent class A without friendship.
    Regards,
    Conrad Weyns



    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    Conrad Weyns, Sep 4, 2004
    #5
  6. Walt Karas

    Walt Karas Guest

    Matthew Hall <> wrote in message news:<cha6vp$718$>...
    > Walt Karas wrote:
    > > Is this code legal under the standard?
    > >
    > > struct A
    > > {
    > > int i;
    > >
    > > struct C
    > > {
    > > unsigned offset_of_c(void) { return((unsigned) &(((A *) 0)->c)); }
    > >
    > > A * ptr_to_containing_A_instance(void)
    > > { return((A *) (((char *) this) - offset_of_c())); }
    > >
    > > void set(void)
    > > { ptr_to_containing_A_instance()->i = 10; }
    > > }
    > > c;

    > (From my experience, 'struct C {...} c; is not very idiomatic C++. It is
    > more common to split this into two statements - one defining the type
    > and another declaring the member variable. See below for example)
    >
    > >
    > > };
    > >
    > > #include <stdio.h>
    > >
    > > int main(void)
    > > {
    > > A a;
    > >
    > > a.c.set();
    > >
    > > // Should print 10.
    > > printf("%d\n", a.i);
    > >
    > > return(0);
    > > }
    > >
    > > The version of GCC I am using compiles and links it,
    > > and the resulting executable produces the expected
    > > output when run.
    > >
    > > Is there a less ugly way to implement a class that
    > > is not only a member class but whose instance must
    > > be in the containing class and access the containing
    > > class's members?

    >
    > First, without questioning the design:
    > Your code relies on the layout of your class. Since A is a POD in this
    > case, this is OK (well, you use C-style casts and I/O). However, it is
    > error prone and is not guaranteed to work once you add in virtual
    > methods or multiple inheritence.


    I'm relying on this principle being true:

    T a, b;

    assert((((char *) &a.x) - ((char *) &a)) ==
    (((char *) &b.x) - ((char *) &b)));

    It's hard to imagine a C++ implementation where this would ever
    be false, even if T is a dynamic class or has multple inheritance.

    > My preferred way would use constructors, and go something like this:
    >
    > struct A
    > {
    > int i;
    >
    > class C
    > {
    > public:
    > C(A* parent) : m_parent(parent) {}
    > void set(void) { m_parent->i = 10;}
    > A* m_parent;
    > };
    > C c;
    >
    > A() : i(0), c(this){}
    > };


    The m_parent pointer seems like a waste of memory to me if C is never
    going to be used again other than being the type of the member c. But
    this is probably just due to the residual trauma of being old enough
    to have own a PC with only 640K of RAM.

    > Nice, portable code. (though not very good at data hiding)
    >
    > //Variants:
    > // if A is a class with private data that c should access, add
    > // 'friend class C' to A's declaration
    > // Use a reference to A as opposed to a pointer.
    >
    > Granted, this does add some overhead in both execution speed of the
    > construction, as well as in size, so if you need to create a billion of
    > these things, you might want to take a different approach. In that case
    > you might want to re-evaluate your use of a nested class/struct.
    >
    > I realize that you have probably provided a minimal example, but why not
    > just use move the 'set' method into A, and use 'a.set()' instead of
    > 'a.c.set()'.
    > In fact, since A is a struct, why not create a free function set:
    > void set(A& a) { a.i=10;}
    > (Though the name 'set' should probably be changed, so as not to confuse
    > humans and/or compilers who might first think of std::set)
    >
    > -matt


    It's hard to give a short example illustrating why I want to do this.
    I have a class template that inherits from a class that is a template
    parameter. This base class tells the templated class where an array
    is located. I want both the array and the instance of an
    instantiation of this template to be data members of the same class.

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    Walt Karas, Sep 5, 2004
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Andrew Thompson

    Re: Squeezy Applet - Pushing the limits

    Andrew Thompson, Jun 19, 2004, in forum: Java
    Replies:
    1
    Views:
    370
    Christophe Vanfleteren
    Jun 19, 2004
  2. Mike Whittemore
    Replies:
    3
    Views:
    395
    Mike Whittemore
    May 4, 2004
  3. Ovidesvideo
    Replies:
    4
    Views:
    527
    Andrey Tarasevich
    Dec 10, 2004
  4. Replies:
    9
    Views:
    319
    Peter Hansen
    Mar 3, 2005
  5. Replies:
    4
    Views:
    99
    Tad McClellan
    Mar 15, 2006
Loading...

Share This Page