Topic: Placement new for cyclic (const) pointer dependancies?


Author: fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/08/18
Raw View
David Bruce <dib@dera.gov.uk> writes:

>My problem is that I sometimes want to make *mutually* dependant Nodes.
>So what is the best way to go about it?

Use forward declarations.
For example,

 extern const Node y;
 const Node x (&y);
 const Node y (&x);

or

 class Circular {
  static const Node x, y;
 }
 const Node Circular::x (&Circular::y);
 const Node Circular::y (&Circular::x);

>An different approach is to separate the memory allocation from
>the object ctor, using a ``put it here'' form of placement new:
>
>    template <class T> T* operator new(size_t, T *p) { return p; }

You should probably just use the `void *' version in <new>.

>    const Node *const x = (const Node *const)operator new(sizeof(Node));
>    const Node *const y = (const Node *const)operator new(sizeof(Node));
>    new (x) Node(y);
>    new (y) Node(x);
...
>* How (assuming of course that I can) do I mimic the functionality of new;
>  i.e., use Node::operator new if it exists and ::operator new otherwise?

I don't think there is any way of doing it, if you don't have control
over the `Node' class.

>* Can an object allocated in two phases like this be safely deleted?

Yes.  Well, you ought to be able to.  There may be some wording in the
draft that says otherwise, but for all but the most pedantic purposes
you should ignore it.  The basic intent is that if `p' is a pointer to
(non-array) type `T', then the expression `delete p' should be
equivalent to the expression `(p->~T(), operator delete(p))'.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Richard See <Richard.See@vega.co.uk>
Date: 1997/08/19
Raw View
David Bruce wrote:
...
> My problem is that I sometimes want to make *mutually* dependant Nodes.
> So what is the best way to go about it?
>
> Obviously I can do something like:
>
>     const Node *const x = new Node(0);    // 0 is just a placeholder
>     const Node *const y = new Node(x);
>
> and fiddle around inside x to close the cycle, but I'd rather not.
> As I mentioned, having bPtr const is good for all other purposes.
> Moreover, the functionality of my Node classes is independent of
> whether the dependancies are cyclic or not, and so I feel strongly
> that their design should be similarly insensitive.
>
> An different approach is to separate the memory allocation from
> the object ctor, using a ``put it here'' form of placement new:
>
>     template <class T> T* operator new(size_t, T *p) { return p; }
>
>     const Node *const x = (const Node *const)operator new(sizeof(Node));
>     const Node *const y = (const Node *const)operator new(sizeof(Node));
>     new (x) Node(y);
>     new (y) Node(x);
>
> I now get the fuss of cyclic dependancies where I introduce the cycles,
> rather than in the Node class (preserving the separation of concerns I want).
>
> But this use of placement new (although it seems to work!) poses some
> technical questions, which are probably in language lawyer territory.
> For example:
>
> * How (assuming of course that I can) do I mimic the functionality of new;
>   i.e., use Node::operator new if it exists and ::operator new otherwise?
>
> * Can an object allocated in two phases like this be safely deleted?
>   (I know that deletion is not allowed if one uses a char array, as in
>   the examples in the ARM, but here the memory *did* ultimately come from
>   operator new (the same operator new as new would use, if Q1 is solved).)
>   [I'm using a garbage collector -- so I don't care -- but I am curious.]

Another option you could consider is using a factory to generate one of
the nodes, something like this (untested code follows):

class Node : public B {
public:
 // typedef for a function that generates new Nodes.
 typedef const B* (*FactoryFn)(const B*);
 Node(const B *const b) : bPtr(b) {}
 Node(FactoryFn f) : bPtr(f(this)) {} // generate other node.
        virtual void m() const { ... }
private:
 const B *const bPtr;
};

class MyNode : public Node {
public:
 MyNode(const B* b) : Node(b) {}
        virtual void m() const { ... }
};

const B* factory_fn(const B* b) {
 return new MyNode(b);
}

void f(void) {
 new Node(factory_fn);
}

You might want to use a functor instead of a function, to allow easy
passing of other parameters to the 2nd Node's ctor, and to access it's
address afterwards.

The only standards question then is whether the factory function's use
of a pointer to a partially constructed object is defined behaviour. I
couldn't find anything to say that it isn't, and it looks like it should
be, especially as the pointer is only a B* and the B part of the Node is
fully constructed.  Only problem I can see is that you'd have to be
careful if you wanted to call virtual functions in the ctor of the
factory-produced Node.

Richard.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Bill Wade" <bill.wade@stoner.com>
Date: 1997/08/20
Raw View
Fergus Henderson <fjh@murlibobo.cs.mu.OZ.AU> wrote in article
<5t7arm$c3p@mulga.cs.mu.OZ.AU>...
> David Bruce <dib@dera.gov.uk> writes:
>
> >    const Node *const x = (const Node *const)operator new(sizeof(Node));

> >    const Node *const y = (const Node *const)operator new(sizeof(Node));

> >    new (x) Node(y);
> >    new (y) Node(x);
> ...
> >* How (assuming of course that I can) do I mimic the functionality of
new;
> >  i.e., use Node::operator new if it exists and ::operator new
otherwise?
>
> I don't think there is any way of doing it, if you don't have control
> over the `Node' class.

How about:

struct NodeWrapper: public Node
{
public:
  static Node* Create(){ return (Node*)operator new(sizeof(Node)); }
};

It seems like NodeWrapper::Create() will attempt to use Node::new if it
exists otherwise it will call the global operator new.  Of course this will
fail if Node::new is private.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: David Bruce <dib@dera.gov.uk>
Date: 1997/08/14
Raw View
I've got a class hierarchy of which the following is a simplified example:

    class B { public: virtual void m() const = 0; };

    class Node : public B
    { public:  Node(const B *const b) : bPtr(b) {}
               virtual void m() const { ... }
      private: const B *const bPtr;
    };

    class Leaf : public B
    { public:  Leaf(const int i) : value(i) {}
               virtual void m() const { ... }
      private: const int value;
    };

Here Nodes represent some kind of data dependancy, in that the instance
of B pointed at by bPtr may be used in m; Leafs have no such dependancies.

Everything has to be heap allocated as their lifetimes are unpredictable;
hence the use of pointers throughout.  They are all declared const as all
the data structures I'm working with are entirely immutable.  Also, none
of the constructors look at (the contents of) any pointers they are given.

My problem is that I sometimes want to make *mutually* dependant Nodes.
So what is the best way to go about it?


Obviously I can do something like:

    const Node *const x = new Node(0);    // 0 is just a placeholder
    const Node *const y = new Node(x);

and fiddle around inside x to close the cycle, but I'd rather not.
As I mentioned, having bPtr const is good for all other purposes.
Moreover, the functionality of my Node classes is independent of
whether the dependancies are cyclic or not, and so I feel strongly
that their design should be similarly insensitive.


An different approach is to separate the memory allocation from
the object ctor, using a ``put it here'' form of placement new:

    template <class T> T* operator new(size_t, T *p) { return p; }

    const Node *const x = (const Node *const)operator new(sizeof(Node));
    const Node *const y = (const Node *const)operator new(sizeof(Node));
    new (x) Node(y);
    new (y) Node(x);

I now get the fuss of cyclic dependancies where I introduce the cycles,
rather than in the Node class (preserving the separation of concerns I want).

But this use of placement new (although it seems to work!) poses some
technical questions, which are probably in language lawyer territory.
For example:

* How (assuming of course that I can) do I mimic the functionality of new;
  i.e., use Node::operator new if it exists and ::operator new otherwise?

* Can an object allocated in two phases like this be safely deleted?
  (I know that deletion is not allowed if one uses a char array, as in
  the examples in the ARM, but here the memory *did* ultimately come from
  operator new (the same operator new as new would use, if Q1 is solved).)
  [I'm using a garbage collector -- so I don't care -- but I am curious.]


Sincerely,

    David Bruce
----
post: DERA Malvern, St Andrews Road, Malvern, WORCS WR14 3PS, ENGLAND
mailto:dib@dera.gov.uk ** phone: +44 1684 895112 ** fax: +44 1684 894389
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]