Czy jest jakiś sposób na osiągnięcie przeciążenia funkcji w C? Patrzę na proste funkcje do przeciążenia
foo (int a)
foo (char b)
foo (float c , int d)
Myślę, że nie ma prostej drogi; Szukam rozwiązań, jeśli takie istnieją.
c
overloading
FL4SOF
źródło
źródło
Odpowiedzi:
Istnieje kilka możliwości:
źródło
Tak!
W chwili, gdy pytanie zostało zadane, standardowy C (bez rozszerzeń) skutecznie zyskał obsługę przeciążania funkcji (nie operatorów), dzięki dodaniu
_Generic
słowa kluczowego w C11. (obsługiwane w GCC od wersji 4.9)(Przeładowanie nie jest tak naprawdę „wbudowane” w sposób pokazany w pytaniu, ale bardzo trudno jest wdrożyć coś, co działa w ten sposób.)
_Generic
jest operatorem czasu kompilacji w tej samej rodzinie cosizeof
i_Alignof
. Jest to opisane w standardowej sekcji 6.5.1.1. Akceptuje dwa główne parametry: wyrażenie (które nie będzie oceniane w czasie wykonywania) oraz listę skojarzeń typów / wyrażeń, która wygląda trochę jakswitch
blok._Generic
pobiera ogólny typ wyrażenia, a następnie „przełącza” je, aby wybrać wyrażenie wyniku końcowego z listy dla swojego typu:Powyższe wyrażenie ocenia na
2
- typ wyrażenia kontrolującego jestint
, więc wybiera wyrażenie powiązane zint
wartością. Nic z tego nie pozostaje w czasie wykonywania. (default
Klauzula jest opcjonalna: jeśli ją odrzucisz, a typ nie będzie zgodny, spowoduje to błąd kompilacji).Przydaje się to w przypadku przeciążenia funkcji, ponieważ może zostać wstawione przez preprocesor języka C i wybrać wyrażenie wynikowe na podstawie typu argumentów przekazanych do makra sterującego. Tak (przykład ze standardu C):
To makro implementuje przeciążoną
cbrt
operację, wysyłając typ makra do makra, wybierając odpowiednią funkcję implementacyjną, a następnie przekazując oryginalny argument makra do tej funkcji.Aby wdrożyć oryginalny przykład, możemy to zrobić:
W tym przypadku moglibyśmy zastosować
default:
skojarzenie dla trzeciego przypadku, ale to nie pokazuje, jak rozszerzyć zasadę na wiele argumentów. Efektem końcowym jest to, że możesz używaćfoo(...)
w swoim kodzie bez obawy (wiele [1]) o rodzaj jego argumentów.W przypadku bardziej skomplikowanych sytuacji, np. Funkcji przeciążających większą liczbę argumentów lub zmieniających się liczb, można użyć makr narzędziowych do automatycznego generowania statycznych struktur wysyłki:
( tutaj implementacja ) Przy odrobinie wysiłku możesz zredukować liczbę podstawek do wyglądu przypominającego język z natywną obsługą przeciążania.
Nawiasem mówiąc , w C99 było już możliwe przeciążenie liczbą argumentów (nie typu).
[1] zauważ, że sposób, w jaki C ocenia typy, może cię jednak potknąć. Spowoduje to wybranie,
foo_int
jeśli na przykład spróbujesz przekazać literał znakowy, i musisz trochę zepsuć, jeśli chcesz, aby przeciążenia obsługiwały literały łańcuchowe. Mimo to ogólnie całkiem fajnie.źródło
Jak już wspomniano, przeciążenie w tym sensie, że masz na myśli, że nie jest obsługiwane przez C. Częstym idiomem do rozwiązania problemu jest spowodowanie, aby funkcja zaakceptowała oznaczony związek . Jest to realizowane przez
struct
parametr, w którymstruct
sam składa się z pewnego rodzaju wskaźnika typu, takiego jak anenum
iunion
różnego rodzaju wartości. Przykład:źródło
whatever
s do oddzielnych funkcji (set_int
,set_float
itp). Następnie „oznaczanie typem” staje się „dodaj nazwę typu do nazwy funkcji”. Wersja w tej odpowiedzi wymaga więcej pisania, większego kosztu działania, większej szansy na błędy, które nie zostaną wykryte podczas kompilacji ... Nie widzę żadnej korzyści z robienia tego w ten sposób! 16 głosów pozytywnych ?!Oto najjaśniejszy i najbardziej zwięzły przykład, w którym wykazałem przeciążenie funkcji w C:
https://gist.github.com/barosl/e0af4a92b2b8cabd05a7
źródło
Jeśli twoim kompilatorem jest gcc i nie masz nic przeciwko robieniu aktualizacji ręcznie za każdym razem, gdy dodajesz nowe przeciążenie, możesz wykonać magię makr i uzyskać pożądany efekt pod względem rozmówców, nie jest tak przyjemnie pisać ... ale jest to możliwe
spójrz na __builtin_types_compatible_p, a następnie użyj go do zdefiniowania makra, które działa podobnie
ale tak, paskudne, po prostu nie
EDYCJA: C1X otrzyma wsparcie dla ogólnych wyrażeń typu, które wyglądają tak:
źródło
Tak, w pewnym sensie.
Oto przykład:
Wyjdzie 0 i witam .. z printA i printB.
źródło
Poniższe podejście jest podobne do modelu a2800276 , ale z dodaną odrobiną magii makro C99:
źródło
To może wcale nie pomóc, ale jeśli używasz clang, możesz użyć atrybutu przeciążalnego - Działa to nawet podczas kompilacji jako C
http://clang.llvm.org/docs/AttributeReference.html#overloadable
nagłówek
Realizacja
źródło
W tym sensie, że masz na myśli - nie, nie możesz.
Możesz zadeklarować
va_arg
funkcję podobną dovoid my_func(char* format, ...);
, ale musisz podać jakieś informacje o liczbie zmiennych i ich typach w pierwszym argumencie - podobnie jak
printf()
robi.źródło
Zwykle brodawka wskazująca typ jest dołączana lub dodawana do nazwy. Możesz uniknąć makr w niektórych przypadkach, ale raczej zależy to od tego, co próbujesz zrobić. W C nie ma polimorfizmu, tylko przymus.
Za pomocą makr można wykonywać proste operacje ogólne:
Jeśli twój kompilator obsługuje typeof , w makrze można umieścić bardziej skomplikowane operacje. Możesz wtedy mieć symbol foo (x), aby obsługiwać tę samą operację dla różnych typów, ale nie możesz zmieniać zachowania między różnymi przeciążeniami. Jeśli chcesz rzeczywistych funkcji zamiast makr, możesz być w stanie wkleić typ do nazwy i użyć drugiego wklejenia, aby uzyskać do niego dostęp (nie próbowałem).
źródło
Odpowiedź Leushenko jest naprawdę fajna - tylko:
foo
przykład nie kompiluje się z GCC, który się nie udajefoo(7)
, potykając się oFIRST
makro i faktyczne wywołanie funkcji ((_1, __VA_ARGS__)
pozostając z nadwyżką przecinka. Dodatkowo mamy kłopoty, jeśli chcemy zapewnić dodatkowe przeciążenia , takie jakfoo(double)
.Postanowiłem więc rozwinąć odpowiedź nieco dalej, w tym pozwolić na przeciążenie pustki (
foo(void)
- co spowodowało sporo problemów ...).Pomysł jest teraz: Zdefiniuj więcej niż jeden rodzajowy w różnych makrach i pozwól wybrać poprawny zgodnie z liczbą argumentów!
Liczba argumentów jest dość łatwa, na podstawie tej odpowiedzi :
To miłe, rozwiązujemy jeden
SELECT_1
lubSELECT_2
(lub więcej argumentów, jeśli ich potrzebujesz / potrzebujesz), więc po prostu potrzebujemy odpowiednich definicji:OK, dodałem już void overload - jednak ten faktycznie nie jest objęty standardem C, który nie dopuszcza pustych argumentów variadic, tzn. Polegamy na rozszerzeniach kompilatora !
Na początku puste wywołanie makra (
foo()
) nadal generuje token, ale puste. Makro liczące faktycznie zwraca 1 zamiast 0, nawet przy pustym wywołaniu makra. Możemy „łatwo” wyeliminować ten problem, jeśli__VA_ARGS__
warunkowo wstawimy przecinek , w zależności od pustej listy:To wydawało się łatwe, ale
COMMA
makro jest dość ciężkie; na szczęście temat jest już omawiany na blogu Jensa Gustedta (dzięki, Jens). Podstawowa sztuczka polega na tym, że makra funkcyjne nie są rozwijane, jeśli nie są nawiasami, w celu uzyskania dalszych wyjaśnień zajrzyj na blog Jensa ... Musimy tylko trochę zmodyfikować makra do naszych potrzeb (zamierzam użyć krótszych nazw i mniej argumentów za zwięzłością).A teraz mamy się dobrze ...
Pełny kod w jednym bloku:
źródło
Czy nie możesz po prostu używać C ++ i nie używać wszystkich innych funkcji C ++ oprócz tej?
Jeśli nadal nie jest to tylko ścisłe C, poleciłbym zamiast tego funkcje variadic .
źródło
Spróbuj zadeklarować te funkcje,
extern "C++"
jakby Twój kompilator to obsługiwał, http://msdn.microsoft.com/en-us/library/s6y4zxec(VS.80).aspxźródło
Mam nadzieję, że poniższy kod pomoże ci zrozumieć przeciążenie funkcji
źródło