LINUX.ORG.RU

СИ: enum VS #define

 


1

2

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

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

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

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

Выгоднее объявить макрос препроцессора и задать через него и размер буфера и ограничитель цикла

И что мешает использовать enum-константу? В крестах так честный const size_t использовать можно. Нет никаких объективных причин использовать в этом сценарии препроцессор.

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

И что мешает использовать enum-константу?

Инерция мышления.

В крестах

Тема про обычную сишку. Я не пишу на плюсах.

Нет никаких объективных причин использовать в этом сценарии препроцессор.

Ровно как и нет причин его не использовать ;)

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

Отчасти

Вообще никак. Можно написать хоть #define VAR 100.a8,25,(ыыы), но пока компилятор не будет вынужден эту дичь использовать, проблем нет. Именно потому, что дефайны разворачиваются еще до начала работы компилятора.

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

#define LED B,8,1,GPIO_PP50
#define TIM_DMA_2_UP	1,2,do{TIM2->DMAINTENR |= TIM_UDE;}while(0),do{TIM2->DMAINTENR &=~ TIM_UDE}while(0)

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

С одной стороны да, но в этом применении он конкурирует не с enum, а с const size_t, который в Си считается переменной, а не константой

С другой - (sizeof(arr)/sizeof(arr[0])) и размер массива можно задавать хоть магическим чиселком.


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

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

TIM_DMA_2_UP

Вот это я называю «программирование на макросах». Никогда так не пишу.

У меня это может быть либо число, либо маска

#define AXP209_REG_COULOMB_CHARGE_COUNTER    0xB0
#define AXP209_REG_COULOMB_DISCHARGE_COUNTER 0xB4
#define AXP209_REG_COULOMB_COUNTER_CONTROL   0xB8
#define AXP209_REG_FUEL_GAUGE                0xB9

#define AXP209_MASK_IRQ1_ACIN_OVERVOLTAGE ( 1 << 7 )
#define AXP209_MASK_IRQ1_ACIN_CONNECT     ( 1 << 6 )
#define AXP209_MASK_IRQ1_ACIN_DISCONNECT  ( 1 << 5 )
#define AXP209_MASK_IRQ1_VBUS_OVERVOLTAGE ( 1 << 4 )
#define AXP209_MASK_IRQ1_VBUS_CONNECT     ( 1 << 3 )
#define AXP209_MASK_IRQ1_VBUS_DISCONNECT  ( 1 << 2 )
#define AXP209_MASK_IRQ1_VBUS_LESS_VHOLD  ( 1 << 1 )

Если нужна функция, то я использую инлайны.

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

Через «число, либо маску» будет еще более громоздко. И не факт, что компилятор догадается оптимизировать. У меня ведь в одной макроконстанте кодируются и номер DMA, и номер канала, и способ включения и выключения. Без этого пришлось бы делать глобальные массивы функций. То есть совсем глобальные - на всю периферию сразу. А так каждая занимается только собой.

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

Тебе не доводилось делать опечатку в таких макросах? Я однажды сделал в процессе ковыряния ядра. Компилятор выдавал здоровую простыню и всё не по делу :) Так что инлайны всё моё :)

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

Тебе не доводилось делать опечатку в таких макросах?

Ты за кого меня принимаешь?! Да я столько косячного выхлопа из-за этих МРакосов ловил, зачастую казалось бы на ровном месте.

Но, увы, для моих задач ничего лучше не придумали. Разве что попробовать освоить наконец С++ с его кошмарными шаблонами…

Так что инлайны всё моё

Ну-ну, посмотрел бы я на твои инлайны. Хотя бы в простейшем случае - настройке портов

#define GPIO_PP50 0b0011

#define concat2(a,b,...)  a##b

#define _marg1(a,...)  a
#define _marg2(a,b,...)  b
#define _marg3(a,b,c,...)  c
#define _marg4(a,b,c,d,...)  d
#define marg1(x) _marg1(x)
#define marg2(x) _marg2(x)
#define marg3(x) _marg3(x)
#define marg4(x) _marg4(x)
#define _GPIO(port) GPIO##port
#define GPIO(x) _GPIO(x)

#define _GPIO_CONFIG(port, bit, letter, mode) \
  do{\
    uint32_t temp = GPIO##port -> concat2(CR,letter);\
    temp &=~(0b1111<<(((bit)&0x07)*4)); \
    temp |= ( mode <<(((bit)&0x07)*4)); \
    GPIO##port -> concat2(CR,letter) = temp; \
  }while(0)

  
#define GPIO_mode(port, bit, mode)\
  do{ \
    if(bit<8)_GPIO_CONFIG(port,bit,L,mode); \
      else _GPIO_CONFIG(port,bit-8,H,mode); \
  }while(0)
  

#define GPIO_config(descr) \
  do{ \
    GPIO_mode(_marg1(descr), _marg2(descr), _marg4(descr)); \
    /*if(_marg4(descr) == GPIO_PULL){ \
      if(_marg3(descr))GPIO(_marg1(descr))->BRR = (1<<_marg2(descr)); \
        else GPIO(_marg1(descr))->BSRR = (1<<_marg2(descr)); \
    }*/ \
  }while(0)
...
#define LED B,11,1,GPIO_PP50
...
GPIO_config(LED);

Разворачиваться это должно примерно в

GPIOB->CRH = (GPIOB->CRH &~(0b1111<<12)) | (0b0011<<12); //у меня сделано через временную переменную

12 - потому что настройка портов сгруппирована по 4 бита на каждую ножку в двух регистрах. CRL - 0-7 ножки, CRH - 8-15. Я для примера взял 11-ю ножку: (11-8)*4 = 12.

А то еще могу предложить пример USB-дескрипторов. Там постоянно нужно подставлять длину получившегося дескриптора то вместо нулевого байта, то вместо 2-3, а то 5-6.

static const uint8_t USB_DeviceDescriptor[] = {
  ARRLEN1(
  bLENGTH,     // bLength
  1,   // bDescriptorType - Device descriptor
  0x01, 0x10, // bcdUSB
  0xEF, // bDevice Class
  0x02, // bDevice SubClass
  0x01, // bDevice Protocol
  8,   // bMaxPacketSize0
  0x16, 0xC0, // idVendor
  0x05, 0xDF, // idProduct
  0x00, 0x01, // bcdDevice_Ver
  1,   // iManufacturer
  2,   // iProduct
  3,   // iSerialNumber
  1    // bNumConfigurations
  )
};

Вместо bLength оно подставляет sizeof(), то есть 18

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

Ты за кого меня принимаешь?!

За пользователя в сети.

Ну-ну, посмотрел бы я на твои инлайны. Хотя бы в простейшем случае - настройке портов

При настройке портов не вижу смысла упарываться, поэтому делаю всё процедуркой

static int sx1509ChipSetup( struct BicOS* os ){
	
	int i;
	int res;
	
	const unsigned char regData[][2] = {
		{ SX1509_REG_RESET,                0x12  },//soft reset - step 1
		{ SX1509_REG_RESET,                0x34  },//soft reset - step 2
		{ SX1509_REG_DIR_B,                0xff  },//all input
		{ SX1509_REG_DIR_A,                0x00  },//all output
		{ SX1509_REG_OPEN_DRAIN_A,         0xef  },//all open-drain, кроме 4
		{ SX1509_REG_PULLUP_B,             0xff  },//all pull-up
		{ SX1509_REG_DEBOUNCE_ENABLE_B,    0xff  },//all enable
		{ SX1509_REG_DEBOUNCE_CONFIG,      0x04  },//8ms
		{ SX1509_REG_KEY_CONFIG_1,         0x65  },//32ms, 4s autosleep
		{ SX1509_REG_KEY_CONFIG_2,         0x1c  },//4rows, 5 columns
		{ SX1509_REG_CLOCK,                0x40  },//clock enable (internal 2MHz)
		{ SX1509_REG_INPUT_DISABLE_A,     (1<<4) },//disable input buffer on gpio 4
		{ SX1509_REG_MISC,                 0x10  },//LED driver frequency 7.56 KHz
		{ SX1509_REG_LED_DRIVER_ENABLE_A, (1<<4) },//enable 4 gpio LED
		{ LIST_TERMINATOR,                 0x00  }
	};
	
	for( i = 0; regData[i][0] != LIST_TERMINATOR; i++ ){
		res = i2c_master_transmit( os->sx1509_keys, regData[i], 2, SX1509_TIMEOUT_MS );
		if( res ) return -1;
	}
	
	return 0;
}
u5er ★★
() автор топика
Последнее исправление: u5er (всего исправлений: 1)
Ответ на: комментарий от COKPOWEHEU

Это ж не порты

Это запись регистров в микросхему sx1509, которая является расширителем портов(gpio extender). Я подумал, что ты про такого рода вещи говорил.

А в моем примере

В твоём примере программирование на дефайнах в которое я не умею и не хочу уметь, поэтому я его не распарсил. Я одно время ковырял ядерный код, в котором такого вагон и маленькая тележка и мне хватило. Я тогда всё это на обычную процедурку переписал. Чтоб мне себя заставить вникнуть в эти макросы, мне нужна веская причина. Такие дела :)

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

Тогда просто поверь на слово, что для некоторых задач дефайны незаменимы. Хотя чтобы эти задачи решить, приходится изрядно повыкручивать мозги. И то не всегда помогает.

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

Охотно верю. Особенно после твоего заявления

Да я столько косячного выхлопа из-за этих МРакосов ловил, зачастую казалось бы на ровном месте.

Сомневаюсь, что кто-то стал бы осознанно писать такой код, где можно наломать дров.

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

Я, конечно, сварщик не настоящий (с) но не проще ли в данном случае использовать бибилиотеки абстракции вместо скриптопортянок?

а вот после данной строки:

temp &=~(0b1111<<(((bit)&0x07)*4)); \

совсем грустно стало

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

Есть еще libopenCM3, где настройка пина выглядит, вроде не сильно вырвиглазно, примерно так:

gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLDOWN, GPIO5);

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

#define LED B,11,1,GPIO_PP50
vs
#define LED B,1,11,GPIO_PP50
pavel_l
()
Последнее исправление: pavel_l (всего исправлений: 2)
Ответ на: комментарий от pavel_l

Есть еще libopenCM3, где настройка пина выглядит, вроде не сильно вырвиглазно, примерно так:

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

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

Вот таких опечаток у меня никогда не было. А бороться с вымышленными угрозами и смысла нет. Настоящих и без того более чем достаточно.

COKPOWEHEU
()