LINUX.ORG.RU

Несколько flexible array member

 ,


0

2

Почему не добавят в стандарт, и какие приемы используют что бы реализовать это?

Описание того что я хочу:

struct String {
  int length;
  char str[length]; // == char str[];
                    // указание длинны, просто подсказка для компилятора
};

struct Item {
  struct String name; 
  struct String description;
  bool flag1, flag2, flag3;
  int elements_length;
  struct Element elements[elements_length]
};


// Item *i = malloc(999999999);
// (char*)&i->description.length == ((char*)&i->name.str) + i->name.length

Пока что самое удобное что я придумал, это вместо массивов вставлять указатели, и при инициализации их вычислять, но это ручная работа, человек ее делать не должен...

★★★★

Ты хочешь чтобы компилятор вычислял смещения полей складывая все length предшествующих им String? Это ужасно.

А если length что-то новое присвоить?

А как будет работать макрос стандартной библиотеки offsetof?

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

Ты хочешь чтобы компилятор вычислял смещения полей складывая все length предшествующих им String? Это ужасно.

Именно так, намного ужаснее делать это все вручную.

А если length что-то новое присвоить?

Старые указатели на последующие элементы станут неправильными, если ты про это.

А как будет работать макрос стандартной библиотеки offsetof?

Никак, можно ввести offsetof_dynamic.

MOPKOBKA ★★★★
() автор топика
Ответ на: комментарий от MOPKOBKA

Именно так, намного ужаснее делать это все вручную.

Обычно просто выделяют память под строки отдельно.

У твоей штуки ещё возникает проблема выравнивания. При некоторых значениях length и некоторых типах полей после оно будет падать на части архитектур и терять производительность на части других. Слишком implementation defined, чтобы пихать в стандарт.

KivApple ★★★★★
()
Ответ на: комментарий от KivApple

Оффсеты же вычисляет компилятор, поэтому падать не должно, он сам сделать выравнивание. Больше проблем с тем, как сделать sizeof() для malloc.

MOPKOBKA ★★★★
() автор топика
Ответ на: комментарий от MOPKOBKA

контент char str[] находится в памяти сразу за int length

А если длину нужно изменить, то что?

А в каком языке это есть?

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

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 2)
Ответ на: комментарий от no-such-file

А если длину нужно изменить, то что?

Меняй, в чем проблема?

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

Я могу и получше решение придумать, если для каждой длинны свой тип, то можно просто использовать шаблоны С++. У меня длинна определяется в рантайме, но не меняется.

MOPKOBKA ★★★★
() автор топика
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

Меняй, в чем проблема?

Проблема в том, что структура фиксированной длины, это определяется в компайлтайме. Не, можно конечно сделать строку меньше, а если надо больше? Она уже на стеке лежит, там ничего нельзя двигать.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Проблема в том, что структура фиксированной длины, это определяется в компайлтайме.

Ну вот нужно сделать что бы такие структуры не были фиксированной длинны (запретить sizeof). Либо дать ей sizeof с учетом что все массивы нулевой длинны.

Не, можно конечно сделать строку меньше, а если надо больше?

А как ты сделаешь строку больше если она определена как char path[MAX_PATH];? Тоже никак, это для определенных вариантов использования. Никто же не жалуется на это.

Она уже на стеке лежит, там ничего нельзя двигать.

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

MOPKOBKA ★★★★
() автор топика
Последнее исправление: MOPKOBKA (всего исправлений: 2)
s/String description/String *description/
s/struct String description/struct String *description/
s/elements[elements_length]/elements[]/
struct String {
  int length;
  char str[length]; // == char str[];
                    // указание длинны, просто подсказка для  --компилятора

Нет, length не constexpr.

thunar ★★★★★
()
Ответ на: комментарий от thunar

s/String description/String *description/

Да, я предложил этот вариант в ОП посте. На самом деле удобнее будет превратить массив в указатель, тогда расположить данные можно будет где угодно.

Нет, length не constexpr.

Компилятору и не надо знать значение length в момент компиляции.

MOPKOBKA ★★★★
() автор топика
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

Нет, нельзя такой член структуры делать посередине, даже если он лежит в другой структуре. https://shivankaul.com/blog/zero-length-arrays.

На самом деле удобнее будет превратить массив в указатель, тогда расположить данные можно будет где угодно.

Да, именно так. Тогда надо делать что-то вроде функции конструктора, которая сама замаллочит сколько надо памяти под обе структуры и инициализирует указатель на String соответствующим смещением.

thunar ★★★★★
()
Ответ на: комментарий от thunar

Нет, нельзя такой член структуры делать посередине, даже если он лежит в другой структуре. https://shivankaul.com/blog/zero-length-arrays.

Да, я знаю. Тред о несправедливости этого ограничения...

MOPKOBKA ★★★★
() автор топика
Ответ на: комментарий от MOPKOBKA

Т.е в памяти получится что-то вроде:

 *Item = {struct Item}{elements...}{struct String}{str...}

Тред о несправедливости этого ограничения...

Так при чём тут несправедливость, если это информация, которая недоступна (в принципе, по своей природе) в компайл-тайме? Если хочется что-то такое делать, то надо уже брать c++, презагрузить конструкторы, геттеры сеттеры. Можно даже что-то вроде small string optimization предусмотреть, что бы структура целиком на стеке жила при малых размерах.

thunar ★★★★★
()
Последнее исправление: thunar (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

как ты сделаешь строку больше если она определена как char path[MAX_PATH]

Никак, и да так можно в т.ч. завернуть в структуру с полем длины. Но ты же не это хочешь?

что бы такие структуры не были фиксированной длинны

Это невозможно. Вообще в принципе.

no-such-file ★★★★★
()

Почему не добавят в стандарт, и какие приемы используют что бы реализовать это?

Потому что оно там давно есть

template<int L>
struct String {
    const char str[L];
    const int length = L;
};

int main()
{
    String<3> s{"12"};
    std::cout << s.str << '\n';
}
PRN
()
Последнее исправление: PRN (всего исправлений: 1)

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

Если хочешь всем доказать, как это на самом деле просто и легко реализуемо, просто реализуй прототип. По результатам и оценим значимость идеи.

i-rinat ★★★★★
()

Я такое написал:
https://github.com/mittorn/OpenXR-API-Layer-Template/blob/wip/src/layer_event...
Правда парсинг думаю переделать ещё, вынеся сигнатуры в отдельный список
А так - используй в своём случае шаблонные аргументы в качестве длины, либо храни строки в куче после объекта

mittorn ★★★★★
()
Ответ на: комментарий от PRN

Размер может быть известен только в рантайме.

Я такое написал

Ну тут как я понял только компилтайм...

MOPKOBKA ★★★★
() автор топика
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Именно так, намного ужаснее делать это все вручную.

Вам в managed языки, где всё делают за вас, а сборщик мусора аккуратненько подтирает вам попу...

Старые указатели на последующие элементы станут неправильными, если ты про это.

Размер объекта изменится, но как ты предлагаешь об этом оповестить хранилище объекта? Место после объекта может быть чем-то занято

Никак, можно ввести offsetof_dynamic.

Попробуй реализовать «кортеж» с динамической длиной элементов сам. Такое в любом случае уже считать только в рантайме. Но в целом всё реализуемо. Только вот я не знаю, кому оно будет надо с такой производительностью и сложностью кода.
Вариант сделать там динамический вектор звучит куда проще по реализации. Если хочешь чтобы это можно было ещё по сети гонять - то тут остаётся 2 способа:
1. Напиши сериализатор (или возьми готовый, для STL контейнеров есть). Это позолит и в рантайме иметь более быстрый доступ к данным и в сетевых протоколах или файлах хранить объект, при этом всё ещё быстро конвертировать между этими представлениями
2. Сделай как по ссылке, что я в предыдущем комментарии кидал - пихни строки в конец и расчитывай размер объекта из параметра.
Кстати, в проекте что по ссылке с предыдущего поста есть ещё утилита, которая позволяет обходить и, например, сериализовывать объекты, при этом никакого STL.

mittorn ★★★★★
()
Ответ на: комментарий от KivApple

в структурах она не возникает, в структуре оффсет следующего поля выровнится если оно конечно не packed. А вот если реализовывать это самому - можно использовать, например, шаблон AlignOf и располагать элементы так же, как это делает компилятор
Здесь например дамп структур таким образом работает корректно:
https://github.com/mittorn/OpenXR-API-Layer-Template/blob/wip/src/struct_utl....

mittorn ★★★★★
()
Ответ на: комментарий от PRN

сообщи, когда параметры шаблона разрешат делать неконстантными.
К сожалению, вариант 'раскрыть шаблон на все допустимые значения" здесь не работает т.к будет очень много мусорного кода под каждую длину строки

mittorn ★★★★★
()
Ответ на: комментарий от mittorn

Вам в managed языки, где всё делают за вас, а сборщик мусора аккуратненько подтирает вам попу...

Как данная проблема решается в managed языках?

Размер объекта изменится, но как ты предлагаешь об этом оповестить хранилище объекта? Место после объекта может быть чем-то занято

Точно такая же проблема может быть и с единичным flexible array member.

Попробуй реализовать «кортеж» с динамической длиной элементов сам.

Никаких сложностей с реализацией у меня нету, я просто хотел бы делать это удобнее.

MOPKOBKA ★★★★
() автор топика
Ответ на: комментарий от MOPKOBKA

нет, в примере не совсем компилтайм. Там правда размер объекта захардкожен т.к мне не жадко максимальный размер выделить на стеке/в памяти, их будет не слишком много. Но я в переменные под строки пишу оффсеты строки, выделяя их внутри объекта. Можно было бы в конце написать [], но мне нужно эти команды в массив пихать. 2 дня ломал голову, как избежать овераллокации, думал сделать массив объектов динамического размера, но мне оно не нужно без быстрого доступа

mittorn ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Как данная проблема решается в managed языках?

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

Точно такая же проблема может быть и с единичным flexible array member.

Потому его и не используют практически. Это малоприменимая технология и если тебе действительно нужно что-то такое хранить - сделай один flexible array и несколько string_view или подмассивов в нём (не забывая про выравнивание в случае, если это не char)

mittorn ★★★★★
()
Ответ на: комментарий от mittorn

Так или иначе ты хочешь низкопроизводительную (на каждый доступ к полю структуры несколько арифметических операций, кеши и предсказатели переходов тоже не будут рады) со сложной реализацией в компиляторе требующей учесть кучу граничных случаев. И встроить в язык её тоже тяжело, потому что, как ты верно заметил, sizeof непонятно как делать (чтобы знать размер структуры, нужно прочитать её поля, но чтобы выделить память под структуру и иметь возможность читать/писать её поля нужно уже знать её размер). И всё ради чего? В чём проблема выделить память под строки отдельно и сохранить указатель в обычной структуре? В плюсах благодаря конструкторам и деструктурам с точки зрения юзера особо не будет отличий для конечного пользователя даже.

Если беспокоит медленность malloc или хочется держать строки рядом ради cache locallity, то это решается кастомным аллокатором.

Единственное, где прям принципиально может быть хранить строки последовательно в структуре - сериализация/десериализация. Но нормальные люди всё равно не дампят структуры на диск в сыром виде, потому что на разных платформах разный порядок байт и разное выравнивание. Всё равно есть какая-то прослойка сериализации/десериализации и в ней легко абстрагировать работы со строками как с указателями на данные в куче.

KivApple ★★★★★
()
Ответ на: комментарий от KivApple

на каждый доступ к полю структуры несколько арифметических операций

Не обязательно, можно переиспользовать вычисленный адрес.

о сложной реализацией в компиляторе

В чем сложность то?

В чём проблема выделить память под строки отдельно и сохранить указатель в обычной структуре?

А зачем так делать, если можно сделать лучше? В ограниченном количестве случаев.

Если беспокоит медленность malloc

Он молниеносный.

Единственное, где прям принципиально может быть хранить строки последовательно в структуре - сериализация/десериализация.

В том числе.

Но нормальные люди всё равно не дампят структуры на диск в сыром виде

Иногда дампят, но не на диск.

MOPKOBKA ★★★★
() автор топика
Ответ на: комментарий от KivApple

так я про это и говорил.
А пример с command делается как раз для эффективности по кэшу, чтобы выделять данные не где-то там в куче, а сразу в массиве. кастомный аллокатор потребует дополнительной синхронизации потоков при использовании в очередях (а там это есть), а от овераллокации в несколько кб никому плохо не станет. В целом я этим кодом из примера и случаем его использования в массиве тоже не шибко доволен. Возможно я сделаю массив и очередь с динамическим размером элемента и фиксированным буффером, буду спрашивать у объекта размер и докидывать выравнивание статической части, будет эффективно и по памяти, но чуть больше кода

mittorn ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Не обязательно, можно переиспользовать вычисленный адрес.

В чем сложность то?

Не сложность, а невозможность. Хочу array таких структурок с произвольным O(1) доступом.

А зачем так делать, если можно сделать лучше?

Нельзя.

bugfixer ★★★★
()
Ответ на: комментарий от MOPKOBKA

ну в общем - решение (которое у меня возможно будет совершенствоваться) я предложил
Нужно ли это в компиляторе? Наверно бы было бы удобно, но в целом не оправдано. Конечно, будь у нас dynamic_sizeof и dynamic_offsetof, на основе этого можно было бы легко сделать класс динамического массива-вектора-кортежа, который бы хранил поля разного размера. Попробуй форкнуть кланг и сделать что-либо подобное, тогда можно будет использовать это для proposal для новой фичи даже в какой-нибудь C26
А я в своём коде наверно сделаю быстрый arraylist, в целом там всё на него опрашивается
итерация по элементам тогда могла бы оптимизироваться до ptr += ptr->val или ptr += table[ptr->val], что в целом весьма cache-friendly по сранвению с linked list

mittorn ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Ну тут как я понял только компилтайм…

Все правильно. Но про рантайм ты не указывал)

Размер может быть известен только в рантайме.

Что-то такое тоже есть, но опять же с твоей постановкой задачи хз что тебе надо)). Размер буфера надо будет вычислять, это можно сделать из размеров имеющихся данных. А вообще, проще взять какой-нибудь протобуф или флатфлатбуферы.

#include <iostream>
#include <array>
#include <string>
#include <vector>
#include <memory_resource>


struct Element
{
};

struct Item {
    Item(std::pmr::polymorphic_allocator<char>& charAlloc,
    const char* n,
    const char* d,
    std::pmr::polymorphic_allocator<Element>& elementAlloc,
    std::initializer_list<Element> e)
        : name{n, charAlloc}
        , description{d, charAlloc}
        , elements{e, elementAlloc}
    {
    }
    
    std::pmr::string name; 
    std::pmr::string description;
    bool flag1{}, flag2{}, flag3{};
    std::pmr::vector<Element> elements;
};

int main()
{
    std::array<std::byte, 1024> buffer;
    std::pmr::monotonic_buffer_resource mbr{buffer.data(), buffer.size()};
    std::pmr::polymorphic_allocator<char> cpa{&mbr};
    std::pmr::polymorphic_allocator<Element> epa{&mbr};
    std::pmr::string str{cpa};
    
    Item item{cpa, "name", "description", epa, {Element{}, Element{}}};

    std::cout << item.name << "; " << item.description << "; " << item.elements.size() << '\n';
}
PRN
()
Ответ на: комментарий от PRN

Я честно говоря даже не знал что такое есть в STL. Думал, только самому писать. Но мне в любом случае самому писать, STL я только на работе соглашусь использовать

mittorn ★★★★★
()
Последнее исправление: mittorn (всего исправлений: 1)