Я борюсь с проблемой, когда какой-то устаревший код пытается общаться с современными системами. В частности, C++11 версии STL (бонусные баллы, если предлагаемое вами решение работает с C++03, отрицательные баллы, если решение работает только с C++17, но меня все еще интересует случае я могу использовать это как аргумент для обновления).
Моей функции передается массив void* и трех указателей на функции, которые являются функциями для сравнения, копирования и освобождения данных в каждом указателе void (все указатели void имеют один и тот же тип данных для любого заданного вызова). Короче говоря, у меня есть все необходимое для того, чтобы эти void* выглядели как объекты, но на самом деле они не являются объектами.
В моей функции я хотел бы использовать некоторые библиотеки для std::set и std::map с этими данными (также некоторые другие библиотеки в нашей собственной кодовой базе, но set и map — хорошие отправные точки). С void* нужно обращаться как с объектами-значениями, т. е. когда я делаю mySet.insert(x), это должно выделять новый указатель (у меня есть способ запросить размер указателя), а затем вызывать функцию копирования чтобы скопировать содержимое x в набор.
TL;DR: Кто-нибудь знает, как написать собственный распределитель, который будет работать с std::set для типа, чьи инструкции копирования/удаления не включены в конструктор копирования?
---------- конец вопроса (остальное я уже пробовал) ---------
Очевидно, я мог бы объединить четыре вещи в класс:
class BundleStuffTogether {
BundleStuffTogether(void *data, CompareFunc compare, CopyFunc copy, DeallocFunc dealloc);
// And create the rest of class accordingly, storing the values,
// and with destructor calling dealloc, copy constructor calling
// copy, etc.
};
Я хотел бы избежать выделения этого класса, если смогу. Мне не нужны накладные расходы памяти для каждой записи в наборе, требующем 4-кратного размера (многие указатели относятся к небольшим объемам данных, поэтому относительный размер велик).
Я рассматриваю просто использование std::set и творческое заполнение этих двух пробелов. Я могу передать указатель функции сравнения в качестве объекта сравнения для набора и карты. Это просто. Более сложная часть — это выделение, освобождение и копирование.
Я пытался написать собственный аллокатор, и он действительно заработал, когда я вызвал его непосредственно в тестах, но когда я подключил его к std::set, все развалилось. Я создал класс только для того, чтобы удерживать void*, чтобы иметь возможность специализации шаблона распределителя (подробности ниже).
Мой первый трюк состоял в том, чтобы создать этот класс:
class Wrapper {
public: void *_ptr;
};
// which allows for this, given "void *y":
Wrapper *wrap = static_cast<Wrapper*>(&y);
Это дает мне определенный тип, который я могу использовать для специализации шаблона. Используя это, я попытался создать успешную специализацию типа std::allocator для Wrapper. Это работало для Wrapper само по себе, но развалилось, когда я попытался добавить дополнительные поля пользовательской специализации для хранения функций — распределители должны сравниваться как равные.
Затем я клонировал std::allocator и создал свой собственный класс шаблона MyAllocator — точно такой же код, как у std::allocator, — а затем создал специализацию шаблона для Wrapper. Затем я дал ОБА основному шаблону и моей специализации функции, необходимые для управления пустотой*, так что теперь они сравниваются как равные.
И это было успешным в качестве распределителя! Я протестировал несколько вариантов, используя аллокаторы напрямую, и это сработало. Но он развалился, когда я подключил его к std::set. Набор не выделяет мой класс напрямую. Он выделяет узлы, которые содержат мой класс... и узел предполагает, что у моего класса есть конструктор копирования. вздох Я думал, что контракт с std::set заключается в том, что он будет использовать метод "construct" для фактического создания объекта Wrapper в своем собственном узле, но, по-видимому, это не так.
Так что теперь я застрял. С++ 17 сообщает, что он даже устарел для методов «создать» и «уничтожить», на которые я делал ставку, поэтому в дальнейшем похоже, что нет никакого способа подключить пользовательский конструктор вообще.
Кто-нибудь может предложить решение, отличное от решения BundleStuffTogether, которое было у меня в начале? Моя следующая лучшая идея — выделить std::set и переписать его внутренности, и я действительно не хочу идти по этому пути, если смогу этого избежать.
void*
будьте очень осторожны в отношении разницы между указателем и на что он указывает. Например, вы, конечно же, не хотите выделять новый указатель и копировать в него данные. Вы хотите выделить память и скопировать в нее данные. - person Pete Becker   schedule 12.04.2018