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

From: Walt Karas (wkaras_at_yahoo.com)
Date: 09/05/04


Date: 5 Sep 2004 07:40:39 -0400

Matthew Hall <mahall@math.uiuc.edu> wrote in message news:<cha6vp$718$1@news.ks.uiuc.edu>...
> 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! ]



Relevant Pages

  • Re: Best Coding Practice
    ... I needed to add an array class member to an object. ... OO standpoint to make a FreeProduct class which extends the Product ... Cost is an attribute, and different values for attributes should not determine new products. ...
    (comp.lang.php)
  • Re: Best Coding Practice
    ... I needed to add an array class member to an object. ... the logic was the same between the two arrays aside from the price, ... and the child classes the specific elements. ...
    (comp.lang.php)
  • calloc problem
    ... I am trying to dynamically allocate memory to be used as an array. ... The array is pointed to by the 'vert' member of the structure 'grid'. ... I wish for this array to be a 2 dimensional array of the struct ...
    (comp.lang.c)
  • Re: Translation-Time Type Information
    ... there are three points of undefined behaviour in the ... point within the same array object. ... functions return the 'value' member of the pointed-to-union, ... pointer is equal to the alignment requirement for that function ...
    (comp.lang.c)
  • Re: Best Coding Practice
    ... I needed to add an array class member to an object. ... wouldn't feel entirely comfortable suddenly making it a child class of ... OO standpoint to make a FreeProduct class which extends the Product ...
    (comp.lang.php)