Opcjonalne funkcje zależne od define

Opcjonalne funkcje zależne od define
mlyszczek
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
  • Postów: 167
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

Kopiuj
/* 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:

Kopiuj
/* 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:

Kopiuj
/* 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?

kq
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Szczecin
2

Inne metody?

Kopiuj
#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.

mlyszczek
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
  • Postów: 167
0
kq napisał(a):

Inne metody?

Kopiuj
#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;-)

kq
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Szczecin
2

Ok, to

Kopiuj
#define foo(...)

http://melpon.org/wandbox/permlink/PPkoIYs4LXchG0Rv

mlyszczek
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
  • Postów: 167
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)

mlyszczek
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
  • Postów: 167
0

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

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

Co w rezultacie da nam

Kopiuj
 /* 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:

Kopiuj
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.

Azarien
  • Rejestracja: dni
  • Ostatnio: dni
0

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

Kopiuj
/* 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.

mlyszczek
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
  • Postów: 167
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:)

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.