-1

QUESTION

I saw THIS question on Java, which allows you to get a pointer to the Outer object from within a Nested object.

But how could you implement this in C++?

Not satisfactory solutions:

Storing a pointer to each object: (not memory efficient)

class Outer {
private:
    int i;
    class Inner {
        int j;
        Outer *outer;
    };
    Inner items[1000];
};

Wrapping the array in a class: (adds unnecessary (?) complexity)

class Outer {
private:
    int i;
    class Inner_array {
        class Inner {
            int j;
        };
        Inner items[1000];

        // Build an interface around the array
        typedef Inner (&array)[1000]; 
        operator array();
        // etc...
    };
            
    Inner items[1000];
    Outer *outer;
};
    
Community
  • 1
  • 1
Kostas
  • 4,061
  • 1
  • 14
  • 32

1 Answers1

1

Here's one idea to save some space:

struct Outer {
    int i;

    struct Inner {
        int j;
        uint16_t where;

        Outer& outer() {
            Inner* first = this - where;
            char* addr = reinterpret_cast<char*>(first) - offsetof(Outer, items);
            return *reinterpret_cast<Outer*>(addr);
        }
    };

    Inner items[1000];

    Outer() {
        for (uint16_t ii = 0; ii < 1000; ++ii)
            items[ii].where = ii;
    }
};

If you're on a 64-bit machine with 32-bit integers, this reduces sizeof(Inner) from 16 to 8 bytes without packing, or 12 to 6 bytes with packing.

If you want to save even more space, you could do this:

struct Outer {
    int i;

    struct Inner {
        int j;

        Outer& outer() {
            Inner* sentinel = this;
            while (sentinel.j != INT_MIN)
                --sentinel;
            char* addr = reinterpret_cast<char*>(sentinel) - offsetof(Outer, sentinel);
            return *reinterpret_cast<Outer*>(addr);
        }
    };

    Inner sentinel = {INT_MIN};
    Inner items[1000];
};

But then outer() is O(n) instead of O(1), and you must be sure that INT_MIN (or some sentinel value) is never used in items.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Unfortunately his is undefined behavior for non-POD classes : stackoverflow.com/questions/1129894/. I respect the idea though :) – Kostas Jun 10 '18 at 05:34
  • @GillBates: If you believe your platform does not implement `offsetof()` in the expected way, hard-code it. You know what the offset actually is if you have a sane compiler. But then, a sane compiler will have no trouble making `offsetof()` work.... – John Zwinck Jun 10 '18 at 05:40
  • 1
    @GillBates: I added a second, totally different solution--please see. It still uses `offsetof`, but that part is not important--you could move `sentinel` to the beginning of `Outer` if you need to avoid `offsetof()`. – John Zwinck Jun 10 '18 at 05:41
  • These are not fool-proof solutions, but I get it that it's the best we can do. I want to use this for company code, so I think I'll just go for the extra pointer, or perhaps a singular outer class with a static pointer to it. Thanks for your contribution. – Kostas Jun 10 '18 at 05:52