Определите, что std :: shared_ptr содержит необработанный массив (и получает его размер) - PullRequest
1 голос
/ 10 апреля 2020

Я работаю над (еще одной) библиотекой сериализации C ++, которая поддерживает стандартные типы, такие как контейнеры. В частности, я хочу поддержать умные указатели. В

C ++ 17 появилась поддержка std::shared_ptr для хранения необработанных массивов (в таких случаях он вызывает delete []). Мне нужно обнаружить, что shared_ptr содержит необработанный массив, чтобы я мог соответствующим образом его сериализовать:

template <typename T>
void serialize(Writer& writer, const std::shared_ptr<T> ptr)
{
    // Writer has overloaded operator()

    if (ptr)
    {
        if (holdsRawArray(ptr)) // How to implement this???
        {
            auto arrayWriter = writer.array(); // RAII
            auto size = rawArraySize(ptr); // How to get this???
            for (std::size_t i=0; i<size; ++i) 
                arrayWriter(ptr[i]);
        }
        else
            writer(*ptr);
    }
    else
        writer(null);
}

Как определить, что интеллектуальный указатель C ++ 17 содержит необработанный массив? Эта информация удаляется в element_type элементе typedef (через std::remove_extent_t). Я также не могу найти ничего в API интеллектуального указателя, который показал бы размер необработанного массива.

Я думал об использовании идиомы детектора на operator[] и operator*, но, похоже, реализации не обязаны их определять, если T является или не является необработанным массивом.

Возможно ли то, что я пытаюсь сделать возможным? Я надеюсь, что что-то пропустил, или что есть какой-то трюк, который я мог бы использовать.

Я знаю, что могу заставить пользователей использовать std::shared_ptr<std::array<N,T>> или std::shared_ptr<std::vector<T>> вместо этого, но я просто хочу проверить, возможно ли это для меня, чтобы поддержать умные указатели, содержащие необработанные массивы.

Ответы [ 2 ]

2 голосов
/ 10 апреля 2020

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

if constexpr (std::is_array_v<T>)

Но получить размер невозможно, поскольку он размещается динамически и нигде не сохраняется.

0 голосов
/ 11 апреля 2020

Как прокомментировал Jarod42, я могу иметь доступ к необработанному размеру массива, если используется std::shared_ptr<T[N]> (но не std::shared_ptr<T[]>).

Поэтому я могу добавить перегрузку serialize, чтобы получить размер N через параметры шаблона и в то же время знать, что shared_ptr содержит массив.

#include <iostream>
#include <memory>
#include <string>
#include <type_traits>

struct Writer // Toy example
{
    template <typename T>
    void operator()(const T& v) {std::cout << v << ", ";}
};

struct ArrayWriter  // Toy example
{
    ArrayWriter() {std::cout << "[";}
    ~ArrayWriter() noexcept {std::cout << "], ";}

    template <typename T>
    void operator()(const T& v) {std::cout << v << ", ";}
};

// "Regular" shared_ptr
template <typename T>
void serialize(Writer& writer, const std::shared_ptr<T> ptr)
{
    static_assert(!std::is_array_v<T>,
                  "shared_ptr<T[]> not supported: size unknowable");
    if (ptr)
        writer(*ptr);
    else
        writer("null");
}

// shared_ptr holding an array of known size
template <typename T, std::size_t N>
void serialize(Writer& writer, const std::shared_ptr<T[N]> ptr)
{
    if (ptr)
    {
        ArrayWriter arrayWriter;
        static constexpr auto size = N;
        for (std::size_t i=0; i<size; ++i) 
            arrayWriter(ptr[i]);
    }
    else
        writer("null");
}

int main()
{
    Writer writer;
    std::shared_ptr<std::string> s{new std::string{"Hello"}};
    std::shared_ptr<int[3]> n{new int[3]}; // Error prone!
    std::shared_ptr<float[]> x{new float[5]}; // Size lost
    n[0] = 1; n[1] = 2; n[2] = 3;
    serialize(writer, s); // Outputs Hello,
    serialize(writer, n); // Outputs [1, 2, 3, ],
    // serialize(writer, x); // static assertion failure

    return 0;
}

Рабочий пример: https://onlinegdb.com/r1R5jv0wI

Спарик все еще прав в своем ответе о том, что не может узнать размер, если ему дано shared_ptr<T> неожиданно (мне следовало бы сформулировать свой вопрос лучше).

Дэвид Шварц отметил в комментариях, что Пользователь может не правильно инициализировать элементы массива (или использовать неправильный размер Dynami c), и мой сериализатор не сможет узнать об этом. Даже если умные указатели, содержащие необработанные массивы динамических c, могут работать в моем случае, не стоит поддерживать их.

Спасибо всем за ваши комментарии и ответы!


Приложение

Согласно этому ответу , unique_ptr<T[N]> плохо сформировано и действительно не работает для меня в G CC.

Я обнаружил P0674R1 (Расширение make_shared для массивов поддержки) , и в нем приводится несколько примеров shared_ptr<T[N]>, поэтому представляется законным допустить, что комитет не запретил его, когда он был принят.

Немного раздражает, что unique_ptr и shared_ptr не ведут себя одинаково в этом отношении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...