Массивная версия функции распределения - PullRequest
2 голосов
/ 29 октября 2019

Существует функция выделения, которая возвращает умный указатель на пул памяти, выделенный и построенный один экземпляр T:

template<typename T, typename... ARGUMENTS>
std::unique_ptr<T, HeapDeleter<T>> allocate_unique( ARGUMENTS&&... arguments ) {

    HeapAllocator<T> allocator;

    /* Allocate memory in memory pool */
    T* ptr = allocator.template allocate<1>();

    if( ptr != nullptr ) {
        /* Construct T in allocated memory */
        ::new( static_cast<void*>( ptr ) ) T( std::forward<ARGUMENTS>( arguments )... );
    }

    return std::unique_ptr<T, HeapDeleter<T>>( ptr, HeapDeleter<T>() );
}

Я пытаюсь реализовать вариант массива, способный выделить N элементов Tимея в виду стиль использования согласованности такой функции. Было бы замечательно сохранить пересылку аргументов ARGUMENTS ..., но применять это для всех случаев. Есть ли возможность сделать это?

template<typename T, typename... ARGUMENTS, std::size_t N>
std::unique_ptr<T[], HeapDeleter<T, N>> allocate_array( /* ??? */ ) { /* <--- */
    HeapAllocator<T> allocator;

    /* Allocate memory in memory pool */
    T* ptr = allocator.template allocate<N>();

    if( ptr != nullptr ) {
        std::ptrdiff_t idx = 0;

        /* Run T's move constructor for all the elements */
        ( ::new( static_cast<void*>( ptr + idx++ ) ) T( std::forward<ARGUMENTS>( arguments ) ), ... );
    }
    return std::unique_ptr<T, HeapDeleter<T>>( ptr, HeapDeleter<T>() );
}

Большое спасибо всем, кто хочет мне помочь ... Мартин

Ответы [ 2 ]

1 голос
/ 29 октября 2019

Я попытался решить проблему, как описано в комментариях. Для простоты я использовал только необработанные указатели:

template <typename T, typename... Vs, size_t... Is>
void allocate_helper(T* ptr, std::tuple<Vs...> v, std::index_sequence<Is...>)
{
   new (ptr) T(std::forward<Vs>(std::get<Is>(v))...); 
}

template <typename T, typename V> 
void allocate_array_helper(T* ptr, V v)
{
   allocate_helper<T>(ptr, std::move(v), 
                      std::make_index_sequence<std::tuple_size_v<V>>{});
}

template <typename T, typename V, typename... Vs>
void allocate_array_helper(T* ptr, V v, Vs... vs)
{
   allocate_helper<T>(ptr, std::move(v), 
                      std::make_index_sequence<std::tuple_size_v<V>>{});
   try
   {
      allocate_array_helper<T>(++ptr, std::move(vs)...);
   }
   catch (...)
   {
      std::destroy_at(ptr);
      throw;
   }
}

template <typename T, typename... Vs>
T* allocate_array(Vs... vs)
{
   T* ptr = (T*)::operator new(sizeof(T) * sizeof...(Vs));
   try 
   {
      allocate_array_helper<T>(ptr, std::move(vs)...);
   }
   catch (...)
   { 
      ::operator delete(ptr);
      throw;
   }
   return ptr;
}

Это работает для меня в этом примере, так что он печатает hello world!, move-from s1 и сохраняет s2:

int main()  
{
   std::string s1("world");
   std::string s2("!");

   std::string* ptr = allocate_array<std::string>(
      std::forward_as_tuple("hello"),
      std::forward_as_tuple(3, ' '),
      std::forward_as_tuple(s1.begin(), s1.end()),
      std::forward_as_tuple(std::move(s2))
   );

   for (size_t i = 0; i < 4; i++)
      std::cout << ptr[i];

   std::destroy(ptr, ptr + 4);
   ::operator delete(ptr);
}

Но, честно говоря, я не совсем уверен, что мое решение на 100% правильное.

Демонстрационная версия здесь .

РЕДАКТИРОВАТЬ: Я добавил обработку исключений, что очень важно здесь.

1 голос
/ 29 октября 2019

Было бы замечательно сохранить пересылку аргументов ARGUMENTS ..., но применить это для всех экземпляров. Есть ли возможность сделать это?

Нет.

Вы можете переслать только один раз, потому что вы можете переместиться только один раз.

Предположим, что аргумент являетсяstring &&.

Отправляя его, вы говорите конструктору T, что он может использовать выделенную в нем память, оставляя его (я полагаю) пустым.

Когда вы пересылаете его дляво второй раз строка пуста.

...