How to use new allocation using custom allocation API?

I am working on some memory space with user-defined assignment and deletion that occur using a malloc-like interface that I do not control (i.e. allocate n bytes or free allocated ptr). So, nothing like delete [].

Now I want to build an array from T. I get space for this with auto space_ptr = magic_malloc(n*sizeof(T)) . Now I want to do something like array-placement-new to build n elements in place. How do I do this? ... or should I just go from 1 to n and build single T?

Note :

  • I ignore alignment problems here (or rather, assuming alignof(T) divides sizeof(T) ). If you want to specify alignment, it will be even better, but you can ignore it for simplicity.
  • Greeting code in C ++ 11 (preferably, essentially), but not C ++ 14/17.
+6
source share
1 answer

I assume your memory is aligned enough for your T You probably want to check this out.

The next problem is exceptions. We really have to write two versions, one of which may be that the construction will throw an exception, and one without it.

I will write a safe version for an exception.

 template<class T, class...Args> T* construct_n_exception_safe( std::size_t n, void* here, Args&&...args ) { auto ptr = [here](std::size_t i)->void*{ return static_cast<T*>(here)+i; }; for( std::size_t i = 0; i < n; ++i ) { try { new(ptr(i)) T(args...); } catch( ... ) { try { for (auto j = i; j > 0; --j) { ptr(j-1)->~T(); } } catch ( ... ) { exit(-1); } throw; } } return static_cast<T*>(here); } 

and safe version with no exceptions:

 template<class T, class...Args> T* construct_n_not_exception_safe( std::size_t n, void* here, Args&&...args ) { auto ptr = [here](std::size_t i)->void*{ return static_cast<T*>(here)+i; }; for(std::size_t i = 0; i < n; ++i) { new (ptr(i)) T(args...); } return static_cast<T*>(here); } 

You can make a tag-based system to choose between them, depending on whether the T construct is built from Args&... or not. If it is thrown, and ->~T() is nontrivial, use the safe option.

C ++ 17 provides some new features to perform just these tasks. They probably handle corner cases I don't have.


If you are trying to emulate new[] and delete[] , if T has a non-trivial dtor, you will need to inject the amount of T that you created in the block.

A typical way to do this is to ask for an extra room for an account in front of the unit. Those. ask sizeof(T)*N+K , where K could be sizeof(std::size_t) .

Now in your emulator new[] , the material is N in the first bit, and then call construct_n on the block immediately after it.

In delete[] subtract sizeof(std::size_t) from the one passed in the pointer, read N , and then destroy the objects (from right to left to create a mirror construction order).

All this will require careful try - catch .

If, however, ~T() trivial, your emulated new[] and delete[] do not save this extra std::size_t and do not read it.

(Note that this is how to emulate new[] and delete[] . How exactly new[] and delete[] depends on the implementation. I just draw one way that you can simulate it, it may not be so compatible with that how they work on your system, for example, some ABIs can always store N , even if ->~T() trivial or many other options.)


As noted by the OP, you can also check the trivial design before worrying about it above.

+5
source

Source: https://habr.com/ru/post/1012121/


All Articles