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

W

Walt Karas

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?
 
S

Sergei Organov

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.
 
M

Matthew Hall

Walt said:
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
 
C

Conrad Weyns

Walt Karas said:
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
 
C

Conrad Weyns

Matthew Hall said:
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:

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
 
W

Walt Karas

Matthew Hall said:
(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)


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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top