Выделение std :: vector с помощью пользовательского распределителя и типа неизвестно во время выполнения - PullRequest
0 голосов
/ 02 ноября 2018

Для вопроса предположим следующий поток кода:

void * some_data = allocate_memory(const std::string & type)

fetch_data_from_somewhere(some_data);

send_data_to_client(some_data);

allocate_memory просто выделит пустое сообщение, которое будет заполнено (включая потенциальное дальнейшее выделение динамической памяти) через fetch_data_from_somewhere, а затем отправлено куда-нибудь на send_data_to_client. Затем клиент преобразует данные в реальный тип (о котором знает только он).

Теперь я должен написать функцию в allocate_memory, учитывая только строку, идентифицирующую тип. С помощью этой информации я могу получить структуру данных, описывающую сообщение (например: его длина составляет 80 байтов, в первых 4 байтах целое число, потом std :: vector и т. Д.).

Представьте, что я знаю, что должен выделить std::vector<T>, где T - это неизвестный мне тип (опять же, я знаю строковый идентификатор типа и могу получить информацию о его структуре, размере и т. Д.).

Если я использую void * some_memory = calloc(sizeof(std::vector<T>), 1), похоже, это работает для всех T (кроме логических значений) с компиляторами gcc, clang и MSVC ++. Позже клиент может просто выполнить static_cast<std::vector<T>>(some_memory), и использование всех векторных операций, похоже, работает. Это кажется несколько разумным, хотя я не могу сказать, гарантированно ли это работает.

Однако, если я хочу передать свой собственный распределитель, мне кажется, что мне не повезло: я ожидаю, что память с нулевым распределением не будет работать, но я не могу действительно void * = new std::vector<T, MyAllocator>(0, my_allocator) или подобное, потому что я не могу знать T. Если бы вместо этого мне понадобился вектор типа std::vector<T *>, я бы предположил, что можно было бы просто использовать std::vecotr<void *>, поскольку данные в любом случае нетипизированы в памяти.

Есть ли способ выделить std::vector<T, MyAllocator> с неизвестным типом T во время компиляции (кроссплатформенный ...)?

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

1 Ответ

0 голосов
/ 02 ноября 2018

Простое выделение группы памяти не делает вектор. Ваше calloc решение неверно, потому что оно фактически не создает вектор, плюс память, в которой вы его создадите *, не была правильно выровнена. Тот факт, что вы неоднократно отмечали, что этот метод «работает», является чистой неудачей; теоретически и на практике можно ожидать, что он катастрофически взорвется.

Даже если все, что вам нужно было сделать, это выделить немного памяти, вы все равно не могли бы сделать это с вектором неизвестного типа, потому что вы не знаете, насколько велик vector<unknown-type>. Конечно, вы не можете построить один. Даже ваша идея vector<T*> & rarr; vector<void*> не работает: вы забываете, что каждая специализация vector является совершенно отдельным типом, а отношения между типами значений не создают отношений между результирующими типами контейнеров.

Ваш подход слишком наивен, чтобы успешно генерировать объекты C ++ в целом. Есть причина, по которой C ++ дает нам operator new и распределители, которые работают в гармонии. Старого подхода в стиле C «дай память, используй память» просто недостаточно. Тот факт, что вы затем хотите «отправить данные клиенту», также является красным флагом: вы не можете сериализовать сложные объекты C ++, выгрузив их непосредственные байты в сеть.

Наконец, мое обязательное предупреждение о том, что C ++ - это абстракция, и что если вы пытаетесь написать «умный» исходный код, который манипулирует объектами на уровне байтов, вам нужно быть очень осторожным. C ++ ожидает, что вы в буквальном смысле расскажете ему, когда вы хотите, чтобы объект был создан, и буквально расскажете ему, когда вы хотите, чтобы этот объект был уничтожен; если этого не сделать, то притворство о том, что объект существует, подвержено всевозможным странностям, поскольку компиляторы (включая, но не ограничиваясь этим, на этапе их оптимизации) в полной мере используют сложные, сильно семантические правила, составляющие язык. В общем, вам действительно нужно «сказать, что вы имеете в виду», а не пытаться идти за его спиной. (Существуют некоторые ограниченные исключения из этого правила, в основном снисходительность к объектам встроенного типа и возможность создавать псевдонимы для существующих правильно построенных объектов, используя char*; ни одно из этих исключений не относится к вашему требованию.)

Придерживайтесь предоставленных средств для строительства / размещения и напишите сериализатор.

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