Czy struktura C może zachowywać się tak, jakby miała jakąś funkcję?

13

Używam C i structs, gdzie struct może mieć członków, ale nie funkcje. Załóżmy dla uproszczenia, że ​​chcę utworzyć strukturę dla ciągów, które nazywam stri chcę być w stanie zrobić, str.replace(int i, char c)gdzie ijest indeks ciągu i cjest on znakiem zastępującym znak w miejscu i. Czy nigdy nie byłoby to możliwe, ponieważ struktury nie mogą mieć funkcji lub istnieje sposób, w jaki możemy wdrożyć to zachowanie i naśladować, że struktura może mieć (prostą) funkcję, która w rzeczywistości jest tylko strukturą kopiującą się do nowej struktury i aktualizującą jej pola, które mógłby zrobić?

Więc replacemoże być trzecim członkiem struct że punkty do nowej struktury, która jest aktualizowana, gdy jest ona dostępna lub podobny. Czy można to zrobić? Czy jest coś wbudowanego, teorii lub paradygmatu, który uniemożliwia mi zamiar?

Tło jest takie, że piszę kod C i odkrywam, że odkryłem na nowo funkcje, które są wbudowanymi bibliotekami w językach OOP i że OOP byłby dobrym sposobem na manipulowanie ciągami i poleceniami.

Niklas
źródło
5
Szczerze mówiąc, myślę, że lepiej byłoby pisać darmowe funkcje, aby robić takie rzeczy. Jeśli jednak masz niezbędną moxie, przeczytaj cs.rit.edu/~ats/books/ooc.pdf
Robert Harvey
5
Struktury mogą zawierać zmienne, które są wskaźnikami do funkcji. Nie ma wbudowanego dziedziczenia, ale można utworzyć instancję struktury za pomocą wskaźników wskazujących różne funkcje o tej samej sygnaturze. Często będziesz chciał ustawić pierwszy parametr funkcji jako wskaźnik do struktury.
James McLeod,
29
czy replace (& str, i, c) naprawdę jest o wiele gorszy niż str .replace (i, c)? Twoje pytanie w rzeczywistości nie dotyczy zastąpienia funkcji, ale próbę wprowadzenia nowej składni do C.
whatsisname
1
@RobertHarvey Dzięki za link cs.rit.edu/~ats/books/ooc.pdf . Ładna książka (a cena jest odpowiednia).
John Forkosh,
3
@whatsisname: W C i tak musisz przekazać wskaźnik struktury do funkcji, więc tak czy str.replace(&str, i, c)inaczej. thisOczywiście C ++ automatyzuje przekazywanie wskaźnika.
Jonathan Leffler,

Odpowiedzi:

21

Twoja funkcja powinna wyglądać tak.

void
replace(struct string * s, int i, char c);

To akceptuje wskaźnik do obiektu, który ma działać jako pierwszy parametr. W C ++ jest to znane jako this-pointer i nie musi być jawnie deklarowane. (Porównaj to z Pythonem tam, gdzie musi).

Aby wywołać twoją funkcję, przekazałeś również ten wskaźnik jawnie. Zasadniczo wymieniasz o.f(…)składnię na f(&o, …)składnię. Nic takiego.

Historia staje się bardziej wciągająca, jeśli chcesz wspierać polimorfizm (aka virtualfunkcje). Można go również emulować w C (pokazałem to dla tej odpowiedzi .), Ale nie jest to łatwe ręcznie.

Jak skomentował Jan Hudec , powinieneś również przyzwyczaić się poprzedzać nazwę funkcji nazwą typu (tj. string_replace), Ponieważ C nie ma przestrzeni nazw, więc można nazwać tylko jedną funkcję replace.

5gon12eder
źródło
17
Oczywiście funkcja prawdopodobnie będzie musiała zostać wywołana string_replace, ponieważ C również nie ma przeciążenia funkcji i prawdopodobnie masz jakieś inne replacedla innego typu…
Jan Hudec
2
Nie można go nazwać string_replace. Nazwy rozpoczynające się od str, memlub wcspoprzedzające małą literę są zarezerwowane dla przyszłych rozszerzeń.
David Conrad
43

Struktury mogą przechowywać wskaźniki funkcji , ale tak naprawdę są one potrzebne tylko w przypadku metod wirtualnych. Nie-wirtualne metody w obiektowym C są zwykle wykonywane przez przekazanie struktury jako pierwszego argumentu do funkcji regularnej. Spójrz na Gobject na dobry przykład frameworka OOP dla C. Korzysta on z makr do obsługi wielu elementów kotłowych wymaganych do dziedziczenia i polimorfizmu.

C powstał 44 lata temu. Jest to bardzo popularny język dla open source. Nie jesteś pierwszą osobą, która uważa, że ​​standardowe ciągi C są niezręczne w pracy. Wyszukaj biblioteki ciągów C. Nie musisz wymyślać koła od nowa.

Karl Bielefeldt
źródło
2
Innym godnym uwagi przykładem jest CPython. Kod wykorzystuje wiele koncepcji OOP, ale jest w 100% czysty C.
Bakuriu
@ Bakuriu Myślę, że mylisz Cython i CPython
kot
1
@cat Prawdopodobnie oznacza interfejs API języka Python C. Cython nie jest w 100% czysty C. docs.python.org/c-api/intro.html
JAB
5
@ kot Nie. Spójrz na źródła CPython. Większość rzeczy jest rzeczywiście wykonywana przy użyciu paradygmatu OOP i zapewniają one interfejs API OOP, który w większości odpowiada API python.
Bakuriu
1
@ Bakuriu Och, masz na myśli środowisko wykonawcze Pythona, źródło i API C, a nie język Pythona. twój komentarz nie wyjaśnił tego bardzo jasno
kot
8

Za pomocą wskaźników funkcji możesz:

str.replace(&str, i, c);

Jest to na ogół przydatne tylko wtedy, gdy implementacja może się zmienić, w takim przypadku powinieneś użyć vtable, aby narzut był tylko jednym wskaźnikiem na strukturę:

str.vtable->replace(&str, i, c);
o11c
źródło
3
Nadal będę go nazywać jako string_replace (& str, i, c), a następnie użyję vtable wewnątrz string_replace zamiast informować witrynę wywołującą o vtable.
Pete Kirkham
2
@Pete Nazwy zaczynające się od str(lub memlub wcs) i małe litery są zarezerwowane przez standard C dla przyszłych rozszerzeń, więc nie nazywaj go string_replace. str_replacejest w porządku.
David Conrad
3

Tak, mogą. Możesz skorzystać z faktu, że C pozwala wskaźnikom na funkcjonowanie bloków w pamięci, czyli wskaźników funkcji, i korzystając z tego, możesz stworzyć interfejs, taki jak polimorfizm, a także funkcje wirtualne (nawet jeśli nie jest to tak ładne).

Napisałem post na blogu na ten temat, w odpowiedzi na pytanie jednego z moich studentów, dotyczące kodu podobnego do interfejsu w C i Go, możesz przeczytać tutaj:

Wpis na blogu o interfejsach innych niż OO

Sprawdź, czy to daje jakieś pomysły.

Możesz także po prostu umieścić w kodzie darmową funkcję i użyć wskaźnika „this”, co oznacza, że ​​przekazujesz wskaźnik do istniejącej struktury, aby pracować, jak opisano w innych odpowiedziach.

Richard Tyregrim
źródło