Pointer to data member vs. offsetof

Discussion in 'C++' started by Imre, Apr 21, 2007.

  1. Imre

    Imre Guest

    Hi

    I know that offsetof is basically a C leftover, and only works for POD
    types, not classes, so it is recommended that I use pointers to
    members instead. However, I have a problem where I don't see how I
    should use pointers to members.
    Basically, I know how to get a member if I have an object and a
    pointer-to-member (obj.*ptr instead of obj + offset), but I don't know
    how to do the opposite: getting the object from a member address and a
    pointer-to-member (that is, what to write instead of member - offset).

    Here's what I need it for:

    I have a class, for now let's just call it Outer. It has a member
    variable, the type of which is a class called Inner. Inner has a
    function F(). In F(), I need to have an Outer* pointer that points to
    the Outer object that contains this Inner object.

    class Inner
    {
    public:
    virtual ~Inner() {}
    void F();
    };

    class Outer
    {
    public:
    Inner inner;
    };

    void Inner::F()
    {
    Outer* parent = ???;
    }

    Of course, a pointer to the parent Outer object could be passed to F()
    as an argument, or as a constructor argument when creating
    Outer::inner, but it seems to be unnecessary. In every Outer object,
    the Inner object is at the same offset (this is guaranteed, isn't
    it?), so we should be able to calculate the address of the the parent
    Outer in Inner::F().

    I can do this on compilers where offsetof is supported for classes as
    well as PODs, but I'd like to know if there's a more portable
    solution.

    Thanks,

    Imre
    Imre, Apr 21, 2007
    #1
    1. Advertising

  2. Imre

    David Harmon Guest

    On 21 Apr 2007 12:12:51 -0700 in comp.lang.c++, Imre <>
    wrote,
    >Of course, a pointer to the parent Outer object could be passed to F()
    >as an argument, or as a constructor argument when creating
    >Outer::inner, but it seems to be unnecessary.


    Yes, it's necessary. Otherwise Inner doesn't even know if it is living
    inside an Outer object, or created stand-alone.
    David Harmon, Apr 22, 2007
    #2
    1. Advertising

  3. Imre

    Imre Guest

    On Apr 22, 4:20 am, David Harmon <> wrote:
    > On 21 Apr 2007 12:12:51 -0700 in comp.lang.c++, Imre <>
    > wrote,
    >
    > >Of course, a pointer to the parent Outer object could be passed to F()
    > >as an argument, or as a constructor argument when creating
    > >Outer::inner, but it seems to be unnecessary.

    >
    > Yes, it's necessary. Otherwise Inner doesn't even know if it is living
    > inside an Outer object, or created stand-alone.


    True, but Inner could know about where it's living without passing in
    the parent object; for example, using a template parameter to pass in
    the parent _type_. Passing around object instances still seems to be
    overkill (and either overhead (storing a parent pointer in Inner) or
    inconvenience (always passing in parent to Inner::F())) to me.
    Here's a more complete solution using offsetof:

    // Empty primary template, see specializations below
    template <typename Outer, int Id = 0>
    struct InnerOffset
    {
    };

    template <typename Outer, int Id = 0>
    class Inner
    {
    public:
    Outer* F();
    };

    template <typename Outer, int Id>
    Outer* Inner<Outer, Id>::F()
    {
    return (Outer*)(((char*)this) - InnerOffset<Outer, Id>::eek:ffset);
    }

    // ---

    class Outer1
    {
    public:
    int i;
    Inner<Outer1> inner;
    };

    template <>
    struct InnerOffset<Outer1>
    {
    enum { offset = offsetof(Outer1, inner) };
    };

    //

    class Outer2
    {
    public:
    int i, j;
    Inner<Outer2, 0> inner0;
    Inner<Outer2, 1> inner1;
    };

    template <>
    struct InnerOffset<Outer2, 0>
    {
    enum { offset = offsetof(Outer2, inner0) };
    };

    template <>
    struct InnerOffset<Outer2, 1>
    {
    enum { offset = offsetof(Outer2, inner1) };
    };


    int main(int argc, char* argv[])
    {
    Outer1 o1;
    Outer1* po1 = o1.inner.F();
    assert(po1 == &o1);
    Outer2 o2;
    Outer2* po20 = o2.inner0.F();
    assert(po20 == &o2);
    Outer2* po21 = o2.inner1.F();
    assert(po21 == &o2);
    return 0;
    }

    Here no memory is wasted by storing a pointer to the parent object in
    every Inner, and also we don't need to always pass the parent object
    to Inner::F(). But still, Inner members can be used in whatever Outer
    class we like, and they can find their parent. Even multiple Inner
    members in the same Outer class are supported.
    The only problem is that this code uses offsetof on classes, and that
    isn't guaranteed to work on all compilers.

    So the question is: is it possible to do the same in a more portable
    way? That is, without offsetof. Using pointers to data members, or
    whatever.

    Imre
    Imre, Apr 22, 2007
    #3
    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. Fraser Ross
    Replies:
    4
    Views:
    1,023
    Fraser Ross
    Aug 14, 2004
  2. Replies:
    18
    Views:
    558
    James Kuyper
    Nov 10, 2007
  3. Not Really Me
    Replies:
    2
    Views:
    748
    Jack Klein
    Mar 14, 2008
  4. Stephen Howe
    Replies:
    2
    Views:
    276
    Stephen Howe
    Nov 6, 2012
  5. somenath
    Replies:
    10
    Views:
    255
    James Kanze
    Jul 2, 2013
Loading...

Share This Page