Can one access one class field, by another class field?

Started by
11 comments, last by Aardvajk 8 years, 7 months ago

Let's say there is a class foo that has a variable of another class, call it C. Is is possible to access C members by a field of foo?

EDIT: I forgot to mention that it should be without any additional memory or significant performance loss (like with using references - mem loss).


class C
{
  public:
  float t;
};

class foo
{
  public:
   C c;
   float x; //this should access c.t
} f;

float T=f.x; //returns foo.c.t 
f.x=5f;
 

The similar example was given by Alvaro, however it regards an access between two fields of one class:


struct vector3 {
    float x, y, z;

    float& operator[](int index) {
        assert(index >= 0 && index < 3);
        return this->*members[index];
    }

    float operator[](int index) const {
        assert(index >= 0 && index < 3);
        return this->*members[index];
    }

    static float vector3::* const members[3];
};

float vector3::* const vector3::members[3] = { &vector3::x, &vector3::y, &vector3::z};
Advertisement

With a reference it's possible.


class C {
public:
	float t;
};

class Foo {
public:
	C c;
	float& x;

	Foo() : x(c.t) {
	}
};

Why do you need this, however?

@Madhed I forgot to add that it shouldn't add any memory overhead like it is with using references. I am just curious - no special need for it. I am still learning C++.

class A
{
public:
C c;
float &x(){ return c.x; }
};

But I have no idea what you are trying to do or why. What's wrong with:

a.c.x;

This is quite idiomatic e.g.

player.animation.skeleton;

@Aardvajk you are using a method instead of the class field. My question is just a spin-off of this question.

If your C class has the same layout as a single float, theoretically, you could use a union, although until quite recently you couldn't even add objects to unions (but I think that got resolved in one of the newer C++ versions).

However, an object having the same layout as a native float is quite unlikely, and definitely not portable across different C++ compilers.

The Vector case is already quite edge case, if not already outside well-defined behavior.

So my guess is, it's not possible. If it is possible (some genius C++ wizard may be able to find a way), I'd say it's something you definitely don't want to use.

C and C++ are incredible flexible, they can be pushed into almost any shape you like, due to their "the programmer knows what he/she is doing" philosophy. That does not mean you should do these weird tricks, unless you are in an obfuscated code contest smile.png

Alvaro's suggestion, using pointer math to get to sequential items, is unsafe and not guaranteed to work in all cases, but in practice works practically everywhere. The three items are in order sequentially, so getting the address of the first item then using pointer math to advance to the other items will work.


I forgot to add that it shouldn't add any memory overhead like it is with using references. I am just curious - no special need for it. I am still learning C++.

There are non-portable ways to do what you describe, to access members inside another structure. They rely on a knowledge of the compiler and hardware so that you know the system will put the data in the correct place and in the correct format.

A union is one method of doing it. Be aware that the language standard specifically warns the code is not portable and can break across machines.


union MyUnion {
    sometype a;
    othertype b;
    thattype c;
};

The three values, a, b, and c, all occupy the same block of memory. There are several rules about using them, and it is not something you should be doing as a beginner.

One of the more common versions of this was networking internet address structure. It was a union of a 32-bit value for one type, and a structure with four 8-bit values for the second type. That way you could address it as a 32-bit single value, or as four 8-bit values like aa.bb.cc.dd that most people are familiar with.

Since the exact layout of values is different on machines, the four values needed to be mapped on a compiler-specific and machine-specific basis. The three major forms are for some systems a.b.c.d, sometimes d.c.b.a, sometimes b.a.d.c.

There are a few other methods of doing it, mostly using raw memory addresses rather than named objects. All of them are inherently unsafe across machines and require specific knowledge of the compiler and architecture you are working on.

For the OP: no, what you want is impossible, and the closest you can get is Aardvajk's suggestions.

There's a couple rowdy and opinionated threads on the isocpp groups right now from people wanting to propose support for such a language construct, if you're interested in reading those.

If your C class has the same layout as a single float, theoretically, you could use a union[/url]

No, you can't. (read the whole thing) You can only read from the union member you've most recently assigned to. If you assign to c.x then the c member of the union is the active member; thus reading from the f member technically results in undefined behavior.

And that's not just academic. Modern optimizing compilers can and will assume that you are properly following the language's aliasing rules. Again, read the linked article for real-world examples of how GCC or Clang will "miscompile" code that violates the rules.

Sean Middleditch – Game Systems Engineer – Join my team!

Alvaro's suggestion, using pointer math to get to sequential items, is unsafe and not guaranteed to work in all cases, but in practice works practically everywhere. The three items are in order sequentially, so getting the address of the first item then using pointer math to advance to the other items will work.


You ninja'd my post so you didn't see the article I posted, but do please read it now that I've posted. smile.png

You cannot and most certainly should not do what you suggest. The undefined behavior can include "works most of the time" which then leads you to spread that hack all over your codebase, but you _will_ find weird cases where the code you assume is valid and safe is "mangled" in completely legal ways by the compiler.

Having had to track down and fix these exact sorts of issues in AAA codebase after upgrading compilers... trust me, just save yourself the trouble and adhere to the language's rules.

Sean Middleditch – Game Systems Engineer – Join my team!


The undefined behavior can include "works most of the time" which then leads you to spread that hack all over your codebase, but you _will_ find weird cases where the code you assume is valid and safe is "mangled" in completely legal ways by the compiler.

Right, which is why it works on most major compilers and architectures, and has shipped in thousands of code bases.

The standard has fairly strict requirements on ordering of objects inside structures, including what can be (and what cannot be) reordered.

Both of those items are used widely.

The union of structs is commonplace, and as mentioned in my post has been part of network programming since the 1970s for Internet sockets While officially you can only read the format you wrote or read a common initial sequence shared with a type you wrote, nearly 40 years of code relies on that behavior and it isn't changing any time soon. There is an enormous body of code out there relying on it. Unions to detect byte ordering are common for file system code and networking libraries, as are unions to pack bytes into more complex structures then read them as shorts, longs, long longs. Unions with bitfields to more easily modify floats and doubles are not strictly legal but widely used for decades.

For access by memory address, it is similarly another programming tidbit used for ages. It's had many uses, including to access objects where you don't have the name but do know the address of a neighbor. The standard has several guarantees such as how each must be in order, and have a higher address than the last. In practice it is reliable within builds, but details about locations and offsets can vary between builds.

For those links, fun reading but full of errors. One claims there are only two byte orderings, little endian and big endian. They forget -- probably by ignorance of not seeing enough variations of hardware -- about middle endian. Dating myself a little bit, I worked on a PDP11 back in college days which was middle endian.

You are right enough to call out they are not portable and not standard --- but they work reliably within implementations and exist in many large and established bodies of code. When you see them (and eventually you WILL see them) it is important to understand what you are looking at.

This topic is closed to new replies.

Advertisement