В очередной раз пытаюсь написать перечисление с возможностью перебора элементов.
Возникает следующий вопрос:
в нижеприведённом коде вектор s_enum будет содержать значения в том же порядке, что они записаны в классе или же порядок не гарантируется?
Здравствуйте, B0FEE664, Вы писали:
BFE>в нижеприведённом коде вектор s_enum будет содержать значения в том же порядке, что они записаны в классе или же порядок не гарантируется?
Интересный вопрос! В стандарте как-то это не очень освещено.
Вообще, выглядит логичным, что в каждой единице трансляции пачка определений (инлайновых) этих статических членов идёт в одном и том же порядке, поэтому и конструироваться они будут — неизвестно в какой единице трансляции, но точно перед main и точно подряд.
Но я бы постарался отмахаться от динамической инициализации вовсе.
Сделал бы какую-нибудь структуру — массив статических констант — с помощью кодогенерации.
Чтобы это всё превратилось вот в такое
Здравствуйте, Кодт, Вы писали:
К>Вообще, выглядит логичным, что в каждой единице трансляции пачка определений (инлайновых) этих статических членов идёт в одном и том же порядке, поэтому и конструироваться они будут — неизвестно в какой единице трансляции, но точно перед main и точно подряд.
Хмм. "точно перед main и точно подряд"? Т.е. мьютек в EnumValue будет лишним? Или нет?
PS Понятно, что с кодогенерацией всё быстро и безопасно можно сделать, но это подходит только если кодогенерация уже используется в проекте, а добавлять кодогенерацию в процесс только ради перечислений чересчур затратно.
Здравствуйте, B0FEE664, Вы писали:
К>>Вообще, выглядит логичным, что в каждой единице трансляции пачка определений (инлайновых) этих статических членов идёт в одном и том же порядке, поэтому и конструироваться они будут — неизвестно в какой единице трансляции, но точно перед main и точно подряд.
BFE>Хмм. "точно перед main и точно подряд"? Т.е. мьютек в EnumValue будет лишним? Или нет?
Точно перед майн и точно подряд.
Однако, если у тебя длл/сошка, то "перед майн" и "в однопоточном окружении" уже неточно. И это — одна из причин, по которой с динамической инициализацией лучше по возможности не связываться.
Один раз написать или припахать какое-нибудь готовое решение на макросах.
Рефлексию энумов же давно колхозят все, кому не лень. (И на буст-препроцессоре это в пять строчек можно сделать).
Здравствуйте, Кодт, Вы писали:
К>Интересный вопрос! В стандарте как-то это не очень освещено. К>Вообще, выглядит логичным, что в каждой единице трансляции пачка определений (инлайновых) этих статических членов идёт в одном и том же порядке, поэтому и конструироваться они будут — неизвестно в какой единице трансляции, но точно перед main и точно подряд.
Разве стандарт не гарантирует в одном файле инициализацию по порядку следования ?
Иначе бы простейший код просто бы не работал:
Здравствуйте, B0FEE664, Вы писали:
BFE>Хмм. "точно перед main и точно подряд"?
Точно перед main, а вот порядок сохранять вроде никто не обещает
BFE>Т.е. мьютек в EnumValue будет лишним? Или нет?
Инициализация статических переменных потоекобезопасна, начиная с 11го стандарта, до этого таких гарантий не было
BFE>PS Понятно, что с кодогенерацией всё быстро и безопасно можно сделать, но это подходит только если кодогенерация уже используется в проекте, а добавлять кодогенерацию в процесс только ради перечислений чересчур затратно.
Я как раз добавил только для перечислений, и теперь не нарадуюсь. Мне эта кодогенерация сильно жизнь упростила, теперь во всех проектах enum'ы только через генерацию и делаю. Бонусом получилось то, что очень удобно все эти перечисления пробрасывать во встраиваемый скриптовый движок
Можно настроить генерацию под любой язык, только конфиги надо напилить, по умолчанию аплаятся такие флаги:
umba-enum-gen --lang=cpp --template=default
Для разной целевой генерации можно использовать разные стили именования:
--namespace-name-style=STYLE
--enum-name-style=STYLE
--enum-values-style=STYLE
DefaultStyle - use name as is.
CppStyle - lowercase name parts separated by underscore.
CamelStyle - first name part is in lowercase, next ones - PascalStyle.
PascalStyle - all name parts starts with uppercas letter.
CppCamelMixedStyle - name parts separated by underscore, first is in
lowercase, next ones - PascalStyleCppPascalMixedStyle - all PascalStyle name
parts separated by underscore.
DefineStyle - uppercase name parts separated by underscore.
--enum-serialize-style=STYLE
All - generate all style names for deserialization. If this serialize value
name style taken, PascalStyle names used to serialize value.
DefaultStyle - use name as is.
HyphenStyle - lowercase name parts separated by hyphen.
HyphenCamelMixedStyle - name parts separated by hyphen, first is in
lowercase, next ones - PascalStyle.
HyphenPascalMixedStyle - name parts separated by hyphen, all part in
PascalStyle.
CppStyle - lowercase name parts separated by underscore.
CamelStyle - first name part is in lowercase, next ones - PascalStyle.
PascalStyle - all name parts starts with uppercas letter.
CppCamelMixedStyle - name parts separated by underscore, first is in
lowercase, next ones - PascalStyleCppPascalMixedStyle - all PascalStyle name
parts separated by underscore.
DefineStyle - uppercase name parts separated by underscore.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, _NN_, Вы писали:
_NN>>Разве стандарт не гарантирует в одном файле инициализацию по порядку следования ?
M>Не гарантирует
M>Вот такой код тоже будет работать: M>
Здесь у нас другой код.
В оригинале нет extern.
Поэтому порядок должен быть.
Насколько я вижу GCC (другие тоже ?) инициализирует литералы сразу, а то что можно вычислить по мере объявления
#include <iostream>
extern int a;
extern int b;
int d = a + 100; // 100 , "а" не вычисленint a = b + 111; // 222int b = 111; // литерал 111int c = a + 100; // 322 , "а" вычисленint main()
{
std::cout << a << " " << b << " " << c << " " << d;
}
Здравствуйте, Marty, Вы писали:
M>andrey.desman, ты с чем не согласен? Не прокомментируешь? M>Я вполне могу ошибаться, и хотелось бы знать, в чем я не прав. Мы ж тут не в "политике", чтобы молчаливыми минусами разбрасываться
Гарантирует инит по порядку. У тебя для i константная инициализация, она выполяется до динамической.
А для j уже динамическая инициализация, она выполняется после.
Но порядок интересен для динамической инициализации, он в рамках юнита определен.
_NN>Насколько я вижу GCC (другие тоже ?) инициализирует литералы сразу, а то что можно вычислить по мере объявления
Да, константная инициализация выполняется до динамической.
Все, что еще не динамически инициализировано, инициализировано нулями. Ниже у тебя не a не вычислен, а a проинициализирован статически, но еще не проинициализирован динамически.
_NN>
_NN>int d = a + 100; // 100 , "а" не вычислен
_NN>int a = b + 111; // 222
_NN>
Здравствуйте, Marty, Вы писали:
BFE>>Хмм. "точно перед main и точно подряд"? M>Точно перед main,
Это ведь гарантии реализации, а не стандарта?
M> а вот порядок сохранять вроде никто не обещает
Для членов класса могли бы и добавить...
BFE>>Т.е. мьютек в EnumValue будет лишним? Или нет? M>Инициализация статических переменных потоекобезопасна, начиная с 11го стандарта, до этого таких гарантий не было
Насколько я понимаю, потокобезопасность гарантирует только, что одна и та же переменная не будет инициализироваться одновременно из двух разных потоков, но не гарантирует, что две разные переменные не будут инициализироваться одновременно.
Т.е. инициализация переменных:
Может производится параллельно, WARNING в одном потоке, а MINOR — другом, а тогда исполнение EnumValue будет происходить одновременно в двух разных потоках:
а такого push_back вектора не переживёт...
Или я ошибаюсь и потокобезопасность инициализации статических переменных гарантирует, что инициализация ни одной статической переменной не может происходить одновременно с инициализацией другой статической переменной?
Здравствуйте, B0FEE664, Вы писали:
BFE>>>Хмм. "точно перед main и точно подряд"? M>>Точно перед main, BFE>Это ведь гарантии реализации, а не стандарта?
Не точно перед main вообще-то.
M>> а вот порядок сохранять вроде никто не обещает BFE>Для членов класса могли бы и добавить...
Так и есть.
BFE>>>Т.е. мьютек в EnumValue будет лишним? Или нет? M>>Инициализация статических переменных потоекобезопасна, начиная с 11го стандарта, до этого таких гарантий не было BFE>Насколько я понимаю, потокобезопасность гарантирует только, что одна и та же переменная не будет инициализироваться одновременно из двух разных потоков, но не гарантирует, что две разные переменные не будут инициализироваться одновременно.
2) Partially-ordered dynamic initialization, which applies to all inline variables that are not an implicitly or explicitly instantiated specialization. If a partially-ordered V is defined before ordered or partially-ordered W in every translation unit, the initialization of V is sequenced before the initialization of W (or happens-before, if the program starts a thread).
То есть happens-before в случае многопоточки. При условии, конечно, что они везде определены в одном и том же порядке, что для классов обычно верно. Ты же не пишешь классы дважды с разным порядком статик инлайнов?
Что касается отложенной инициализации, то там же ниже:
If the initialization of an inline variable is deferred, it happens before the first ODR-use of that specific variable.
То есть, они могут быть не все инициализированы (например, до MAJOR, а CRITICAL будет uninit), но только по порядку.
Но это все тлен, инициализация этих inline Enum никак не связана с вектором, поэтому во-первых, по факту мьютекс на вектор нужен, в том числе и на чтение, а во-вторых, мьютекс все равно не спасет от пустого вектора.
Лучше, как советовал Кодт, константная инициализация.
Здравствуйте, andrey.desman, Вы писали:
AD>То есть happens-before в случае многопоточки. При условии, конечно, что они везде определены в одном и том же порядке, что для классов обычно верно. Ты же не пишешь классы дважды с разным порядком статик инлайнов?
Ага. Спасибо.
AD>Что касается отложенной инициализации, то там же ниже: AD>
AD>If the initialization of an inline variable is deferred, it happens before the first ODR-use of that specific variable.
AD>То есть, они могут быть не все инициализированы (например, до MAJOR, а CRITICAL будет uninit), но только по порядку. AD>Но это все тлен, инициализация этих inline Enum никак не связана с вектором, поэтому во-первых, по факту мьютекс на вектор нужен, в том числе и на чтение, а во-вторых, мьютекс все равно не спасет от пустого вектора.
Интересно.
Действительно, от пустого вектора не спастись... Хотя, если сделать value_type объектом с конструктором в который добавить side effect...:
However, as long as anything from a translation unit is ODR-used, all non-local variables whose initialization or destruction has side effects will be initialized even if they are not used in the program.
То всё должно получится.
Кстати, зачем мьютекс если есть гарантия на happens-before?
Здравствуйте, B0FEE664, Вы писали:
AD>>Что касается отложенной инициализации, то там же ниже: AD>>
AD>>If the initialization of an inline variable is deferred, it happens before the first ODR-use of that specific variable.
AD>>То есть, они могут быть не все инициализированы (например, до MAJOR, а CRITICAL будет uninit), но только по порядку. AD>>Но это все тлен, инициализация этих inline Enum никак не связана с вектором, поэтому во-первых, по факту мьютекс на вектор нужен, в том числе и на чтение, а во-вторых, мьютекс все равно не спасет от пустого вектора. BFE>Интересно. BFE>Действительно, от пустого вектора не спастись... Хотя, если сделать value_type объектом с конструктором в который добавить side effect...:
То ничего не поменяется. Статику что конструктор завть, что через функцию инициализироваться — все одно.
И вообще, это касается non-inline статиков. С inline все сложнее.
BFE>
BFE> However, as long as anything from a translation unit is ODR-used, all non-local variables whose initialization or destruction has side effects will be initialized even if they are not used in the program.
BFE>То всё должно получится. BFE>Кстати, зачем мьютекс если есть гарантия на happens-before?
Это гарантия на инициализацию статик инлайнов относительно друг друга. Но они никак не связаны с вектором, в который ты их добавляешь. В одном месте ты можешь взять вектор через RefValues() и что-то с ним делать, а в другом будет происходить отложенная инициализация. Так в теории по стандарту. На практике, скорее всего, этот набор инлайнов попадет в первый попавшийся cpp и их инициализация произойдет со всеми остальными статиками из этого cpp до входа в main, но это не точно.
Здравствуйте, B0FEE664, Вы писали:
BFE>В очередной раз пытаюсь написать перечисление с возможностью перебора элементов. BFE>Возникает следующий вопрос: BFE>в нижеприведённом коде вектор s_enum будет содержать значения в том же порядке, что они записаны в классе или же порядок не гарантируется?
Не гарантируется и не следует на это пытаться полагаться! Проблемой может стать вовсе не компилятор, а линкер.
Хотя обычно порядок более-менее сохраняется и это может вводить в заблуждение.
Здравствуйте, _NN_, Вы писали:
_NN>Разве стандарт не гарантирует в одном файле инициализацию по порядку следования ?
Ни разу. Все подобные примеры лишь демонстрируют, что инициализация POD-типов превращается
в константы в .data секции и они "инициализируются" ещё при компиляции (при запуске сразу содержат
какие надо значения).
Здравствуйте, K13, Вы писали:
BFE>>В очередной раз пытаюсь написать перечисление с возможностью перебора элементов.
K13>А чем не устраивает magic_enum ? K13>https://github.com/Neargye/magic_enum
Всем, кроме того, что на всякий чих тащить в проект миллион библиотек с гитхаба -- дурная идея.