Каков «правильный» способ размещения буферов переменного размера в C ++? - PullRequest
2 голосов
/ 14 сентября 2011

Это очень похоже на этот вопрос , но ответы на самом деле не отвечают на этот вопрос, поэтому я подумал, что я бы снова спросил:

Иногда я взаимодействую с функциями, которые возвращают переменнуюструктуры;например, FSCTL_GET_RETRIEVAL_POINTERS в Windows возвращает структуру RETRIEVAL_POINTERS_BUFFER переменного размера.

Использование malloc / free не рекомендуется в C ++, и поэтому мне было интересно:
Каков «правильный» способ размещения буферов переменной длины в стандартном C ++ (т.е. без Boost и т. Д.)?

vector<char> небезопасен по типу (и ничего не гарантируето выравнивании , если я правильно понимаю), new не работает с распределениями нестандартного размера, и я не могу придумать хорошую замену.Есть идеи?

Ответы [ 6 ]

5 голосов
/ 14 сентября 2011

Я бы использовал std::vector<char> buffer(n). На самом деле в C ++ нет такой вещи, как структура с переменным размером, поэтому вы должны ее подделать; выбрасывать безопасность типа из окна.

2 голосов
/ 14 сентября 2011

Если вам нравится malloc() / free(), вы можете использовать

RETRIEVAL_POINTERS_BUFFER* ptr=new char [...appropriate size...];

... do stuff ...

delete[] ptr;

Цитата из стандарта относительно выравнивания (expr.new/10):

ДляВ массивах char и unsigned char разница между результатом выражения new и адресом, возвращаемым функцией размещения, должна быть целым кратным самого строгого требования фундаментального выравнивания (3.11) любого типа объекта, размер которого не превышаетразмер создаваемого массива.[Примечание: поскольку предполагается, что функции выделения возвращают указатели на хранилище, которые соответствующим образом выровнены для объектов любого типа с фундаментальным выравниванием, это ограничение на накладные расходы на выделение массивов позволяет распространить идиому распределения массивов символов, в которые впоследствии будут помещены объекты других типов.,- конец примечания]

2 голосов
/ 14 сентября 2011

Я не вижу причин, почему вы не можете использовать std::vector<char>:

{
   std::vector<char> raii(memory_size); 
   char* memory = &raii[0];

  //Now use `memory` wherever you want
  //Maybe, you want to use placement new as:

   A *pA = new (memory) A(/*...*/); //assume memory_size >= sizeof(A);
   pA->fun();
   pA->~A(); //call the destructor, once done!

}//<--- just remember, memory is deallocated here, automatically!

Хорошо, я понимаю вашу проблему с выравниваниемЭто не так сложно.Вы можете сделать это:

A *pA = new (&memory[i]) A();
//choose `i` such that `&memory[i]` is multiple of four, or whatever alignment requires
//read the comments..
0 голосов
/ 15 сентября 2011

Хорошо, давайте начнем с самого начала.Идеальный способ вернуть буфер переменной длины:

MyStruct my_func(int a) { MyStruct s; /* magic here */ return s; }

К сожалению, это не работает, так как sizeof (MyStruct) вычисляется во время компиляции.Все, что переменной длины, просто не помещается в буфер, размер которого рассчитывается во время компиляции.Следует отметить, что это происходит с каждой переменной или типом, поддерживаемым c ++, поскольку все они поддерживают sizeof.В C ++ есть только одна вещь, которая может обрабатывать размеры буферов во время выполнения:

MyStruct *ptr = new MyStruct[count];

Так что все, что собирается решить эту проблему, обязательно будет использовать массив новой версии.Это включает в себя std :: vector и другие решения, предложенные ранее.Обратите внимание, что у трюков, таких как размещение новых для массива char, точно такая же проблема с sizeofБуферам переменной длины просто нужны куча и массивы.Нет никакого способа обойти это ограничение, если вы хотите остаться в рамках c ++.Далее требуется более одного объекта!Это важно.Вы не можете сделать объект переменной длины с C ++.Это просто невозможно.

Ближайший к объекту переменной длины, который предоставляет c ++, - это "переход от типа к типу".Каждый объект не обязательно должен быть одного типа, и вы можете во время выполнения манипулировать объектами разных типов.Но каждая часть и каждый завершенный объект все еще поддерживают sizeof, и их размеры определяются во время компиляции.Программисту остается только выбрать, какой тип вы используете.

Итак, каково наше решение проблемы?Как вы создаете объекты переменной длины?std :: string предоставляет ответ.Он должен иметь более одного символа внутри и использовать альтернативу массива для выделения кучи.Но все это обрабатывается stdlib, и программисту не нужно заботиться.Тогда у вас будет класс, который манипулирует этими std :: strings.std :: string может это сделать, потому что на самом деле это две отдельные области памяти.Sizeof (std :: string) возвращает блок памяти, размер которого можно вычислить во время компиляции.Но фактические данные переменной длины находятся в отдельном блоке памяти, выделенном версией массива new.

Версия массива new имеет некоторые собственные ограничения.sizeof(a[0])==sizeof(a[1]) и т. Д. Сначала выделение массива, а затем размещение нового для нескольких объектов разных типов обойдут это ограничение.

0 голосов
/ 15 сентября 2011

std::vector<char> просто отлично.Как правило, вы можете вызвать низкоуровневую c-функцию с аргументом нулевого размера, чтобы вы знали, сколько нужно.Затем вы решаете проблему с выравниванием: просто выделите больше, чем вам нужно, и сместите начальный указатель:

Допустим, вы хотите, чтобы буфер был выровнен до 4 байтов, выделите needed size + 4 и добавьте 4 - ((&my_vect[0] - reinterpret_cast<char*>(0)) & 0x3).

Затем вызовите вашу c-функцию с требуемым размером и смещенным указателем.

0 голосов
/ 15 сентября 2011

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

sizeof(DWORD) + sizeof(LARGE_INTEGER)

плюс

ExtentCount * sizeof(Extents)

(Я уверен, что вы более знакомы с этой структурой данных, чем я - вышеизложенное в основном предназначено для будущих читателей вашего вопроса).

Пул памяти сводится к тому, чтобы «выделить кучу памяти, а затем выделитьэта память на мелкие кусочки, используя свой собственный быстрый распределитель ".Вы можете создать свой собственный пул памяти , но, возможно, стоит взглянуть на Повышает пул памяти , который является библиотекой с чистыми заголовками (без DLL!).Обратите внимание, что я не использовал библиотеку пула памяти Boost, но вы спрашивали о Boost, поэтому я подумал, что упомяну это.

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