Ist es eine schlechte Praxis, einen C ++ - Compiler nur zum Überladen von Funktionen zu verwenden?


60

Also arbeite ich an einem Software-Design mit C für einen bestimmten Prozessor. Das Tool-Kit enthält die Möglichkeit, sowohl C als auch C ++ zu kompilieren. Für das, was ich mache, ist in dieser Umgebung keine dynamische Speicherzuweisung verfügbar, und das Programm ist insgesamt ziemlich einfach. Ganz zu schweigen davon, dass das Gerät fast keine Prozessorleistung oder -ressourcen hat. Es ist wirklich nicht unbedingt erforderlich, C ++ zu verwenden.

Davon abgesehen gibt es einige Stellen, an denen ich Funktionsüberladungen durchführe (eine Funktion von C ++). Ich muss ein paar verschiedene Arten von Daten senden und habe keine Lust, die printfFormatierung mit einer Art %s(oder was auch immer) Argument zu verwenden. Ich habe einige Leute gesehen, die keinen Zugriff auf einen C ++ - Compiler hatten printf, aber in meinem Fall ist C ++ - Unterstützung verfügbar.

Jetzt werde ich wahrscheinlich die Frage haben, warum ich eine Funktion zunächst überladen muss. Also werde ich versuchen, das jetzt zu beantworten. Ich muss verschiedene Datentypen über eine serielle Schnittstelle übertragen, damit ich einige Überladungen habe, die die folgenden Datentypen übertragen:

unsigned char*
const char*
unsigned char
const char

Ich würde es einfach vorziehen, keine einzige Methode zu haben, die all diese Dinge erledigt. Wenn ich die Funktion aufrufe, möchte ich nur, dass sie die serielle Schnittstelle überträgt, ich habe nicht viele Ressourcen, also möchte ich nur NICHTS tun, sondern nur meine Übertragung.

Jemand anderes hat mein Programm gesehen und mich gefragt: "Warum verwenden Sie CPP-Dateien?" Das ist also mein einziger Grund. Ist das schlechte Praxis?

Aktualisieren

Ich möchte einige gestellte Fragen beantworten:

Eine objektive Antwort auf Ihr Dilemma hängt ab von:

  1. Gibt an, ob die Größe der ausführbaren Datei bei Verwendung von C ++ erheblich zunimmt.

Die Größe der ausführbaren Datei belegt derzeit 4,0% des Programmspeichers (von 5248 Bytes) und 8,3% des Datenspeichers (von 342 Bytes). Das heißt, für C ++ kompilieren ... Ich weiß nicht, wie es für C aussehen würde, da ich den C-Compiler nicht verwendet habe. Ich weiß, dass dieses Programm nicht mehr wachsen wird. Für die begrenzten Ressourcen würde ich sagen, dass es mir gut geht ...

  1. Gibt an, ob die Leistung bei Verwendung von C ++ spürbar beeinträchtigt wird.

Wenn ja, habe ich nichts bemerkt ... aber das könnte auch der Grund sein, warum ich diese Frage stelle, da ich sie nicht vollständig verstehe.

  1. Gibt an, ob der Code auf einer anderen Plattform wiederverwendet werden kann, auf der nur ein C-Compiler verfügbar ist.

Ich weiß, dass die Antwort darauf definitiv nein ist . Wir überlegen derzeit, auf einen anderen Prozessor zu wechseln, aber nur leistungsfähigere ARM-basierte Prozessoren (von denen ich sicher weiß, dass sie über C ++ - Compiler-Toolketten verfügen).


59
Es ist bekannt, dass ich C ++ für ein Projekt nur mit C-Funktionen verwende, um //Kommentare zu erhalten. Wenn es funktioniert, warum nicht?
Jules

76
Die schlechte Praxis wäre, sich auf C zu beschränken, wenn Sie eine gute Verwendung für Funktionen haben, die es nicht bietet.
Jerry Coffin

35
Wenn Sie "einen C ++ - Compiler verwenden" sagen, meinen Sie "C ++ verwenden". Sag es einfach. Sie können C nicht mit einem C ++ - Compiler kompilieren, aber Sie können einfach von C zu C ++ wechseln, was Sie eigentlich tun würden.
user253751

4
"Für das, was ich mache, gibt es keine dynamische Speicherzuweisung auf dem Prozessor und das Programm ist insgesamt ziemlich einfach. Ganz zu schweigen davon, dass das Gerät fast keine Prozessorleistung oder -ressourcen hat. Es ist wirklich nicht unbedingt erforderlich, C ++ zu verwenden." Ich hoffe, dass der erste dieser beiden Sätze Gründe dafür sein sollte, C ++ nicht zu verwenden, da es sich um ziemlich schlechte Sätze handelt. C ++ eignet sich perfekt für eingebettete Systeme.
Pharap

21
@Jules Ich bin sicher, Sie wissen das und haben eine Weile darüber nachgedacht, aber falls jemand dies nicht liest: //Kommentare sind seit C99 im C-Standard.
Davislor

Antworten:


77

Ich würde nicht so weit gehen, es zu nennen „schlechte Praxis“ per se , aber ich bin auch nicht überzeugt , es ist wirklich die richtige Lösung für Ihr Problem. Wenn Sie nur vier separate Funktionen für Ihre vier Datentypen benötigen, warum sollten Sie dann nicht das tun, was C-Programmierer seit jeher getan haben?

void transmit_uchar_buffer(unsigned char *buffer);
void transmit_char_buffer(char *buffer);
void transmit_uchar(unsigned char c);
void transmit_char(char c);

Genau das macht der C ++ - Compiler ohnehin hinter den Kulissen und es ist kein großer Aufwand für den Programmierer. Vermeidet alle Probleme wie "Warum schreiben Sie nicht ganz C mit einem C ++ - Compiler?"


8
Ich würde sagen, warum nicht, weil der übertragene Typ (wahrscheinlich) ein Implementierungsdetail ist. Wenn Sie ihn also ausblenden und den Compiler die Auswahl der Implementierung überlassen, kann dies zu besser lesbarem Code führen. Und wenn die Verwendung einer C ++ - Funktion die Lesbarkeit verbessert, warum dann nicht?
Jules

29
Man kann sogar #definemit C11_Generic
Deduplicator

16
@Jules Weil es dann sehr verwirrend ist, welche C ++ - Features im Projekt verwendet werden dürfen. Würde eine mögliche Änderung, die Objekte enthält, abgelehnt? Was ist mit Vorlagen? Kommentare im C ++ - Stil? Sicher, Sie können das mit einem Dokument im Coding-Stil umgehen, aber wenn Sie nur einen einfachen Fall von Funktionsüberladung tun, schreiben Sie stattdessen einfach C.
Philip Kendall

25
@Phillip "Kommentare im C ++ - Stil" sind seit weit über einem Jahrzehnt in C gültig.
David Conrad

12
@Jules: Während der übertragene Typ wahrscheinlich ein Implementierungsdetail für Software ist, die Versicherungsangebote erstellt, scheint die OP-Anwendung ein eingebettetes System für die serielle Kommunikation zu sein, bei dem Typ und Datengröße von entscheidender Bedeutung sind.
Whatsisname

55

Nur einige Funktionen von C ++ zu verwenden, während es ansonsten als C behandelt wird, ist nicht gerade üblich, aber auch nicht genau unbekannt. Tatsächlich verwenden einige Leute überhaupt keine Funktionen von C ++, außer der strengeren und leistungsfähigeren Typprüfung. Sie schreiben einfach C (wobei darauf geachtet wird, nur an der gemeinsamen Schnittstelle von C ++ und C zu schreiben), kompilieren dann mit einem C ++ - Compiler zur Typprüfung und einem C-Compiler zur Codegenerierung (oder bleiben einfach den ganzen Weg bei einem C ++ - Compiler).

Linux ist ein Beispiel für eine Codebasis, in der regelmäßig gefragt wird, ob Bezeichner wie oder classumbenannt werden sollen , damit Linux mit einem C ++ - Compiler kompiliert werden kann. Offensichtlich gegeben Linus Meinung von C ++ , sie immer bekommen abgeschossen :-D GCC ist ein Beispiel für eine Codebasis , die zuerst transformiert wurde auf „C ++ - sauber C“ und dann Refactoring allmählich mehr C ++ Funktionen zu verwenden.klasskclass

Es ist nichts falsch mit dem, was Sie tun. Wenn Sie die Qualität der Codegenerierung Ihres C ++ - Compilers wirklich paranoid finden, können Sie einen Compiler wie Comeau C ++ verwenden, der in C als Zielsprache kompiliert wird, und anschließend den C-Compiler verwenden. Auf diese Weise können Sie auch den Code vor Ort überprüfen, um festzustellen, ob mit C ++ unvorhergesehener, leistungskritischer Code eingefügt wird. Dies sollte jedoch nicht nur bei Überladung der Fall sein, die buchstäblich nur automatisch anders benannte Funktionen generiert - IOW, genau das, was Sie in C sowieso tun würden.


15

Eine objektive Antwort auf Ihr Dilemma hängt ab von:

  1. Gibt an, ob die Größe der ausführbaren Datei bei Verwendung von C ++ erheblich zunimmt.
  2. Gibt an, ob die Leistung bei Verwendung von C ++ spürbar beeinträchtigt wird.
  3. Gibt an, ob der Code auf einer anderen Plattform wiederverwendet werden kann, auf der nur ein C-Compiler verfügbar ist.

Wenn die Antwort auf eine der Fragen "Ja" lautet, ist es möglicherweise besser, Funktionen mit unterschiedlichen Namen für verschiedene Datentypen zu erstellen und bei C zu bleiben.

Wenn die Antwort auf alle Fragen "nein" lautet, sehe ich keinen Grund, warum Sie C ++ nicht verwenden sollten.


9
Ich muss sagen, ich bin noch nie auf einen C ++ - Compiler gestoßen, der deutlich schlechteren Code generiert als ein C-Compiler für Code, der in der gemeinsam genutzten Teilmenge der beiden Sprachen geschrieben wurde. C ++ - Compiler haben einen schlechten Ruf in Bezug auf Größe und Leistung, aber meiner Erfahrung nach ist es immer eine unangemessene Verwendung von C ++ - Funktionen, die das Problem verursacht haben Vorlagen, aber sonst sollten Sie in Ordnung sein.
Jules

@Jules: Nur für das, was es wert ist (nicht viel, IMO) Ich habe gesehen, was als einzelner Compiler für C und C ++ (Turbo C ++ 1.0, wenn Speicher dient) verkauft wurde, um signifikant unterschiedliche Ergebnisse für identische Eingaben zu erzielen. So wie ich es verstehe, war dies jedoch, bevor sie ihren eigenen C ++ - Compiler fertiggestellt hatten. Obwohl es äußerlich wie ein Compiler aussah, hatte es zwei völlig separate Compiler - einen für C, den anderen für C ++.
Jerry Coffin

1
@ JerryCoffin Wenn Speicher dient, haben die Turbo-Produkte noch nie einen guten Ruf gehabt. Und wenn es eine 1.0-Version wäre, könnte man sich dafür entschuldigen, nicht hoch entwickelt zu sein. Es ist also wahrscheinlich nicht sehr repräsentativ.
Barmar

10
@Barmar: Eigentlich hatten sie eine ganze Weile einen ziemlich anständigen Ruf. Ihr schlechter Ruf ist heute vor allem auf ihr hohes Alter zurückzuführen. Sie sind konkurrenzfähig mit anderen Compilern des gleichen Jahrgangs - aber niemand schreibt Fragen darüber, wie man Dinge mit gcc 1.4 (oder was auch immer) macht. Aber du hast recht - es ist nicht sehr repräsentativ.
Jerry Coffin

1
@Jules Ich würde argumentieren, dass sogar Vorlagen in Ordnung sind. Trotz der weit verbreiteten Theorie wird durch das Instanziieren einer Vorlage die Codegröße nicht implizit erhöht. Die Codegröße wird im Allgemeinen erst erhöht, wenn eine Funktion aus einer Vorlage verwendet wird (in diesem Fall ist die Vergrößerung proportional zur Funktionsgröße) oder wenn die Vorlage nicht größer ist statische Variablen deklarieren. Die Inlining-Regeln gelten weiterhin, so dass es möglich ist, Vorlagen zu schreiben, in denen alle Funktionen vom Compiler inliniert werden.
Pharap

13

Sie stellen die Frage, als hätten Sie beim Kompilieren mit C ++ einfach Zugriff auf weitere Funktionen. Das ist nicht der Fall. Das Kompilieren mit einem C ++ - Compiler bedeutet, dass Ihr Quellcode als C ++ - Quelle interpretiert wird und C ++ eine andere Sprache als C ist. Die beiden haben eine gemeinsame Teilmenge, die groß genug ist, um sinnvoll programmiert zu werden, aber jede hat Funktionen, die die andere nicht hat Es ist möglich, Code zu schreiben, der in beiden Sprachen akzeptabel ist, aber unterschiedlich interpretiert wird.

Wenn wirklich alles, was Sie wollen, Funktionsüberladung ist, dann sehe ich wirklich keinen Grund, das Problem zu verwirren, indem ich C ++ hineinbringe. Anstelle von verschiedenen Funktionen mit dem gleichen Namen, unterschieden sich deren Parameterlisten, schreiben Sie einfach Funktionen mit unterschiedlichen Namen.

Wie für Ihre objektiven Kriterien,

  1. Gibt an, ob die Größe der ausführbaren Datei bei Verwendung von C ++ erheblich zunimmt.

Die ausführbare Datei ist möglicherweise etwas größer, wenn sie als C ++ kompiliert wird, im Vergleich zu ähnlichem C-Code, der als solcher erstellt wurde. Zumindest hat die ausführbare C ++ - Datei den zweifelhaften Vorteil der C ++ - Namensverfälschung für alle Funktionsnamen, sofern alle Symbole in der ausführbaren Datei erhalten bleiben. (Und genau das sorgt in erster Linie für Ihre Überlastungen.) Ob der Unterschied groß genug ist, um für Sie wichtig zu sein, müssten Sie experimentell feststellen.

  1. Gibt an, ob die Leistung bei Verwendung von C ++ spürbar beeinträchtigt wird.

Ich bezweifle, dass Sie für den von Ihnen beschriebenen Code einen merklichen Leistungsunterschied gegenüber einem hypothetischen reinen C-Analog sehen würden.

  1. Gibt an, ob der Code auf einer anderen Plattform wiederverwendet werden kann, auf der nur ein C-Compiler verfügbar ist.

Ich würde dies etwas anders beleuchten: Wenn Sie den Code, den Sie mit C ++ erstellen, mit anderem Code verknüpfen möchten, muss dieser andere Code entweder auch in C ++ geschrieben und mit C ++ erstellt werden, oder Sie benötigen besondere Vorkehrungen in Ihrem eigenen Code treffen ("C" -Verknüpfung deklarieren), was Sie für Ihre überladenen Funktionen auch gar nicht tun können. Wenn Ihr Code dagegen in C geschrieben und als C kompiliert ist, können andere ihn mit C- und C ++ - Modulen verknüpfen. Diese Art von Problem kann in der Regel durch Umdrehen behoben werden. Da Sie C ++ jedoch nicht wirklich zu benötigen scheinen, warum sollten Sie dann ein solches Problem überhaupt akzeptieren?


4
Msgstr "Die ausführbare Datei kann etwas größer sein, wenn sie als C ++ kompiliert wird ... insofern, als alle Symbole in der ausführbaren Datei erhalten bleiben." Um fair zu sein, sollte jede anständige Optimierungs-Toolchain eine Link-Option haben, um diese Symbole in "Release" -Erstellungen zu entfernen, was bedeutet, dass sie nur Ihre Debugging-Erstellungen aufblähen würden. Ich denke nicht, dass das ein großer Verlust ist. In der Tat ist es häufiger ein Vorteil.
Cody Grey

5

Zurück in den Tag, mit einem C ++ Compiler als „besser C“ wurde gefördert als Anwendungsfall. Tatsächlich war das frühe C ++ genau das. Das zugrunde liegende Konstruktionsprinzip bestand darin, dass Sie nur die gewünschten Funktionen verwenden konnten und nicht die Kosten für Funktionen verursachten, die Sie nicht verwendeten. Sie könnten also Funktionen überladen (indem Sie die Absicht über das overloadSchlüsselwort deklarieren!) Und der Rest Ihres Projekts würde nicht nur gut kompilieren, sondern auch Code generieren, der nicht schlechter ist als der C-Compiler.

Seitdem sind die Sprachen etwas auseinander gegangen.

Jeder mallocin Ihrem C-Code wird ein Typenkonfliktfehler sein - in Ihrem Fall kein Problem ohne dynamischen Speicher! Ebenso werden alle Ihre voidZeiger in C Sie auslösen, da Sie explizite Besetzungen hinzufügen müssen. Aber… warum tust du das so? … Du wirst immer mehr auf den Weg gebracht, C ++ - Funktionen zu nutzen.

Es könnte also mit etwas zusätzlicher Arbeit möglich sein. Es wird jedoch als Gateway für die Übernahme von C ++ in größerem Maßstab dienen. In ein paar Jahren werden sich die Leute über Ihren Legacy-Code beschweren, der so aussieht, als wäre er 1989 geschrieben worden, indem sie Ihre mallocAufrufe durch ersetzen new, Codeblöcke von Schleifenkörpern herausreißen, um stattdessen nur einen Algorithmus aufzurufen, und den unsicheren gefälschten Polymorphismus beschimpfen, der dazu führen würde war trivial gewesen, hätte der Compiler es tun dürfen.

Andererseits wissen Sie, dass es egal wäre, wenn Sie es in C geschrieben hätten. Ist es also jemals falsch, in C anstatt in C ++ zu schreiben, wenn es existiert? Wenn die Antwort „Nein“ lautet, kann die Verwendung von aus C ++ ausgewählten Funktionen auch nicht falsch sein .


2

Viele Programmiersprachen haben eine oder mehrere Kulturen, die sich um sie herum entwickeln, und sie haben bestimmte Vorstellungen darüber, was eine Sprache "sein" soll. Während es möglich, praktisch und nützlich sein sollte, eine für die Systemprogrammierung geeignete niedrige Programmiersprache zu haben, die einen für diesen Zweck geeigneten Dialekt von C mit einigen Merkmalen von C ++ ergänzt, die ebenfalls geeignet wären, ziehen die Kulturen, die die beiden Sprachen umgeben, t befürworten insbesondere eine solche Fusion.

Ich habe von einem eingebetteten C-Compiler gelesen, der einige Funktionen von C ++ enthielt, einschließlich der Fähigkeit zu haben

foo.someFunction(a,b,c);

im Wesentlichen als interpretiert

typeOfFoo_someFunction(foo, a, b, c);

sowie die Fähigkeit, statische Funktionen zu überladen (Probleme mit der Namensverknüpfung treten nur beim Export auf)Funktionen sind überladen). Es gibt keinen Grund, warum C-Compiler solche Funktionen nicht unterstützen sollten, ohne zusätzliche Anforderungen an die Verknüpfungs- und Laufzeitumgebung zu stellen. Viele C-Compiler lehnen jedoch fast alles von C ++ reflexartig ab, um Umweltbelastungen zu vermeiden lehnen Sie sogar Funktionen ab, die keine derartigen Kosten verursachen. Unterdessen hat die Kultur von C ++ wenig Interesse an Umgebungen, die nicht alle Funktionen dieser Sprache unterstützen können. Sofern oder bis sich die Kultur von C ändert, um solche Funktionen zu akzeptieren, oder die Kultur von C ++ ändert, um die Implementierung von Lightweight-Subsets zu fördern, ist "Hybrid" -Code anfällig für viele der Einschränkungen beider Sprachen, während seine Fähigkeit, zu ernten, eingeschränkt ist die Vorteile des Betriebs in einer kombinierten Obermenge.

Wenn eine Plattform sowohl C als auch C ++ unterstützt, wird der zur Unterstützung von C ++ erforderliche zusätzliche Bibliothekscode die ausführbare Datei wahrscheinlich etwas größer machen, obwohl der Effekt nicht besonders groß sein wird. Die Ausführung von "C-Funktionen" in C ++ ist wahrscheinlich nicht besonders betroffen. Ein größeres Problem könnte sein, wie C- und C ++ - Optimierer verschiedene Eckfälle behandeln. Das Verhalten von Datentypen in einem C wird hauptsächlich durch den Inhalt der darin gespeicherten Bits bestimmt, und C ++ verfügt über eine anerkannte Kategorie von Strukturen (PODS - Plain Old Data Structures), deren Semantik ebenfalls hauptsächlich durch die gespeicherten Bits bestimmt wird. Sowohl der C- als auch der C ++ - Standard erlauben jedoch manchmal ein Verhalten, das dem durch die gespeicherten Bits implizierten widerspricht, und die Ausnahmen zum Verhalten "gespeicherter Bits" unterscheiden sich zwischen den beiden Sprachen.


1
Interessante Meinung, geht aber nicht auf die Frage des OP ein.
R Sahu

@RSahu: Code, der zur "Kultur" einer Sprache passt, kann besser unterstützt und leichter an eine Vielzahl von Implementierungen angepasst werden als Code, der dies nicht tut. Die Kulturen von C und C ++ sind der Art der Verwendung, die das OP vorschlägt, abgeneigt. Ich werde einige spezifischere Punkte in Bezug auf die spezifischen Fragen hinzufügen.
Supercat

1
Beachten Sie, dass das C ++ - Standardkomitee eine Embedded- / Financial- / Gaming-Gruppe umfasst, die genau die Art von Situationen lösen soll, von denen Sie behaupten, dass sie "wenig interessant" sind.
Yakk

@Yakk: Ich gebe zu, dass ich die C ++ - Welt nicht so genau verfolgt habe, da meine Interessen hauptsächlich bei C liegen. Ich weiß, dass es Bemühungen um mehr eingebettete Dialekte gegeben hat, aber ich hatte nicht gedacht, dass sie wirklich irgendwo angekommen sind.
Supercat

2

Ein weiterer wichtiger Faktor, den Sie berücksichtigen sollten: Wer auch immer Ihren Code erben wird.

Wird diese Person immer ein C-Programmierer mit einem C-Compiler sein? Das nehme ich an.

Wird diese Person auch ein C ++ - Programmierer mit einem C ++ - Compiler sein? Ich möchte mir diesbezüglich einigermaßen sicher sein, bevor ich meinen Code von C ++ - spezifischen Dingen abhängig mache.


2

Polymorphismus ist eine wirklich gute Funktion, die C ++ kostenlos zur Verfügung stellt. Es gibt jedoch noch weitere Aspekte, die Sie bei der Auswahl eines Compilers berücksichtigen müssen. Es gibt eine Alternative für Polymorphismus in C, aber seine Verwendung kann einige hochgezogene Augenbrauen verursachen. Informationen zur Variadic-Funktion finden Sie im Tutorial zur Variadic-Funktion .

In deinem Fall wäre es so etwas wie

enum serialDataTypes
{
 INT =0,
 INT_P =1,
 ....
 }Arg_type_t;
....
....
void transmit(Arg_type_t serial_data_type, ...)
{
  va_list args;
  va_start(args, serial_data_type);

  switch(serial_data_type)
  {
    case INT: 
    //Send integer
    break;

    case INT_P:
    //Send data at integer pointer
    break;
    ...
   }
 va_end(args);
}

Ich mag Phillips Ansatz, aber es verschmutzt Ihre Bibliothek mit vielen Anrufen. Mit dem oben genannten ist die Schnittstelle sauber. Es hat seine Nachteile und letztendlich ist es eine Frage der Wahl.


Variadic-Funktionen dienen in erster Linie dem Anwendungsfall, in dem sowohl die Anzahl der Argumente als auch ihre Typen variabel sind. Ansonsten brauchen Sie es nicht und insbesondere ist es für Ihr spezielles Beispiel übertrieben. Es wäre einfacher, eine gewöhnliche Funktion zu verwenden, die ein Arg_type_tund ein void *Zeigen auf die Daten akzeptiert . Vor diesem Hintergrund ist es in der Tat eine sinnvolle Alternative, der (einzelnen) Funktion ein Argument zu geben, das den Datentyp angibt.
John Bollinger

1

In Ihrem speziellen Fall, in dem Sie eine "Funktion" für einige verschiedene Typen statisch überladen, sollten Sie stattdessen C11 mit seiner _GenericMakromaschine verwenden. Ich denke, es könnte genug für Ihre begrenzten Bedürfnisse sein.

Mit Philip Kendalls Antwort könnten Sie definieren:

#define transmit(X) _Generic((X),      \
   unsigned char*: transmit_uchar_buffer, \
   char*: transmit_char_buffer,           \
   unsigned char: transmit_uchar,         \
   char: transmit_char) ((X))

und Code, transmit(foo) was auch immer der Typ ist foo(unter den vier oben aufgeführten Typen).

Wenn Sie sich nur für GCC- Compiler (und kompatible Compiler, z. B. Clang- Compiler) __builtin_types_compatible_pinteressieren , können Sie die typeofErweiterung in Betracht ziehen .


1

Ist es eine schlechte Praxis, einen C ++ - Compiler nur zum Überladen von Funktionen zu verwenden?

IMHO-Standpunkt, ja, und ich muss schizophren werden, um diesen zu beantworten, da ich beide Sprachen liebe, aber es hat nichts mit Effizienz zu tun, sondern eher mit Sicherheit und idiomatischem Sprachgebrauch.

C Seite

Vom C-Standpunkt aus finde ich es so verschwenderisch, dass Ihr Code C ++ erfordert, nur um das Überladen von Funktionen zu verwenden. Sofern Sie es nicht für statischen Polymorphismus mit C ++ - Vorlagen verwenden, ist es ein so trivialer syntaktischer Zucker, der im Austausch für den Wechsel zu einer völlig anderen Sprache gewonnen wird. Wenn Sie Ihre Funktionen jemals in eine Dylib exportieren möchten (kann ein praktisches Problem sein oder auch nicht), können Sie dies nicht mehr sehr praktisch für den weit verbreiteten Gebrauch mit allen mit Namen versehenen Symbolen tun.

C ++ - Seite

Aus C ++ - Sicht sollten Sie C ++ nicht wie C mit Funktionsüberladung verwenden. Dies ist kein stilistischer Dogmatismus, sondern ein Dogmatismus, der sich auf die praktische Anwendung des alltäglichen C ++ bezieht.

Ihre normale Art von C-Code ist nur dann einigermaßen vernünftig und "sicher" zu schreiben, wenn Sie gegen das C-Typ-System arbeiten, das das Kopieren von C-Codes verbietet structs. Sobald Sie in C ++ 's viel umfangreicherem Typensystem arbeiten, sollten Sie sich ständig auf Funktionen stützen , die von enormem Wert sind memsetund memcpynicht zu Funktionen werden. Stattdessen sind es Funktionen, die Sie im Allgemeinen vermeiden möchten, wie die Pest, da Sie sie bei C ++ - Typen nicht wie unformatierte Bits und Bytes behandeln sollten, die kopiert, gemischt und freigegeben werden sollen. Selbst wenn Ihr Code memsetderzeit nur Dinge wie bei Primitiven und POD-UDTs verwendet, fügt jeder Benutzer einem von Ihnen verwendeten UDT einen ctor hinzu (einschließlich des Hinzufügens eines Members, für das ein solcher erforderlich ist, z. B.std::unique_ptrmember) gegen solche Funktionen oder eine virtuelle Funktion oder irgendetwas in dieser Art macht es Ihre gesamte normale C-artige Codierung anfällig für undefiniertes Verhalten. Nimm es von Herb Sutter selbst:

memcpyund das memcmpTypensystem verletzen. Das memcpyKopieren von Objekten ist wie das Geldverdienen mit einem Fotokopierer. Das memcmpVergleichen von Objekten ist wie das Vergleichen von Leoparden durch Zählen ihrer Flecken. Die Tools und Methoden scheinen die Aufgabe zu erfüllen, sind jedoch zu grob, um sie akzeptabel auszuführen. Bei C ++ - Objekten geht es ausschließlich um das Verbergen von Informationen (wohl das rentabelste Prinzip in der Softwareentwicklung; siehe Punkt 11): Objekte verbergen Daten (siehe Punkt 41) und entwickeln präzise Abstraktionen zum Kopieren dieser Daten durch Konstruktoren und Zuweisungsoperatoren (siehe Punkte 52 bis 55). . Bulldozing über all das mit memcpyist eine schwerwiegende Verletzung des Versteckens von Informationen und führt oft zu Speicher- und Ressourcenlecks (bestenfalls), Abstürzen (schlimmer) oder undefiniertem Verhalten (schlimmstenfalls) - C ++ Coding Standards.

So viele C-Entwickler würden dem zu Recht widersprechen, da die Philosophie nur gilt, wenn Sie Code in C ++ schreiben. Sie höchstwahrscheinlich werden das Schreiben sehr problematischen Code , wenn Sie Funktionen wie verwenden memcpyalle Zeiten in Code, der als C ++ erstellt , aber es ist völlig in Ordnung , wenn man es in C . Die beiden Sprachen sind in dieser Hinsicht aufgrund der Unterschiede im Typensystem sehr unterschiedlich. Es ist sehr verlockend, die Teilmenge der Funktionen zu betrachten, die diese beiden gemeinsam haben, und zu glauben, dass eine wie die andere verwendet werden kann, insbesondere auf der C ++ - Seite, aber C + -Code (oder C-- -Code) ist im Allgemeinen weitaus problematischer als sowohl C als auch C ++ Code.

Ebenso sollten Sie beispielsweise nicht mallocin einem C- ähnlichen Kontext (der keine EH impliziert) arbeiten, wenn C ++ - Funktionen direkt aufgerufen werden können, die ausgelöst werden können, da Sie dann einen impliziten Exit-Punkt in Ihrer Funktion als Ergebnis von haben Ausnahme, die Sie nicht effektiv abfangen können, freewenn Sie Code im C-Stil schreiben, bevor Sie in den Speicher zugreifen können . Also , wenn Sie eine Datei, die als C ++ baut mit einer .cppErweiterung oder was auch immer und es tut all diesen Arten von Dingen wie malloc, memcpy, memset,qsortWenn nicht bereits, wird nach Problemen gefragt, es sei denn, es handelt sich um das Implementierungsdetail einer Klasse, die nur mit primitiven Typen arbeitet. Zu diesem Zeitpunkt muss die Ausnahmebehandlung noch ausgeführt werden, um ausnahmesicher zu sein. Wenn Sie C ++ Code schreiben wollen Sie stattdessen im Allgemeinen auf RAII verlassen verwenden , Dinge wie vector, unique_ptr, shared_ptrusw., und vermeiden Sie alle normalen C-Stil - Codierung , wenn möglich.

Der Grund, warum Sie mit Rasierklingen in C- und Röntgendatentypen spielen und mit ihren Bits und Bytes spielen können, ohne Kollateralschäden in einem Team zu verursachen (obwohl Sie sich trotzdem so oder so wirklich verletzen können), liegt nicht an dem, was C Typen können , aber aufgrund dessen, was sie niemals können. In dem Moment, in dem Sie das Typensystem von C erweitern, um C ++ - Funktionen wie ctors, dtors und vtables zusammen mit der Ausnahmebehandlung einzubeziehen, wird der gesamte idiomatische C-Code weitaus gefährlicher gerendert, als er derzeit ist, und Sie werden eine neue Art von sehen Entwicklung von Philosophie und Denkweise, die eine völlig andere Art der Codierung fördern, wie Sie in C ++ sehen, das jetzt sogar die Verwendung eines rohen Zeigerfehlverhaltens für eine Klasse in Betracht zieht, die das Gedächtnis verwaltet, im Gegensatz zu beispielsweise einer RAII-konformen Ressource wie unique_ptr. Diese Einstellung ist nicht aus einem absoluten Sicherheitsgefühl entstanden. Es hat sich aus dem entwickelt, was C ++ speziell für Funktionen wie die Ausnahmebehandlung benötigt, wenn man bedenkt, was es nur durch sein Typensystem zulässt .

Ausnahmesicherheit

Sobald Sie sich in C ++ befinden, erwarten die Benutzer, dass Ihr Code ausnahmesicher ist. In Zukunft wird Ihr Code möglicherweise gewartet, vorausgesetzt, er ist bereits in C ++ geschrieben und kompiliert und wird einfach std::vector, dynamic_cast, unique_ptr, shared_ptrin Code verwendet, der entweder direkt oder indirekt von Ihrem Code aufgerufen wird Code. An diesem Punkt müssen wir uns der Chance stellen, dass die Dinge sich auswirken, und dann, wenn Sie perfekten und schönen C-Code wie diesen nehmen:

int some_func(int n, ...)
{
    int* x = calloc(n, sizeof(int));
    if (x)
    {
        f(n, x); // some function which, now being a C++ function, may 
                 // throw today or in the future.
        ...
        free(x);
        return success;
    }
    return fail;
}

... es ist jetzt kaputt. Es muss umgeschrieben werden, um ausnahmesicher zu sein:

int some_func(int n, ...)
{
    int* x = calloc(n, sizeof(int));
    if (x)
    {
        try
        {
            f(n, x); // some function which, now being a C++ function, may 
                     // throw today or in the future (maybe someone used
                     // std::vector inside of it).
        }
        catch (...)
        {
            free(x);
            throw;
        }
        ...
        free(x);
        return success;
    }
    return fail;
}

Brutto! Aus diesem Grund würden die meisten C ++ - Entwickler dies stattdessen fordern:

void some_func(int n, ...)
{
    vector<int> x(n);
    f(x); // some function which, now being a C++ function, may throw today
          // or in the future.
}

Die oben ist RAH-konforme Ausnahme-sichere Code der Art , dass C ++ Entwickler im Allgemeinen , da die Funktion Leck nicht nein , die Codezeile löst einen impliziten Ausgang als Folge eines billigen würden throw.

Wähle eine Sprache

Sie sollten entweder das Typensystem und die Philosophie von C ++ mit RAII, Ausnahmesicherheit, Vorlagen, OOP usw. einbeziehen oder C einbeziehen, das sich hauptsächlich um unformatierte Bits und Bytes dreht. Sie sollten keine unheilige Ehe zwischen diesen beiden Sprachen eingehen, sondern sie in verschiedene Sprachen aufteilen, um sie ganz anders zu behandeln, anstatt sie zu verwischen.

Diese Sprachen wollen dich heiraten. Im Allgemeinen musst du dir eine aussuchen, anstatt dich mit beiden zu verabreden und herumzublödeln. Oder du kannst ein Polygamist wie ich sein und beide heiraten, aber du musst dein Denken völlig ändern, wenn du Zeit miteinander verbringst, und sie gut voneinander getrennt halten, damit sie sich nicht bekämpfen.

Binäre Größe

Aus Neugier habe ich gerade versucht, meine kostenlose Listenimplementierung und meinen kostenlosen Benchmark auf C ++ zu portieren, da ich wirklich neugierig geworden bin:

[...] weiß nicht, wie es für C aussehen würde, weil ich den C-Compiler nicht verwendet habe.

... und wollte wissen, ob sich die Binärgröße überhaupt aufblähen würde, wenn man nur C ++ baut. Es erforderte, dass ich explizite Casts über den ganzen Ort verteilte, was nur eine Minute dauerte (ein Grund, warum ich es mag, Low-Level-Dinge wie Allokatoren und Datenstrukturen besser in C zu schreiben).

Dies war nur ein Vergleich eines MSVC-64-Bit-Release-Builds für eine einfache Konsolenanwendung mit Code, der keine C ++ - Funktionen verwendete, nicht einmal das Überladen von Operatoren - nur der Unterschied zwischen dem Erstellen mit C und der Verwendung von beispielsweise <cstdlib>anstelle von <stdlib.h>und solche Dinge, aber ich war überrascht, dass es keinen Unterschied zur Binärgröße machte!

Die Binärdatei bestand aus 9,728Bytes, als sie in C erstellt wurde, und ebenso aus 9,278Bytes, als sie als C ++ - Code kompiliert wurde. Das habe ich eigentlich nicht erwartet. Ich dachte, Dinge wie EH würden dort zumindest ein wenig hinzufügen (dachten, es würde sich zumindest um einhundert Bytes unterscheiden), obwohl es wahrscheinlich möglich war, herauszufinden, dass es nicht notwendig war, EH-bezogene Anweisungen hinzuzufügen, da ich nur bin mit der C-Standardbibliothek und nichts löst. Ich dachte etwaswürde ein wenig zur Binärgröße hinzufügen, wie z. B. RTTI. Jedenfalls war es irgendwie cool, das zu sehen. Natürlich denke ich nicht, dass Sie dieses eine Ergebnis verallgemeinern sollten, aber es hat mich zumindest ein bisschen beeindruckt. Es hat auch keinen Einfluss auf die Benchmarks, und natürlich, da ich mir vorstelle, dass die identische resultierende Binärgröße auch identische resultierende Maschinenbefehle bedeutete.

Wer kümmert sich um die Binärgröße bei den oben genannten Sicherheits- und Engineering-Problemen? Suchen Sie sich also erneut eine Sprache aus und nehmen Sie deren Philosophie an, anstatt zu versuchen, sie zu verfälschen. das empfehle ich.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.