This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

operator new/delete forwarding


We currently have:

operator new forwards to malloc. operator delete forwards to free.
operator new(nothrow) forwards to malloc. operator delete(nothrow) forwards to free.
operator new [] forwards to operator new. operator delete [] forwards to operator delete.
operator new(nothrow)[] forwards to operator new(nothrow). operator delete(nothrow)[] forwards to operator delete.


Note the asymmetry (nothing forwards to delete(nothrow)). This can cause confusion for those clients which overload only a subset of these 8 operators.

The careful programmer will override all 8 of the operators and thus not be affected by our implementation. However our implementation currently allows the programmer to override only the 4 non-array forms of new/delete (both ordinary and nothrow), and get the array forms for free (as long as the user's operator delete can handle a destruction triggered by an exception emanating from a new(nothrow)[] expression - from a constructor, not from the operator new[](size_t, nothrow)).

I view this forwarding as a valuable convenience to the client, however I believe it is incomplete, and slightly wrong. I respectfully propose the following slight alteration:

operator new forwards to malloc. operator delete forwards to free.
operator new(nothrow) forwards to operator new. operator delete (nothrow) forwards to operator delete.
operator new [] forwards to operator new. operator delete [] forwards to operator delete.
operator new(nothrow)[] forwards to operator new[]. operator delete (nothrow)[] forwards to operator delete[].


This will allow the client to only override:

void* operator new(std::size_t size) throw(std::bad_alloc);
void  operator delete(void* ptr) throw();

And get both the array forms and nothrow forms for free (which is the most common use case). If the client wishes to handle array allocation/deallocation differently than non-array, then he can override only these 4:

void* operator new(std::size_t size) throw(std::bad_alloc);
void  operator delete(void* ptr) throw();
void* operator new[](std::size_t size) throw(std::bad_alloc);
void  operator delete[](void* ptr) throw();

And still get correctly behaving nothrow forms for both the array and non-array overrides (our default nothrow forms will forward correctly to the client's non-array and array variants).

Specifically, change:

new_opnt.cc from:

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz, const std::nothrow_t&) throw()
{
  void *p;

  /* malloc (0) is unpredictable; avoid it.  */
  if (sz == 0)
    sz = 1;
  p = (void *) malloc (sz);
  while (p == 0)
    {
      new_handler handler = __new_handler;
      if (! handler)
	return 0;
      try
	{
	  handler ();
	}
      catch (bad_alloc &)
	{
	  return 0;
	}

      p = (void *) malloc (sz);
    }

  return p;
}

to:

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz, const std::nothrow_t&) throw()
{
    try
    {
        return ::operator new(sz);
    }
    catch (...)
    {
    }
    return 0;
}

and change new_opvnt.cc from:

_GLIBCXX_WEAK_DEFINITION void*
operator new[] (std::size_t sz, const std::nothrow_t& nothrow) throw()
{
  return ::operator new(sz, nothrow);
}

to:

_GLIBCXX_WEAK_DEFINITION void*
operator new[] (std::size_t sz, const std::nothrow_t&) throw()
{
    try
    {
        return ::operator new[](sz);
    }
    catch (...)
    {
    }
    return 0;
}

and change del_opnt.cc from:

_GLIBCXX_WEAK_DEFINITION void
operator delete (void *ptr, const std::nothrow_t&) throw ()
{
  if (ptr)
    free (ptr);
}

to:

_GLIBCXX_WEAK_DEFINITION void
operator delete (void *ptr, const std::nothrow_t&) throw ()
{
  ::operator delete (ptr);
}

and change del_opvnt.cc from:

_GLIBCXX_WEAK_DEFINITION void
operator delete[] (void *ptr, const std::nothrow_t&) throw ()
{
  ::operator delete (ptr);
}

to:

_GLIBCXX_WEAK_DEFINITION void
operator delete[] (void *ptr, const std::nothrow_t&) throw ()
{
  ::operator delete[] (ptr);
}

Remaining unchanged are:

new_opv.cc
del_opv.cc

Here is a brief example demonstration:

#include <cstdio>
#include <new>

const std::size_t N = 1025*1024;
char pool[N];
char* p = pool;
unsigned s = 0;

void* operator new(std::size_t size) throw(std::bad_alloc)
{
    if (size > N - (p-pool))
        throw std::bad_alloc();
    std::printf("Allocating\n");
    void* r = p + s;
    p += s;
    return r;
}

void  operator delete(void*) throw()
{
    std::printf("Deallocating\n");
}

int main()
{
    std::printf("new\n");
    delete new char;
    std::printf("new(nothrow)\n");
    delete new(std::nothrow) char;
    std::printf("new[]\n");
    delete [] new char[3];
    std::printf("new(nothrow)[]\n");
    delete [] new(std::nothrow) char[3];
}

Today's output is:

new
Allocating
Deallocating
new(nothrow)
Deallocating
new[]
Allocating
Deallocating
new(nothrow)[]
Deallocating

The desired output is:

new
Allocating
Deallocating
new(nothrow)
Allocating
Deallocating
new[]
Allocating
Deallocating
new(nothrow)[]
Allocating
Deallocating

As a last detail, I can see valid arguments for three different implementations of the array-nothrow forms:

operator new(nothrow)[] forwards to operator new[]. operator delete (nothrow)[] forwards to operator delete[].
or
operator new(nothrow)[] forwards to operator new(nothrow). operator delete(nothrow)[] forwards to operator delete(nothrow).
or
operator new(nothrow)[] forwards to operator new. operator delete (nothrow)[] forwards to operator delete.


I somewhat arbitrarily chose the first implementation in my description above. But I think any of these three choices is reasonable (note that our current implementation is a cross between the second and third choice). The most important change is to have the non-array nothrow variants forward to the non-array, throwing implementations.

-Howard


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]