LINUX.ORG.RU

СИ: enum VS #define

 


1

2

У меня зачастую бывают такие ситуации, что я как осёл между двумя стогами сена стою и не могу для себя решить, что использовать в данном конкретном случае.

Напишите, чем вы руководствуетесь, когда решаете использовать дефайны или енумы.

P.S. Тема не имеет под собой какого-то конкретного случая. Просто хочу посмотреть на опыт других программистов.

★★
Ответ на: комментарий от EXL

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

Лучше вообще не использовать, если это касается доступа к железу, типа

#define RMAP_NESTED_LPID_MASK		0xFFF0000000000000UL
#define RMAP_NESTED_GPA_MASK		0x000FFFFFFFFFF000UL

Тут я дефайну альтернативы не вижу

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

всегда так делаю. это не паскалевский enum какой-нить. в си и плюсах, это просто набор ПРОИЗВОЛЬНЫХ целочисленных констант.

«Можно ли на одноколесном велосипеде проехать по канату, натянутому между двумя девятиэтажками, держа в руках кипящий самовар ?»

В том смысле, что я не знаю (и не хочу знать) какие проблемы здесь могут всплыть с размером, выравниванием и прочей эндиэнностью на различных платформах

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

В том смысле, что я не знаю (и не хочу знать) какие проблемы здесь могут всплыть с размером, выравниванием и прочей эндиэнностью на различных платформах

это к чему вообще? чем отличается задание константы через дефайн и через енум… для эндиэнности, размера и выравнивания?

дефайны это просто калька с ассемблерных дефайнов. а сишные енумы - уже какой-то закос в типы и структурирование.

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

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

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

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

а там все правильно

Конечно, все правильно. И с выравниванием будет все правильно. И с эндианностью.

Вопрос тут только в том, сколько времени займет со всеми этими «правильностями» на каждой платформе разобраться, вместо того чтобы изначально всего этого избежать

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

И с выравниванием будет все правильно. И с эндианностью.

А можно пример проблемы? Потому что в норме битовые флаги uint и положительные целые в него конвертируютя без сюрпризов. Я бы очень хотел посмотреть на компилятор, который покорёжит такое преобразование.

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

А можно пример проблемы?

typedef enum {
  CMD_1=1,
  CMD_2=2,
  CMD_3=3
} CMD_t;

Видя только исходники, нельзя заранее сказать, что покажет sizeof(CMD_t), придется смотреть используемые опции компилятора (которые еще найти надо)

См. также тут

Тут, например, непонятно, как будет выравнен param_a. Это может быть медленнее или вообще запрещено на некоторых платформах

typedef struct {
  CMD_t     cmd;
  uint8_t   param_a;
  uint32_t  param_b;
} attribute((packed)) my_cmd_struct_t;

С эндианностью тоже возникают вопросы из-за неизвестного размера - какое значение должно быть у CMD_1 после конвертации - 0x1000000 или 0x0100 ?

По-моему, использование enum’ов для работы с железом - это лишний источник проблем, которых и так хватает в случае кросс-платформы

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

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

Не убедительно.

Видя только исходники, нельзя заранее сказать, что покажет sizeof(CMD_t)

  1. Это верно для любого тайпдефа. Это даже верно для тупого int.
  2. Какая нам разница? Мы используем sizeof именно для того, чтобы НЕ знать, что именно он покажет.
  3. Чем это отличается от дефайна-то?

Тут, например, непонятно, как будет выравнен param_a.

Оно в любом случае «не понятно», потому что за выравнивание отвечает компилятор, если мы сами ему ручками прагма паков не насовали. Ну и нам-то какое дело? Мы же не пишем код, который поплывёт от выравнивания? А если пишем с прагма паками, то нам всё равно надо идти и смотреть, что там за типы данных.

Ну и в чем будет отличие от дефайна-то?

в норме битовые флаги uint и положительные целые в него конвертируютя без сюрпризов

CMD_1=1,
какое значение должно быть у CMD_1 после конвертации - 0x1000000 или 0x0100 ?

Для unit’ов тут нет никакой загадки. Единица будет сконвертирована в единицу. Если для битовых флагов кто-то использует signed char - это уже к доктору, а не к энуму.

Ну и в чем будет отличие от дефайна-то?

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

Это верно для любого тайпдефа. Это даже верно для тупого int.

Верно. Именно поэтому у меня там будет uint32_t, uint16_t etc. Чтобы сразу было понятно, что это, без обращения к Makefile и прочим environment-setup-cortexa53-linux

Какая нам разница? Мы используем sizeof именно для того, чтобы НЕ знать, что именно он покажет.

Как я уже дважды упоминал до этого, мои возражения в основном касаются использование enum’ов для работы с железом и в этом случае размер имеет критическое значение

Ну и нам-то какое дело? Мы же не пишем код, который поплывёт от выравнивания?

В случае работы с железом именно такой код мы часто и пишем

Если в подобной конструкции что-то поплывёт, NIC скорее всего не обрадуется

struct TxDescriptor
{
    uint32_t param1;
    uint32_t param2;
    uint64_t addr;
};

А если пишем с прагма паками, то нам всё равно надо идти и смотреть, что там за типы данных.

Или изначально указать какие типы данных и в каком порядке нам требуются

Для unit’ов тут нет никакой загадки. Единица будет сконвертирована в единицу

typedef enum {
    CMD_1=1,
    CMD_2=2,
    CMD_3=3
} CMD_t;

Для конвертации нужно знать размер, который из кода выше не очевиден. Так все-таки какое значение должно быть у CMD_1 после конвертации - 0x01000000 или 0x0100 ? Недостаточно информации, а ведь его придется в регистр писать

Если для битовых флагов кто-то использует signed char - это уже к доктору, а не к энуму

Таким может оказаться ваше совместное с компилятором решение:

«Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration.»

Ну и в чем будет отличие от дефайна-то?

Тем, что вся необходимая для понимания кода информация содержится прямо в исходнике и мне не придется строить догадки «А что же в результате получится если этот код скомпилировать gcc4 для PowerPC»

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

Если в подобной конструкции что-то поплывёт,

struct TxDescriptor  
{
...

А каким образом «в этой конструкции» оказалась константа? Вы точно понимаете, что разговор идёт о разнице между определением констант через энум или дефан, и тайпдеф тут - с боку бантик?

Для конвертации нужно знать размер,

Для какой конвертации?

Так все-таки какое значение должно быть у CMD_1 после конвертации - 0x01000000 или 0x0100 ?

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

Ну и в чем будет отличие от дефайна-то?

Тем, что вся необходимая для понимания кода информация содержится прямо в исходнике

Энум тоже содержится "прямо в исходнике". Обычно, во всяком случае.

Таким может оказаться ваше совместное с компилятором решение:
«Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type.

Не надо лохматить бабушку. Сам тип перечисления может быть каким-то целочисленным типом. Но значения перечисления - нет:

6.4.4.3 Enumeration constants
An identifier declared as an enumeration constant has type int.

6.7.2.2 Enumeration specifiers
Constraints
The identifiers in an enumerator list are declared as constants that have type int and may appear wherever such are permitted.

При этом гарантируется:

The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration.

и

6.3.1 Arithmetic operands
6.3.1.1 Boolean, characters, and integers
Every integer type has an integer conversion rank defined as follows:

— The rank of any enumerated type shall equal the rank of the compatible integer type

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

А каким образом «в этой конструкции» оказалась константа? Вы точно понимаете, что разговор идёт о разнице между определением констант через энум или дефан, и тайпдеф тут - с боку бантик?

Вообще-то тут обсуждалось выравнивание. Касательно enum’ов - если есть например

typedef enum {
    FLAG_1 = 0x01,
    FLAG_2 = 0x02,
    FLAG_3 = 0x03
} FLAG_t;

то скорее всего он будет использоваться не только для того, чтобы

uint32_t u32_flag = FLAG_1;

но и для

struct TxDescriptor
{
    FLAG_t flag;
    uint32_t param1;
    uint32_t param2;
    uint64_t addr;
};

Вот таким образом «в этой конструкции» может оказаться константа

Энум тоже содержится «прямо в исходнике». Обычно, во всяком случае.

Это хорошо, что он там содержится. Жаль только, что непонятно какой у него размер. Чтобы снять большинство моих вопросов, он должен выглядеть хотя бы так:

typedef enum {
    FLAG32_1 = 0x01,
    FLAG32_2 = 0x02,
    FLAG32_3 = 0x03,
    FLAG32_PLACEHOLDER = UINT32_MAX
} __attribute__((packed)) FLAG32_t;

Но, судя по тому, что это не очевидно даже вам и даже после всей этой дискуссии, это будет еще менее очевидно какому-нибудь косорукому студенту, получившему задачу адаптировать очередной драйвер под новое железо в качестве подработки

Не надо лохматить бабушку. Сам тип перечисления может быть каким-то целочисленным типом. Но значения перечисления - нет

Ну так и не лохматьте. Еще раз - при работе с железом важно знать какой размер имеют переменные, константы и как они будут выравнены. Enum’ы вносят дополнительную неоднозначность по всем этим вопросам и затрудняют понимание кода, поэтому я против их использования в данном случае

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

Хотя меня и терзают смутные сомнения, а нет ли на свете такой системы/компилятора/ключей компиляции, где FLAG32_t вдруг станет 64-битным ?

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

чем вы руководствуетесь, когда решаете использовать дефайны или енумы.

Вообще-то тут обсуждалось выравнивание.

Да, в контексте противопоставления энумов дефайнам. И пока на эту тему я не увидел ни одной запятой. Напомню, с чего началась эта ветка обсуждения:

В том смысле, что я не знаю (и не хочу знать) какие проблемы здесь могут всплыть с размером, выравниванием и прочей эндиэнностью на различных платформах

это к чему вообще? чем отличается задание константы через дефайн и через енум… для эндиэнности, размера и выравнивания?

Вместо ответа на это вопрос всплыло туманное:

а там все правильно

Конечно, все правильно. И с выравниванием будет все правильно. И с эндианностью.

Но примеров "неправильности" использования констант энумов вместо дефайнов продемонстрировано так и не было.

Еще раз - при работе с железом

99% сишного кода не «работают с железом» напрямую. Хуже того, си как язык спроектирован так, чтобы абстрагировать программиста от работы с железом. Ещё раз - у нас не известна размерность обычного int - буквально самого базового типа в языке. С учётом этого обстоятельства разговоры про «неизвестный размер» выглядят как минимум неубедительно, если не сказать просто смешно.

Если есть например

typedef enum {
    FLAG_1 = 0x01,
    FLAG_2 = 0x02,
    FLAG_3 = 0x03
} FLAG_t;

то скорее всего он будет использоваться

Вы всё время в качестве аргумента по третьему кругу приводите какой-то мегавсратый пример, да ещё и проецируете своё «скорее всего», которое вообще выглядит как дичь. Я за четверть века работы с кодом ни разу в жизни не видел объявления переменной enum-типа. То есть вот вообще. Я уж не говорю про моветон с тайпдефом.

это не очевидно даже вам и даже после всей этой дискуссии, это будет еще менее очевидно какому-нибудь косорукому студенту, получившему задачу адаптировать очередной драйвер под новое железо в качестве подработки

Ни мне, ни студенту первокуру вообще не очевидна довольно бредовая идея использовать тип энума там, где требуется строгая разрядность, вместо существующих специально для этих целей типов из <stdint.h>. И, вероятно, по этой же причине мне не очевидно, как эта бредовая идея может быть аргументом в контексте сопоставления enum vs define.

Судя по тому, что вы копипастите по третьему разу один и тот же пример и приводите один тот же аргумент «про железо», у меня есть ощущение, что вы банально не умеете пользоваться анонимным перечислением. А между тем у enum-констант есть как минимум два серьёзных преимущества перед defineами:

  1. Они не участвуют в логике работы препроцессора:
enum
{
    STATIC_CONSTANT = 0xFF
};

// #define STATIC_CONSTANT 0xFF

#if  STATIC_CONSTANT  > 1
#error "Can't compile program!"
#endif
  1. Это честные символы и они (в случае нормальной реализации работы с символами) видны в отладчике.
LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

у enum-констант есть как минимум два серьёзных преимущества перед defineами:

Они не участвуют в логике работы препроцессора:

Этот-то недостаток вполне очевиден. Но у переменных (в т.ч. const переменных) та же беда. А задач, где бы нужно было использовать енум в препроцессоре с ходу придумать сложно. То есть синтетические варианты придумать, конечно, можно, но это будет значить лишь что для тех задач енум не подходит.

COKPOWEHEU
()
Ответ на: комментарий от LamerOk

Вместо ответа на это вопрос всплыло туманное:

Ну а вы ссылку то хоть смотрели ?

«Внимание, код SDK драйвера ATI Imageon GPU именитой корпорации ATI/AMD»

Там ведь действительно все правильно. Только не работает …

99% сишного кода не «работают с железом» напрямую. Хуже того, си как язык спроектирован так, чтобы абстрагировать программиста от работы с железом. Ещё раз - у нас не известна размерность обычного int - буквально самого базового типа в языке. С учётом этого обстоятельства разговоры про «неизвестный размер» выглядят как минимум неубедительно, если не сказать просто смешно.

Как я уже дважды упоминал до этого, мои возражения в основном касаются использование enum’ов для работы с железом и в этом случае размер имеет критическое значение

Упоминаю специально для вас в четвертый раз

Я за четверть века работы с кодом ни разу в жизни не видел объявления переменной enum-типа. То есть вот вообще.

«Как много нам открытий чудных …»

Среди меня такие переменные довольно популярны, например, как состояние при реализации конечных автоматов

Я уж не говорю про моветон с тайпдефом.

Если у вас такое неприятие такого рода конструкций, то с Linux вам точно не по пути

$ cd src/Linux/Kernel
$ egrep -r "typedef enum" . |wc -l
10655

Ни мне, ни студенту первокуру вообще не очевидна довольно бредовая идея использовать тип энума там, где требуется строгая разрядность, вместо существующих специально для этих целей типов из <stdint.h>.

Я рад что вы в конце концов приняли мою точку зрения

И, вероятно, по этой же причине мне не очевидно, как эта бредовая идея может быть аргументом в контексте сопоставления enum vs define.

Может все-таки по ссылке сходите ?

Они не участвуют в логике работы препроцессора:

Каким образом эта особенность enum’ов становится плюсом ? Для меня это скорее даже минус

Это честные символы и они (в случае нормальной реализации работы с символами) видны в отладчике

Ну, допустим, gdb может и макры показать, если попросить

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

Я уж не говорю про моветон с тайпдефом.

Если у вас такое неприятие такого рода конструкций

Да, у меня неприятие засорения пространства имён без объективной необходимости. Энумы не настолько часто используются, чтобы засорять ими пространство типов.

Среди меня такие переменные довольно популярны, например, как состояние при реализации конечных автоматов

Я очень рад, что в 2к25 кто-то ещё пишет конечные автоматы вручную.

И, вероятно, по этой же причине мне не очевидно, как эта бредовая идея может быть аргументом в контексте сопоставления enum vs define.

Может все-таки по ссылке сходите ?

По ссылке на этот же тред, на сообщение, которое уже прочитано, и где нет ни ответа на вопрос:

Для какой конвертации?

ни запрошенных примеров:

Так все-таки какое значение должно быть у CMD_1 после конвертации - 0x01000000 или 0x0100 ?

Надо привести конкретный пример кода, в котором при использовании энума что-нибудь сломается, но при замене его на дифайн все проблемы сразу исчезнут. Но примеров «неправильности» использования констант энумов вместо дефайнов продемонстрировано так и не было.

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

Я рад что вы в конце концов приняли мою точку зрения

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

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

Это честные символы и они (в случае нормальной реализации работы с символами) видны в отладчике

Ну, допустим, gdb может и макры показать, если попросить

#include <stdio.h>
#include <stdlib.h>

#define DEFINE 0x0A0B
enum
{
    ENUM = 0x0C0D
};

int main(int argc, char *argv[])
{
    (void) argc;
    (void) argv;

    int i = rand();

    if (i == DEFINE)
    {
        printf("0\n");
    }
    if (i == ENUM)
    {
        printf("1\n");
    }

    return 0;
}
gcc -std=c99 -Werror -Wall -Wextra -Wpedantic -ggdb -O0  enum_vs_define.c -o enum_vs_define
gdb --args ./enum_vs_define 
GNU gdb (Debian 13.1-3) 13.1
...
Reading symbols from ./enum_vs_define...
(gdb) break 26
Breakpoint 1 at 0x1190: file enum_vs_define.c, line 26.
(gdb) r
Starting program: enum_vs_define 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main (argc=1, argv=0x7fffffffe848) at enum_vs_define.c:26
26          return 0;
(gdb) p/x ENUM
$1 = 0xc0d
(gdb) p/x DEFINE
No symbol "DEFINE" in current context.
(gdb) c
Continuing.
[Inferior 1 (process 170021) exited normally]
(gdb) q

И как мне попросить gdb показать значения дефайнов?

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

И как мне попросить gdb показать значения дефайнов?

Раз пошла такая пьянка… А что на счёт глобальных static const переменных? По сути всё то же самое, что енумы и дефайны, только с заранее известным типом.

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

И как мне попросить gdb показать значения дефайнов?

«А теперь ты приходишь и говоришь: Дон gdb, мне нужны дефайны. Но ты не просишь с уважением, не предлагаешь дружбу»

В общем, -g3 надо бы добавить

~/W/gdb-defs> gcc -std=c99 -Werror -Wall -Wextra -Wpedantic -ggdb -O0 -g3 enum_vs_define.c -o enum_vs_define

 ~/W/gdb-defs> gdb --args ./enum_vs_define
GNU gdb (Ubuntu 15.1-1ubuntu2) 15.1
...
Reading symbols from ./enum_vs_define...
(gdb) b 26
Breakpoint 1 at 0x11b4: file enum_vs_define.c, line 26.
(gdb) r
Starting program: ./gdb-defs/enum_vs_define 

Breakpoint 1, main (argc=1, argv=0x7fffffffe758) at enum_vs_define.c:26
26	    return 0;
(gdb) info macro DEFINE
Defined at ./gdb-defs/enum_vs_define.c:4
#define DEFINE 0x0A0B
(gdb)
alx777 ★★
()
Ответ на: комментарий от u5er

А что на счёт глобальных static const переменных?

А что на счёт них? Они, естественно, лучше, чем костыли на энумах, или, прости господи, строковые подстановки дефайнами. Трагедия чистой сишки в том, что их использование ограничено - см выше.

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

main.c:

#include <stdio.h>

const int t15 = 15;
const int t16 = 16;

int main( int argc, char* argv[] ){
	
	int i;
	
	i = 16;
	
	switch( i ){
		
		case t15:
			printf( "15\n" );
			break;
			
		case t16:
			printf( "16\n" );
			break;
		
	}
	
	printf( "End of program.\n" );
	return 0;
}

makefile:

CFLAGS = -Wall -O2
LDFLAGS = -s

main: main.o

PHONY: clean

clean:
	rm -f main *.o

Консоль:

$ make
cc -Wall -O2   -c -o main.o main.c
cc -s  main.o   -o main
$ ./main 
16
End of program.
$ 

Работает. Или это другое?

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

Или это другое?

Разумеется:

user@computer:$ gcc  const_vs_enum.c -o const_vs_enum
const_vs_enum.c: In function ‘main’:
const_vs_enum.c:14:17: error: case label does not reduce to an integer constant
   14 |                 case t15:
      |                 ^~~~
const_vs_enum.c:18:17: error: case label does not reduce to an integer constant
   18 |                 case t16:
      |                 ^~~~
user@computer:$ clang-20  -Wpedantic  const_vs_enum.c -o const_vs_enum
const_vs_enum.c:14:8: warning: expression is not an integer constant expression; folding it to a constant is a GNU extension [-Wgnu-folding-constant]
   14 |                 case t15:
      |                      ^~~
const_vs_enum.c:18:8: warning: expression is not an integer constant expression; folding it to a constant is a GNU extension [-Wgnu-folding-constant]
   18 |                 case t16:
      |                      ^~~
2 warnings generated.
user@computer:$ clang-20  const_vs_enum.c -o const_vs_enum
user@computer:$ 
LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

Ничего не понял. Клэнг у меня тоже предупреждения выдаёт, но код собирается и работает. А вот гцц собирает без ошибок и предупреждений. У меня гцц 13.2.0, а у тебя?

UPD: Всё ясно, вопрос снят https://stackoverflow.com/questions/14069737/switch-case-error-case-label-does-not-reduce-to-an-integer-constant

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

Если речь только о константах, то разницы в итоге никакой нет. И то и другое превращается в immediate value в инструкциях процессора.

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

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

Если enum выползает за пределы софтины, то очень легко вообще всё поломать например добавив элемент в середину какого-нибудь enum’a. Чтобы типа по алфавиту в коде было или просто так. Для внутреннего использования внутри софтины это по барабану, но если это библиотека, или enum используется во входных или выходных данных то можно легко отстрелить себе голову, причём напрочь.

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

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

Прошу прощения, а как в 2к25 принято писать конечные автоматы?

Ты шутишь? Ты бы ещё спросил, как программировать в 2к25. Всё зависит от целей и задач. У робототехников обычно свои гуёвые генерилки, чтобы они сами не запутались. У остальных от генераторов парсеров типа Minipeg, Lemon, генераторов сишного кода типа Ragel и библиотечек на крестах типа условно простеньких HFSM2 и Embedded Template Library до DSL-фреймворков в Boost’е - Meta State Machine, Statechart и реализаций поверх Boost.Hana - hsm.

Ручной fsm хорош, пока у тебя полтора состояния и два с половиной перехода, а ты сам - студент и у тебя ещё четыре курса впереди, чтобы закончить лабу.

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

Зачем кросс-платформить платформозависимое?

Скажу честно - не знаю. Возможно, на ваш вопрос сможет ответить @LamerOk, собирающийся заменить все дефайны в мире на енумы

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

И в каком дистрибутиве линукса его добавляют по дефолту?

Во всех, где не взирая на Makefile etc по дефолту добавляется

Не продолжай, я уже понял, что postmortem-отладкой чужого кода ты никогда не занимался.

По существу твоего тезиса:

Страшно, очень страшно, мы не знаем правил преобразований int’ов. Если бы мы знали правила преобразований int’ов, мы не знаем правил преобразований int’ов. Мы, конечно, при своей необразованности можем даже предположить, что enum-ы это диверсия какая-то.

будет хоть один пример?

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

Не продолжай, я уже понял, что postmortem-отладкой чужого кода ты никогда не занимался.

Отчего же не занимался. Вот буквально третьего дня и было дело. Хотя не совсем понятно из контекста, что ты имеешь ввиду - core, pstore или что-то свое, личное. Как ты, надеюсь, уже понял из предыдущей дискуссии, моей основной деятельностью является линуксовая кроссплатформерная ембеддщина, где все это будет отключено по дефолту просто потому что его некуда складывать.

Так что берем use-case, конфигурацию, собираем для x86_64 c необходимыми ключами и наслаждаемся.

Или я чего-то не понимаю и сегодня актуально в продакшн выкатывать -ggdb -O0 ?

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

Хотя не совсем понятно из контекста, что ты имеешь ввиду - core

Корки, конечно же.

Или я чего-то не понимаю и сегодня актуально в продакшн выкатывать -ggdb -O0 ?

Во всех ходовых дистрах сегодня актуально в продакшн выкатывать -g, который синоним -g2.

Так что берем use-case,

"use-case" типовой: вот тебе корка твоей проги с либами из дистра, надо сделать, что б без корок.

собираем для x86_64 c необходимыми ключами

Ох уж мне эта "ембеддщина". Никто не будет пересобирать весь центос с убунтосервером "с необходимыми ключами".

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

Прошу прощения, а как в 2к25 принято писать конечные автоматы?

Ты шутишь? Ты бы ещё спросил, как программировать в 2к25. Всё зависит от целей и задач. У робототехников обычно свои гуёвые генерилки, чтобы они сами не запутались.

Уверен, все вышеперечисленное может оказаться полезным при программировании огромных человекоподобных роботов

Лично для моих целей (FSM с десятком состояний/переходов) в результате гораздо полезнее оказалось ознакомиться с Автоматным программированием и упомянутыми там работами Шалыто А.А, нарисовать FSM на бумаге и сразу закодить на сишечке

alx777 ★★
()

У #define область порчи до следующего #undef или до конца единицы трансляции.

У enum есть контекст и область видимости.

int x64(){
	enum { 
	AX=0, EAX=0, RAX=0,
	CX=1, ECX=1, RCX=1,
	DX=2, EDX=2, RDX=2, 
	};

	return RDX * 8 + RCX;
}

int i386(){
	enum { 
	AX=0, EAX=0, // RAX=0,
	CX=1, ECX=1, // RCX=1,
	DX=2, EDX=2, //RDX=2, 
	};

	return EDX * 8 + ECX;
}

int i8086(){
	enum { 
	AX=0, // EAX=0, // RAX=0,
	CX=1, // ECX=1, // RCX=1,
	DX=2, // EDX=2, //RDX=2, 
	};

	return EDX * 8 + CX;
// en.c: In function ‘i8086’:
// en.c:29:9: error: ‘EDX’ undeclared (first use in this function)
//  return EDX * 8 + CX;
//	   ^~~
}
anonymous
()
Ответ на: комментарий от LamerOk

Тут (СИ: enum VS #define (комментарий)) ни слова про поломку.

Это где у тебя такая разница между lts и апстримом, где есть " постоянно добавляемые в glibc функции"? Примеры можешь привести или будет как с std::shared_ptr?

Пишешь код с новыми функциями sinpi(), cospi()… в апстриме, идёшь компилять в лтс, получаешь болт, так как их там ещё нет.

Всю жизнь была разница между lts и апстримом. Это касается всего. Иначе в нём не было бы смысла.

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

Пишешь код с новыми функциями sinpi(), cospi()… в апстриме, идёшь компилять в лтс,

Это заведомо не работающий мегатупой сценарий, работу которого не только никто никогда не обещал, но прямо напротив сказано, что так работать не будет не только с глис, линуксом и опенсорсом, а вообще со всем ПО вселенной. До того, как ты начинаешь писать код, ты выясняешь минимально поддерживаемое окружение, для которого ты этот код пишешь. Потом пишешь код под него, опционально используя новые фичи в более свежих версиях. Если ты переставил местами телегу с лошадью - ССЗБ.

Тут ни слова про поломку.

Напоминаю:

этом проклятом мире, где ничего не собирается
постоянно добавляемых в glibc функций

Поинт был, что злые разрабы ежечасно добавлют в glibc код, из-за которого ни у кого ничего не собирается. Поинт доказан не был.

Всю жизнь была разница между lts и апстримом.

Вот только для gblic этой разницы продемонстрировано не было. Использование нового 23-его стандарта нельзя выдавать как пример злобной поломки глибс - это литерально другая версия языка программирования. Разумеется, она не будет работать на системах, эту версию не поддерживающую.

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

Так вы там и так сравнивали разные стандарты. Чем сравнение «C99 vs C17» отличается от «C17 vs C23»? И сейчас активно реализуется поддержка C23 и поэтому является апстримом, естественно в свежий glibc будут новые функции добавляться «ежечасно», хотя этого слова в постах никто не употреблял. Ты этот поинт сам придумал.

Разумеется, она не будет работать на системах, эту версию не поддерживающую.

Вот об этом тебе и говорили. А новость на форониксе - просто пример свежака. За другими примерами - в гугл по latest glibc breaks.

anonymous
()

Побуду Капитаном. #define это директива препроцессора, т.е. не является элементом языка.

Везде, где всё равно, что использовать, и где можно использовать enum, используй enum. #define – для специфических случаев, например, когда надо поуправлять компиляцией.

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

Плохо – не контролируется компилятором. На 1 странице были примеры.

Хорошо – когда надо как-то сильно извратиться, но тот, кому это надо, не спрашивает, enum или дефайн, а просто использует дефайн :)

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

Плохо – не контролируется компилятором.

Отчасти

#include <stdio.h>

#define VAR 1000

int main( int argc, char* argv[] ){
	
	char c;
	
	c = VAR;
	
	printf( "End of program.\n" );
	return 0;
}
$ make
cc -Wall -O2   -c -o main.o main.c
main.c: In function 'main':
main.c:3:13: warning: overflow in conversion from 'int' to 'char' changes value from '1000' to '-24' [-Woverflow]
    3 | #define VAR 1000
      |             ^~~~
main.c:9:13: note: in expansion of macro 'VAR'
    9 |         c = VAR;
      |             ^~~
main.c:7:14: warning: variable 'c' set but not used [-Wunused-but-set-variable]
    7 |         char c;
      |              ^
cc -s  main.o   -o main

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

u5er ★★
() автор топика