Kann sich eine C-Struktur so verhalten, als hätte sie eine Funktion?

13

Ich benutze C und structs, wobei eine Struktur Mitglieder haben kann, aber keine Funktionen. Nehmen wir der Einfachheit halber an, dass ich eine Struktur für Zeichenfolgen erstellen möchte, die ich benenne, strund dass ich tun möchte, str.replace(int i, char c)wo isich der Index der Zeichenfolge befindet und welches cZeichen das Zeichen an der Position ersetzen soll i. Wäre dies niemals möglich, da Strukturen keine Funktionen haben können oder gibt es noch eine Möglichkeit, dieses Verhalten zu implementieren und nachzuahmen, dass eine Struktur eine (einfache) Funktion haben könnte, die eigentlich nur darin besteht, dass die Struktur sich selbst in eine neue Struktur kopiert und diese aktualisiert Felder, was könnte es tun?

Könnte replacealso ein drittes Mitglied der Struktur sein, das auf eine neue Struktur verweist, die aktualisiert wird, wenn darauf zugegriffen wird, oder ähnliches. Könnte es gemacht werden? Oder gibt es etwas Eingebautes oder eine Theorie oder ein Paradigma, das meine Absicht verhindert?

Der Hintergrund ist, dass ich C-Code schreibe und Funktionen neu erfinde, von denen ich weiß, dass sie in OOP-Sprachen eingebaute Bibliotheken sind und dass OOP eine gute Möglichkeit ist, Zeichenfolgen und Befehle zu manipulieren.

Niklas
quelle
5
Ich denke ehrlich, Sie wären besser dran, freie Funktionen zu schreiben, um so etwas zu tun. Wenn Sie jedoch das nötige Moxie haben, lesen Sie cs.rit.edu/~ats/books/ooc.pdf
Robert Harvey
5
Strukturen können Variablen enthalten, die auf Funktionen verweisen. Keine eingebaute Vererbung, aber Sie können Ihre Struktur instanziieren, indem Sie Zeiger verwenden, die auf verschiedene Funktionen mit derselben Signatur verweisen. Oft möchten Sie den ersten Parameter der Funktion zu einem Zeiger auf die Struktur machen.
James McLeod
29
ist replace (& str, i, c) wirklich so viel schlimmer als str.replace (i, c)? Ihre Frage ist eigentlich nicht das Ersetzen von Funktionen, sondern der Versuch, eine neue Syntax in C.
Whatsisname einzufügen
1
@RobertHarvey Danke für den Link cs.rit.edu/~ats/books/ooc.pdf . Schönes Buch (und der Preis stimmt).
John Forkosh
3
@whatsisname: In C müssen Sie den Strukturzeiger sowieso an die Funktion übergeben, damit Sie str.replace(&str, i, c)sowieso am Ende sind . thisNatürlich automatisiert C ++ die Übergabe des Zeigers.
Jonathan Leffler

Antworten:

21

Ihre Funktion sollte so aussehen.

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

Dies akzeptiert einen Zeiger auf das zu bearbeitende Objekt als ersten Parameter. In C ++ wird dies als this-pointer bezeichnet und muss nicht explizit deklariert werden. (Vergleichen Sie dies mit Python, wo es sein muss.)

Um Ihre Funktion aufzurufen, würden Sie diesen Zeiger auch explizit übergeben. Grundsätzlich tauscht man die o.f(…)Syntax gegen die f(&o, …)Syntax. Keine große Sache.

Die Geschichte wird mehr involviert, wenn Sie Polymorphismus unterstützen möchten (akavirtual Funktionen) . Es kann auch in C emuliert werden (ich habe es für diese Antwort gezeigt .), Aber es ist nicht schön, von Hand zu tun.

Wie Jan Hudec kommentiert hat, sollten Sie es sich auch zur Gewohnheit machen, dem Funktionsnamen den Typnamen (dh string_replace) voranzustellen, da C keine Namensräume hat, so dass es nur eine einzige benannte Funktion geben kann replace.

5gon12eder
quelle
17
Natürlich muss die Funktion wahrscheinlich aufgerufen werden string_replace, da C auch keine Funktionsüberladung hat und Sie wahrscheinlich eine andere replacefür einen anderen Typ haben werden ...
Jan Hudec
2
Es kann nicht benannt werden string_replace. Namen, die mit str, memoder wcsgefolgt von einem Kleinbuchstaben beginnen, sind für zukünftige Erweiterungen reserviert.
David Conrad
43

Structs können Funktion halten Zeiger , aber die sind wirklich nur für virtuelle Methoden benötigt. Nicht virtuelle Methoden in objektorientiertem C werden normalerweise ausgeführt, indem die Struktur als erstes Argument an eine reguläre Funktion übergeben wird. Ansehen Gobject an, um ein gutes Beispiel für ein OOP-Framework für C zu finden. Es verwendet Makros, um einen Großteil der für Vererbung und Polymorphie erforderlichen Boilerplates zu verarbeiten.

C wurde vor 44 Jahren erstellt. Es ist eine sehr beliebte Sprache für Open Source. Sie sind nicht die erste Person, die der Meinung ist, dass die Arbeit mit Standard-C-Zeichenfolgen schwierig ist. Suchen Sie nach C-String-Bibliotheken. Sie müssen das Rad nicht neu erfinden.

Karl Bielefeldt
quelle
2
Ein weiteres bemerkenswertes Beispiel ist CPython. Der Code verwendet eine Menge von OOP-Konzepten, aber es ist 100% reines C.
Bakuriu
@ Bakuriu Ich denke, Sie verwirren Cython und CPython
Katze
1
@cat Er meint wahrscheinlich die Python C-API, Cython ist nicht 100% rein C. docs.python.org/c-api/intro.html
JAB
5
@cat Nein. Schauen Sie sich die CPython-Quellen an. Die meisten Dinge werden in der Tat mit dem OOP-Paradigma erledigt, und sie bieten eine OOP-API, die zum größten Teil mit der Python-API übereinstimmt.
Bakuriu
1
@ Bakuriu Oh, du meinst Pythons Laufzeit-, Quell- und C-API, nicht die Python-Sprache. Ihr Kommentar hat das nicht sehr deutlich gemacht
Katze
8

Mit Funktionszeigern können Sie Folgendes tun:

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

Dies ist im Allgemeinen nur sinnvoll, wenn sich die Implementierung ändern kann. In diesem Fall sollten Sie eine vtable verwenden, damit der Overhead nur einen Zeiger pro Struktur darstellt:

str.vtable->replace(&str, i, c);
o11c
quelle
3
Ich würde es immer noch als string_replace (& str, i, c) bezeichnen und dann die vtable in string_replace verwenden, anstatt der Aufrufsite die vtable bekannt zu machen.
Pete Kirkham
2
@Pete Namen, die mit str(oder memoder wcs) beginnen, und Kleinbuchstaben werden vom C-Standard für zukünftige Erweiterungen reserviert. Nennen Sie sie daher nicht string_replace. str_replaceist gut.
David Conrad
3

Ja, das können sie. Sie können die Tatsache ausnutzen, dass C Zeiger auf Funktionsblöcke im Speicher zulässt, auch als Funktionszeiger bezeichnet, und damit Schnittstellen wie Polymorphismus sowie virtuelle Funktionen erstellen (auch wenn sie nicht so hübsch sind).

Ich habe einen Blogbeitrag zu diesem Thema verfasst, der auf eine Frage eines meiner Studenten hin kürzlich zum schnittstellenähnlichen Code in C und Go verfasst wurde. Sie können ihn hier lesen:

Blogbeitrag zu Nicht-OO-Schnittstellen

Sehen Sie nach, ob es Ihnen Ideen gibt.

Sie können auch einfach eine freie Funktion in Ihren Code einfügen und einen "this" -Punkt verwenden. Dies bedeutet, dass Sie einen Zeiger auf eine vorhandene Struktur übergeben, an der gearbeitet werden soll, wie in den anderen Antworten beschrieben.

Richard Tyregrim
quelle