简体   繁体   中英

Stateful custom deleter for unique_ptr

I am using a memory pool for faster allocations & releases. The memory pool's Release() operation needs the pointer to release and the number of bytes allocated.

Now I want to store an array pointer obtained from a memory pool in a unique_ptr . The array is of variable size, so I need a stateful custom deleter that stores the size of the array.

Could you provide example code on how to declare such a unique_ptr ?

UPDATE: after the hint from Artyer that made it clear to me, here's what I've tried:

template<typename T> struct MPSingleDeleter {
  void operator()(T *p) {
    p->~T();
    MemPool::Instance().Release(p, sizeof(T));
  }
};

template<typename T> struct MPArrayDeleter {
  size_t _nItems;

  explicit MPArrayDeleter(const size_t nItems) : _nItems(nItems) {
  }

  void operator()(T *p) {
    for (size_t i = 0; i < _nItems; i++) {
      p[i].~T();
    }
    MemPool::Instance().Release(p, _nItems * sizeof(T));
  }
};

template <typename T> using SingleUP = std::unique_ptr<T, MPSingleDeleter<T>>;
template <typename T> using ArrayUP = std::unique_ptr<T[], MPArrayDeleter<T>>;

struct MemHelper {
  template<typename T, typename ...Args>  static T* NewSingle(Args&&... args) {
    void * const p = MemPool::Instance().Acquire(sizeof(T));
    ::new (p) T(std::forward<Args>(args)...);
    return static_cast<T*>(p);
  }

  template<typename T, typename ...Args> static T* NewArray(const size_t nItems, Args&&... args) {
    T *p = static_cast<T*>(MemPool::Instance().Acquire(nItems * sizeof(T)));
    for (size_t i = 0; i < nItems; i++) {
      void * const pv = static_cast<void *>(p + i);
      ::new (pv) T(std::forward<Args>(args)...);
    }
    return p;
  }

  template<typename T, typename ...Args> static SingleUP<T> MakeSUP(Args&&... args) {
    return SingleUP<T>(NewSingle<T>(std::forward<Args>(args)...));
  }

  template<typename T, typename ...Args> static ArrayUP<T> MakeAUP(const size_t nItems, Args&&... args) {
    return ArrayUP<T>(NewArray<T>(nItems, std::forward<Args>(args)...), MPArrayDeleter<T>(nItems));
  }
};

Now the declaration of a unique_ptr variable is as easy as:

// Array of double
ArrayUP<double> pBuffer = MemHelper::MakeAUP<double>(2ui64 * nItems);
// Single Connection
SingleUP<Connection> pConn = MemHelper::MakeSUP<Connection>(ioContext);

You would normally store the size before the allocated data in a memory pool, so you would just pass a stateless deleter that gets the size from the pointer.

You can easily do something like this though:

#include <memory>

struct pool_deleter {
    std::size_t size;

    template<class T>
    void operator()(T* ptr) noexcept {
        std::destroy_n(ptr, size);
        Release(ptr, size * sizeof(T));
    }
};

template<class T>
using pool_ptr = std::unique_ptr<T, pool_deleter>;

// Used like:
std::size_t n = /* ... */;
T* ptr_ = /* Allocate and construct n objects from pool  */;
pool_ptr<T> ptr{ptr_, pool_deleter{n}};  // Pass the custom deleter as an argument

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM