Czy dobrą praktyką jest używanie #ifdef podczas programowania do przełączania różnych typów zachowań? Na przykład chcę zmienić zachowanie istniejącego kodu, mam kilka pomysłów, jak zmienić zachowanie i konieczne jest przełączanie się między różnymi implementacjami, aby przetestować i porównać różne podejścia. Zwykle zmiany w kodzie są złożone i wpływają na różne metody w różnych plikach.
Zazwyczaj wprowadzam kilka identyfikatorów i robię coś takiego
void foo()
{
doSomething1();
#ifdef APPROACH1
foo_approach1();
#endif
doSomething2();
#ifdef APPROACH2
foo_approach2();
#endif
}
void bar()
{
doSomething3();
#ifndef APPROACH3
doSomething4();
#endif
doSomething5();
#ifdef APPROACH2
bar_approach2();
#endif
}
int main()
{
foo();
bar();
return 0;
}
Pozwala to szybko przełączać się między różnymi podejściami i robić wszystko w jednej kopii kodu źródłowego. Czy to dobre podejście do rozwoju, czy jest lepsza praktyka?
c++
programming-practices
development-process
Yury Shkliaryk
źródło
źródło
#ifdef
blokach, jeśli blok jest wyłączony. Natknęliśmy się na przypadki, w których kod może łatwo zestarzeć się i nie skompilować, jeśli nie budujesz rutynowo wszystkich ścieżek.#ifdefs
mniej uciążliwymi.Odpowiedzi:
Wolę używać gałęzi kontroli wersji dla tego przypadku użycia. Pozwala to na różnicowanie między implementacjami, prowadzenie osobnej historii dla każdej z nich, a kiedy już podejmiesz decyzję i musisz usunąć jedną z wersji, po prostu odrzucisz tę gałąź zamiast poddawać się podatnej na błędy edycji.
źródło
git
jest szczególnie biegły w tego typu rzeczach. Może nie tyle w przypadkusvn
,hg
czy inni, ale może być jeszcze zrobione.git branch
!Kiedy trzymasz młotek, wszystko wygląda jak gwóźdź. To kuszące, gdy wiesz, jak to
#ifdef
działa, aby użyć go jako środka do uzyskania niestandardowego zachowania w twoim programie. Wiem, bo popełniłem ten sam błąd.Odziedziczyłem starszy program napisany w MFC C ++, który już służył
#ifdef
do definiowania wartości specyficznych dla platformy. Oznaczało to, że mogłem skompilować mój program do użycia na platformie 32-bitowej lub 64-bitowej, po prostu definiując (lub w niektórych przypadkach nie definiując) określone wartości makr.Powstał wtedy problem, że musiałem napisać niestandardowe zachowanie klienta. Mógłbym stworzyć gałąź i stworzyć osobną bazę kodu dla klienta, ale to spowodowałoby piekło serwisowe. Mógłbym również zdefiniować wartości konfiguracyjne do odczytania przez program podczas uruchamiania i użyć tych wartości do określenia zachowania, ale musiałbym wtedy utworzyć niestandardowe ustawienia, aby dodać prawidłowe wartości konfiguracyjne do plików konfiguracyjnych dla każdego klienta.
Kusiło mnie i poddałem się. Napisałem
#ifdef
sekcje w moim kodzie, aby rozróżnić różne zachowania. Nie popełnij błędu, na początku nie było to przesadą. Wprowadzono bardzo niewielkie zmiany w zachowaniu, które pozwoliły mi redystrybuować wersje programu wśród naszych klientów i nie muszę mieć więcej niż jednej wersji bazy kodu.Z biegiem czasu stało się to piekło konserwacyjnych i tak , ponieważ program nie będzie już zachowywał się konsekwentnie w całej planszy. Gdybym chciał przetestować wersję programu, musiałem koniecznie wiedzieć, kim był klient. Kod, choć starałem się zredukować go do jednego lub dwóch plików nagłówkowych, był bardzo zagracony, a szybkie podejście, które
#ifdef
zapewniło, oznaczało, że takie rozwiązania rozprzestrzeniły się w całym programie jak złośliwy rak.Od tego czasu nauczyłem się mojej lekcji i ty też powinieneś. Użyj go, jeśli absolutnie musisz, i używaj go ściśle do zmian platformy. Najlepszym sposobem podejścia do różnic w zachowaniu między programami (a zatem i klientami) jest zmiana tylko konfiguracji ładowanej podczas uruchamiania. Program pozostaje spójny i zarówno łatwiej jest go czytać, jak i debugować.
źródło
Tymczasowo nie ma nic złego w tym, co robisz (powiedzmy przed odprawą): to świetny sposób na przetestowanie różnych kombinacji technik lub zignorowanie części kodu (choć mówi o problemach samo w sobie).
Ale słowo ostrzeżenia: nie utrzymuj, że gałęzie #ifdef są nieco bardziej frustrujące niż marnowanie czasu na czytanie tej samej rzeczy zaimplementowane na cztery różne sposoby, tylko po to, aby dowiedzieć się, który powinienem czytać .
Czytanie nad #ifdef wymaga wysiłku, ponieważ musisz pamiętać, aby go pominąć! Nie rób tego trudniej niż to absolutnie musi być.
Używaj #ifdefs tak oszczędnie, jak to możliwe. Istnieją ogólnie sposoby, aby to zrobić w środowisku programistycznym w celu uzyskania trwałych różnic, takich jak kompilacje debugowania / wydania lub dla różnych architektur.
Napisałem funkcje biblioteki, które były zależne od dołączonych wersji bibliotek, które wymagały podziału #ifdef. Czasami więc może to być jedyny lub najłatwiejszy sposób, ale nawet wtedy powinieneś być zmartwiony ich utrzymaniem.
źródło
Korzystanie z #ifdefs w ten sposób bardzo utrudnia odczytanie kodu.
Więc nie, nie używaj #ifdefs w ten sposób.
Może być mnóstwo argumentów, dlaczego nie użyć ifdefs, dla mnie ten wystarczy.
Potrafi robić wiele rzeczy:
Wszystko w zależności od tego, jakie podejścia są zdefiniowane, czy nie. To, co robi, nie jest absolutnie jasne na pierwszy rzut oka.
źródło