Opcjonalne funkcje zależne od define

0

W programowaniu często się zdarza, że pisząc program chcemy aby jakieś funkcji były opcjonalne. Na przykład możemy mieć opcjonalne spi. Załóżmy że posiadamy funkcji spi_gpio_init, która inicjuje gpio. Wołana jest ona w gpio_init, która inicjuje wszystkie peryferia. Definicja funkcji spi_gpio_init jest prosta, po prostu opakowujemy w #define

/* spi.c */

#ifdef CONFIG_SPI
void spi_gpio_init(void)
{
  /* code */
}
#endif

Teraz pozostaje problem z deklaracjami. Jak na moje to mamy dwa rozwiązania tutaj

Rozwiązanie pierwsze:

/* spi.h */

#ifdef CONFIG_SPI
void spi_gpio_init(void);
#endif

/* boot.c */

void gpio_init(void)
{
#ifdef CONFIG_SPI
  spi_gpio_init();
#endif
}

Rozwiązanie drugie:

/* spi.h */

#ifdef CONFIG_SPI
void spi_gpio_init(void);
#else
static inline spi_gpio_init(void) {}
#endif

/* boot.c */

void gpio_init(void)
{
  spi_gpio_init();
}

Jak widać, różnica jest taka, że 1 rozwiązanie dodatkowo zasyfia header, a drugie dodaje syf do samego już kodu. Osobiście stosuję metodę drugą, bo kompilator i tak wywali funkcję spi_gpio_init jako, że nic się tam nie dzieje - nawet jakby tam był return 0 to i tak optymalizator powinien wyciąć to wszystko w pień.

I tutaj powstaje pytanie. Znacie jakieś inne metody na rozwiązanie takiego problemu? Może jest jeszcze lepsze rozwiązanie problemu, którego nie znam?

2

Inne metody?

#define spi_gpio_limit()

W C++ jakieś ładne szablonowe rozwiązanie można by pyknąć, ale nie wiem czy jest łatwo dostępny kompilator C++ na Twój system wbudowany.

Co do lepszości to się nie wypowiadam, ale raczej wybrałbym Twoje rozwiązanie 1 lub 2.

0
kq napisał(a):

Inne metody?

#define spi_gpio_limit()

Tylko, że to rozwiązanie zadziała gdy funkcja nie przyjmuje parametrów, więc musielibyśmy jakieś 2 rozwiązania stosować wtedy (drugie dla funkcji z parametrami), to już się chaos robi powoli;-)

0

O, faktycznie, o tym nie pomyślałem. I to jest chyba na razie najlepsze rozwiązanie, bo nie trzeba "voidować" parametrów i zwracać 0, gdy funkcja jest inna niż void foo(void)

0

Dla ciekawskich, po dogłębnym reaserchu doszedłem do wniosku, że najbezpieczniejszą i najlepszą metodą jest

#define spi_gpio_init /* dla bezparametrowych funkcji
#define spi_gpio_init (void) /* dla funkcji z parametrami */

Co w rezultacie da nam

 /* kompletnie nic dla bezparametrowych funkcji */
(void)(a,b,c); /* dla funkcji z parametrami */

(void)(a,b,c); ten kod tworzy listę argumentów a potem ją odrzuca, każdy optymalizator zrobi z tego "nic".

Dlaczego tak, a nie inaczej? Bo wywołania funkcji mogą mieć efekty uboczne i w innym przypadku jest niebezpieczeństwo usunięcia takiego efektu ubocznego, przykład:

foo(*buf++)

przy #define foo(a) zmienna buf nie zostanie zinkrementowana co najprawdopodobniej doprowadzi do błędów w późniejszym kodzie. #define foo (void) zostawia nam (void)(*buf++) dzięki czemu mamy pewność, że efekt uboczny zostanie zachowany.

Jako bonus, metoda ta również działa z c89.

0

Ja bym raczej ukrył pustą bądź niepustą funkcję w pliku .c, a nagłówek i wywołanie zostawił czyste.

/* spi.h */

void spi_gpio_init(void);

/* spi.c */
 
void spi_gpio_init(void)
{
#ifdef CONFIG_SPI
  /* code */
#endif
}
 
/* boot.c */
 
void gpio_init(void)
{
  spi_gpio_init();
}

Chyba że to spi.c jest w innym projekcie niż boot.c (osobnej bibliotece) i ifdef jest potrzebny na etapie nagłówka.

0

Ale Twoje rozwiązanie powoduje, że przy -O0, funkcja spio_gpio_init zostanie zawołana (mimo, że pusta), a przy -O2 funkcja spio_gpio_init mimo, ze nie będzie wołana to wciąż zostanie Ci symbol funkcji w kodzie. I jak na x86 problemu nie ma, tak na mikrokontrolerze gdy takich funkcji jest trochę to może się trochę bajtów nazbierać, a jak flash masz 8kb, to i 100 bajtów jest wiele:)

1 użytkowników online, w tym zalogowanych: 0, gości: 1