Skip to content

Instantly share code, notes, and snippets.

@dwilliamson
Created October 16, 2014 09:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dwilliamson/20cc8fff0595cec18365 to your computer and use it in GitHub Desktop.
Save dwilliamson/20cc8fff0595cec18365 to your computer and use it in GitHub Desktop.
Allow placement delete
// Placement new from specific allocator: combines allocation+construction
void* operator new (size_t size, Allocator& allocator)
{
return malloc(allocator);
}
// Required matching placement delete chained to by compiler-generated code
// whenever a constructor throws an exception during unwind
void operator delete(void* ptr, Allocator& allocator)
{
free(allocator, ptr);
}
// Allocation example
Allocator allocator;
char* data = new (allocator) char[10];
// Ideally you should be able to clear this up by calling directly to
// the delete operator
delete (allocator) data;
// This is not legal. Stroustrup says this is purposefully not allowed because
// "there is no general way of assuring that it would be used correctly."
// http://www.stroustrup.com/bs_faq2.html#placement-delete
// Spurious at best...
// ...especially given that I now have to write this
template <typename TYPE>
void Delete(TYPE* ptr, Allocator& allocator)
{
if (ptr != nullptr)
{
ptr->~TYPE();
free(allocator, ptr);
}
}
// It's not *too* bad given that you can parameterise your free function and just do this
template <typename TYPE, typename ALLOCATOR>
void Delete(TYPE* ptr, ALLOCATOR& allocator)
{
if (ptr != nullptr)
{
ptr->~TYPE();
free(allocator, ptr);
}
}
// Just let me call operator delete!
// I can already blow my leg off with C++, this isn't going to scratch a hair...
@sopyer
Copy link

sopyer commented Oct 16, 2014

Oh I see. Never knew that there was a placement new operator overload. Always thought that placement argument should be a pointer. Sometimes I wonder why do they need all those complex syntax constructs which are half-baked.
Well I never have this problem for my self, as I tend to use C-style arena(pool) based API for allocations. Though I loose ability to construct object at allocation time and destroy at deallocation, but it is just a different tradeoff.

@dwilliamson
Copy link
Author

I would love a language that somehow automated the opaque C-style API of:

ErrorCode Object_Construct(Object* object)
{
   assert(object != NULL);

   // Initialise to defaults
   object->a = NULL;
   object->b = NULL;

   // Create required resources and teardown on failure
   if (A_Create(&object->a) == ErrorCode_Error)
   {
      Object_Destruct(object);
       return ErrorCode_Error;
   }
   if (B_Create(&object->b) == ErrorCode_Error)
   {
      Object_Destruct(&object->b);
      return ErrorCode_Error;
   }

   return ErrorCode_OK;
}


ErrorCode Object_Destruct(Object* object)
{
   assert(object != NULL);

   // Destroy only those resources that have been created so far
   // Destroy function checks for null pointer
   B_Destroy(object->b);
   object->b = NULL;
   A_Destroy(object->a);
   object->a = NULL;
}


ErrorCode Object_Create(Object** object)
{
   // Allocate container
   assert(object != NULL);
   *object = malloc(sizeof(Object));
   if (*object == NULL)
      return ErrorCode_MallocFail;

   // Construct with free on error
   if (Object_Construct(*object) == ErrorCode_Error)
   {
      free(*object);
      *object = NULL;
      return ErrorCode_Error;
   }

   return ErrorCode_OK;
}


void Object_Destroy(Object* object)
{
   if (object != NULL)
   {
      Object_Destruct(object);
      free(object);
   }
}

@sopyer
Copy link

sopyer commented Oct 16, 2014

C++ does similar thing but does not return error code.
But often time of allocation is not the same as time of object creation. For example in language with GC you often have finalize method as memory release is not a moment when you want your object destroyed. This is one of the reason why I do not like C++ constructors/destructors, as they assume for me time of object creation.
Some use cases can be covered by language. But I am not sure about general case. For example I can envision in C some kind of python with construct which will cover scoped used case for C++ classes.
Probably still good API is the best for this kind of things.

@dwilliamson
Copy link
Author

I don't really care for GC stuff, just looking for a mechanism to remove a lot of the boiler-plate when using opaque C types.

@sopyer
Copy link

sopyer commented Oct 16, 2014

In this case I think API like this can help:

template
R alloc(arena_t arena);
template<typename R, typename A1>
R alloc(arena_t arena, A1 a1);
template<typename R, typename A1, typename A2>
R alloc(arena_t arena, A1 a1, A2 a2);
....

You should be able to generate them quite efficiently with your python generator. But it is a slippery slope. The end result can be boost like...

@sopyer
Copy link

sopyer commented Oct 16, 2014

Another random idea if you combine clReflect with python generator - you should without problems generate all those C API you mentioned earlier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment