Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ templates and header allocations

I recently encountered problems with memory allocations made in one DLL (or *.so - portable code) and deallocation done in another DLL. The errors I encountered so far are:

  • It just doesn't work - fails an assert() on debug.
  • It doesn't work if one DLL was statically linked with the standard C library and the other DLL dynamically linked with it.
  • It doesn't work if one DLL does an allocation then the DLL is unloaded and another DLL tries to deallocate this memory.

Basically the rule I decided I should follow is not to make allocations in one DLL and release it in another (and preferably keep it within one cpp file). This usually also means I shouldn't do allocations in a header file that may be shared by more than one DLLs. This means I shouldn't do allocations in tempaltes (since they are all in header) and this is quite a big limitation.

When I do need to create a new object in a template what I do now is allocate the memory for it a cpp file and only then run its c'tor with placement new operator.

// header
class MyBase
{
public:
  static void* allocate(std::size_t i_size);
};

template <typename T>
class MyClass: MyBase
{
public:
  T* createT();
};

temlpate <typename T>
T* MyClass<T>::createT()
{
  void* pMem = MyBase::allocate( sizeof(T) );
  return new (pMem) T;
}

// Cpp file
void* MyBase::allocate(std::size_t i_size)
{
  return malloc( i_size );
}

While this works, this is a bit ugly. It means writing template code without using new.

Another implication is that if you don't know that a template was written using this technique you should only use const methods of it in a header file (including other templates) (this is assuming const methods don't allocate or deallocate memory). This includes STL. In fact, one of the places I encountered this was in a vector that was resized by one dynamic library (on HP-UX) then unloaded than its d'tor was called by another dynamic library.

Is there some widely known solution for this that I'm just missing or is it just an overlooked problem?

like image 648
selalerer Avatar asked Oct 12 '22 02:10

selalerer


1 Answers

Basically the rule I decided I should follow is not to make allocations in one DLL and release it in another (and preferably keep it within one cpp file). This usually also means I shouldn't do allocations in a header file that may be shared by more than one DLLs.

No, the one does not imply the other.

If your allocation and de-allocation functions are templates in a header, that's still fine; just ensure that you restrict use of these functions for any given object to one TU1.

Encapsulate your objects such that it would be invalid/prohibited/undefined for code in a DLL 1 to call these functions on objects from DLL 2. Make it a contract to the user, write it in comments that ownership of objects remains with the original allocating context, then move on to the next part of your project without ever having to worry about this again.

That the functions are available to all TUs is not relevant; after all, you can always attempt delete on these things!


1 - Translation Unit. Roughly equivalent to one pre-processed .cpp file.

like image 119
Lightness Races in Orbit Avatar answered Oct 13 '22 16:10

Lightness Races in Orbit