From: Martin B. on
Andy Venikov wrote:
> Hyman Rosen wrote:
>> Andy Venikov wrote:
>>> (you can)
>>> X * px = new (regionAllocator) X;
>>> But you can't
>>> delete (regionAllocator) px;
>>> you can only
>>> delete px;
>>
>> Just do
>> px->~X();
>> regionAllocator.free(px);
>>
>
> Yes, this is what I'm forced to do. But it looks ugly that for a single
> new you actually need two calls. In my case I just created a templated
> "Destruct" function that does both.
>

I agree it's ugly, but so is the placement-new syntax IMHO (the explicit
dtor call is just even more ugly).

If I were faced with such (which I have never been yet) I would prefer
to use some more machinery to hide all that new and placement stuff
between some interface.
Personally I think new-with-parameter is just something that's better
and more clearly done with a named function (except for the void* case,
as we already have that operator-new).

I have done a simple scratch program below.

br,
Martin

[CODE]
#include "stdafx.h"
#include <boost/smart_ptr.hpp>
#include "unique_ptr.hpp" // from
http://home.roadrunner.com/~hinnant/unique_ptr03.html
#include <string>
#include <iostream>
using namespace std;
using namespace boost;

struct X {
string m_;
X() {
// throw runtime_error("Check that ctor exceptions are handled
correctly.");
}
};

struct destruct_X {
void operator()(X* obj) {
if(obj) {
obj->~X();
free(obj);
}
}
};

//! create a new X object in a custom memory pool
//! The knowledge of how to delete the object is already encapsulated
//! in the shared_ptr instance.
shared_ptr<X> construct_shared_x() {

void* p = malloc(sizeof(X));
if(!p)
throw bad_alloc("struct X");
try {
return shared_ptr<X>( new (p) X, destruct_X() );
} catch(...) {
free(p);
throw;
}
}

//! create a new X object in a custom memory pool
//! The Unique pointer takes care of deallocating it correctly, but
//! if you absolutely want to, you can call release() on the
//! returned uptr to manually manage the raw ptr.
typedef unique_ptr<X, destruct_X> PooledXPtr;
PooledXPtr construct_unique_x() {
void* p = malloc(sizeof(X));
if(!p)
throw bad_alloc("struct X");
try {
return PooledXPtr( new (p) X );
} catch(...) {
free(p);
throw;
}
}

int main(int argc, char* argv[])
{
// Dump leaked memory (Windows specific)
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

try {
shared_ptr<X> px = construct_shared_x();
px->m_ = "Shared!";
cout << px->m_ << "\n";
} catch(...) {
cout << "Failed shared.\n";
}

try {
PooledXPtr ux = construct_unique_x();
ux->m_ = "Unique!";
cout << ux->m_ << "\n";
} catch(...) {
cout << "Failed unique.\n";
}

try {
X* x = construct_unique_x().release();
x->m_ = "Raw!";
cout << x->m_ << "\n";
destruct_X()(x);
} catch(...) {
cout << "Failed raw.\n";
}

return 0;
}

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Andy Venikov on
peter koch larsen wrote:

> On 4 Dec., 19:56, Andy Venikov <swojchelo...(a)gmail.com> wrote:
>

Hmmm, knowledge of what argument to use when calling delete can be
>> gained statically, without the need to revert to virtual dispatch.
>>
>> No it can't, and that is the problem. Even in the case of whole
> program analysis (and this often is practically not possible) you
> could have cases such is:
>
> void f(volatile bool b)
> {
> int* val = b ? new int(1): new(myallocator) int(2);
> ...
>
> // how to delete here?
> }
>
> /Peter
>
>

I did not say the knowledge will always be gained statically, I said it
*can* be gained statically. You provided an example where it certainly
can't. Might I also add that evil-looking code like this shouldn't be
written unless freeing through myallocator and through default free are
equivalent. And that *is* known when you call new.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Goran on
On Dec 4, 7:51 pm, Andy Venikov <swojchelo...(a)gmail.com> wrote:
> restor wrote:
> >> What's the rational for not having placement delete?
>
> > C++ has a placement delete it is just spelled differently: I believe
> > explicit destructor call. Consider an example:
>
> > void createAndDestroy( Class * obj )
> > {
> > new (obj) Class("obj1"); // ctor w/o allocation
> > obj->~Class(); // dtor w/o deallocation
> > }
>
> > I think the naming convention is confusing, as this question reoccurs
> > time and again. And it is the "placement new" that is confusing, as it
> > implies special (trivial) form of allocation. They probably should be
> > called "explicit constructor call" and "explicit destructor call".
>
> > Regards,
> > &rzej
>
> I believe you've misunderstood my point.
>
> I was talking about a general form of placement new, the one that takes
> any user-defined type, not just a pointer to void. You assumed that I
> was talking about a version of placement new that was taking a pointer
> to void. According to the standard it is not even possible to overload
> this function and yes, all it does is calls a constructor in the
> specified memory.

Ulrich also misunderstood you, I think, and that's because "placement
new" is not the best term, overloaded being better: your use isn't
"placement", and placement new is overloaded.

Goran.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Goran on
On Dec 4, 7:56 pm, Andy Venikov <swojchelo...(a)gmail.com> wrote:
> Goran Pusic wrote:
>
> >> That puts an unduly difficult requirement on (presumably custom)
> >> operator delete to figure out where the memory came from. And no, it's
> >> not always possible to embed that information with the returned memory.
>
> > Ahhh, but if you allocate instances of your class both from standard
> > heap and your own allocator, are these still the same class? If
> > nothing else, they are not the same WRT that particular detail that is
> > heap handling.
>
> Well, I don't want my types to be aware of actual physical memory
> placement. The same type can be allocated on the heap or anywhere else
> and that doesn't change the type's type.

Hmmm, yes, that's a noble theoretic goal ;-). But still, given that if
you could have two operators delete, you would have to keep track
which to call, it just doesn't buy you much (you do work that would be
done by the compiler if you did have two classes).

> > If that is the case, you could have variants of your class: base (with
> > actual functionality), one where you use default allocation and one
> > that comes from a pool. Then you pass reference to base around for
> > work, and call operator delete (compiler picks correct one for you)
> > when it's time to go.
>
> That would add virtual dispatch to classes that whouldn't need it
otherwise.

No, it wouldn't. operator delete ain't virtual, and it really is the
compiler that picks the "deletion" function. Did you mix operator
delete and the destructor in your mind?

Goran.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: itaj sherman on
On Dec 5, 6:17 am, peter koch larsen <peter.koch.lar...(a)gmail.com>
wrote:
> On 4 Dec., 19:56, Andy Venikov <swojchelo...(a)gmail.com> wrote:> Goran Pusic wrote:
>
> [snip]
> > > You should note that operator new and delete must "reverse-match" ;-)
> > > in functionality for a given class instance. Even if what you want to
> > > do was possible, you would still have to keep track of how a
> > > particular instance was instantiated so that you can call appropriate
> > > delete version. And if that is so, you could just as well split your
> > > instances in two actual classes and be done with it. In other words,
> > > it seems that you don't gain much (if anything) with your idea.
>
> > Hmmm, knowledge of what argument to use when calling delete can be
> > gained statically, without the need to revert to virtual dispatch.
>
> No it can't, and that is the problem. Even in the case of whole
> program analysis (and this often is practically not possible) you
> could have cases such is:
>
> void f(volatile bool b)
> {
> int* val = b ? new int(1): new(myallocator) int(2);
> ...
>
> // how to delete here?


//By Andy's hypothetical placement delete, like this:

b ? delete val : delete(myallocator) val;

>
> }
>
> /Peter
>

So what I think Andy seems to be asking, is whether there was a
specific reason not to include in c++ the option to overload operator
delete with parameters in symmetry with operator new, in such syntax.
That is, rather than not including it in c++, forcing us to use any of
the workarounds suggested here.

btw, I hope I didn't miss something important: what is the point in
'b' being volatile?


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]