Auf meinen Linux- (und OS X-) Computern hat die iconv()
Funktion diesen Prototyp:
size_t iconv (iconv_t, char **inbuf...
auf FreeBSD sieht es so aus:
size_t iconv (iconv_t, const char **inbuf...
Ich möchte, dass mein C ++ - Code auf beiden Plattformen erstellt wird. Bei C-Compilern gibt das Übergeben von a char**
für einen const char**
Parameter (oder umgekehrt) normalerweise nur eine Warnung aus. In C ++ ist dies jedoch ein schwerwiegender Fehler. Wenn ich also a übergebe char**
, wird es unter BSD nicht kompiliert, und wenn ich a übergebe const char**
, wird es unter Linux / OS X nicht kompiliert. Wie kann ich Code schreiben, der auf beiden kompiliert wird, ohne zu versuchen, die Plattform zu erkennen?
Eine (fehlgeschlagene) Idee war, einen lokalen Prototyp bereitzustellen, der alle vom Header bereitgestellten überschreibt:
void myfunc(void) {
size_t iconv (iconv_t, char **inbuf);
iconv(foo, ptr);
}
Dies schlägt fehl, weil iconv
eine C-Verknüpfung erforderlich ist und Sie keine extern "C"
Funktion einfügen können (warum nicht?)
Die beste Arbeitsidee, die ich mir ausgedacht habe, ist, den Funktionszeiger selbst umzuwandeln:
typedef void (*func_t)(iconv_t, const char **);
((func_t)(iconv))(foo, ptr);
Dies kann jedoch andere, schwerwiegendere Fehler maskieren.
quelle
iconv
mussinbuf
non-const sein.iconv
ohneconst
: svnweb.freebsd.org/base/stable/9/include/…Antworten:
Wenn Sie nur ein Auge auf einige konstante Probleme werfen möchten, können Sie eine Konvertierung verwenden, die die Unterscheidung verwischt, dh char ** und const char ** interoperabel macht:
Dann später im Programm:
sloppy () nimmt a
char**
oder aconst char*
und konvertiert es in achar**
oder aconst char*
, unabhängig davon, was der zweite Parameter von iconv verlangt.UPDATE: geändert, um const_cast zu verwenden und schlampig aufzurufen, nicht als Besetzung.
quelle
sloppy<char**>()
Initialisierer direkt dort aufrufen .(char**)&in
wenn Sie zuerst ein typedef für erstellenchar**
.Sie können zwischen den beiden Deklarationen unterscheiden, indem Sie die Signatur der deklarierten Funktion überprüfen. Hier ist ein grundlegendes Beispiel für die Vorlagen, die zum Überprüfen des Parametertyps erforderlich sind. Dies könnte leicht verallgemeinert werden (oder Sie könnten die Funktionsmerkmale von Boost verwenden), aber dies reicht aus, um eine Lösung für Ihr spezifisches Problem zu demonstrieren:
Hier ist ein Beispiel, das das Verhalten demonstriert:
Sobald Sie die Qualifikation des Parametertyps erkannt haben, können Sie zwei Wrapper-Funktionen schreiben, die aufrufen
iconv
: eine, dieiconv
mit einemchar const**
Argument aufruft, und eine, dieiconv
mit einemchar**
Argument aufruft .Da die Spezialisierung von Funktionsvorlagen vermieden werden sollte, verwenden wir eine Klassenvorlage, um die Spezialisierung durchzuführen. Beachten Sie, dass wir jeden Aufrufer auch zu einer Funktionsvorlage machen, um sicherzustellen, dass nur die von uns verwendete Spezialisierung instanziiert wird. Wenn der Compiler versucht, Code für die falsche Spezialisierung zu generieren, werden Fehler angezeigt.
Wir schließen diese dann mit einem ab
call_iconv
, um das Aufrufen so einfach wie dasiconv
direkte Aufrufen zu machen . Das Folgende ist ein allgemeines Muster, das zeigt, wie dies geschrieben werden kann:(Diese letztere Logik könnte bereinigt und verallgemeinert werden. Ich habe versucht, jedes Stück explizit zu machen, um hoffentlich klarer zu machen, wie es funktioniert.)
quelle
decltype
erfordert C ++ 11.#ifdef
, dass Sie nach der Plattform suchen, erhalten Sie 30 ungerade Codezeilen :) Netter Ansatz (obwohl ich mir in den letzten Tagen Sorgen gemacht habe, Fragen zu SO zu stellen, die Leute nicht haben Ich verstehe wirklich, was sie tun. Ich habe angefangen, SFINAE als goldenen Hammer zu verwenden ... nicht Ihr Fall, aber ich befürchte, dass der Code komplexer und schwer zu pflegen sein wird ...)Sie können Folgendes verwenden:
Sie können übergeben
const char**
und unter Linux / OSX wird es die Vorlagenfunktion durchlaufen und unter FreeBSD wird es direkt zu geheniconv
.Nachteil: Es ermöglicht Aufrufe, mit
iconv(foo, 2.5)
denen der Compiler unendlich oft wiederholt wird.quelle
const_cast
müsste der jedoch in einen verschoben werdenadd_or_remove_const
, der sich in den eingreiftT**
, um festzustellen, ob dies der FallT
ist,const
und gegebenenfalls eine Qualifikation hinzufügen oder entfernen. Dies wäre immer noch (weitaus) einfacher als die Lösung, die ich demonstriert habe. Mit ein wenig Arbeit ist es möglicherweise auch möglich, diese Lösung ohne das zu verwendenconst_cast
(dh indem Sie eine lokale Variable in Ihrem verwendeniconv
).iconv
nicht const ist, wird nichtT
als abgeleitetconst char**
, was bedeutet, dass der Parameterinbuf
den Typ hatconst T
, der istconst char **const
, und der Aufruf voniconv
in der Vorlage sich selbst aufruft? Wie James jedoch sagt, istT
dieser Trick mit einer geeigneten Modifikation des Typs die Grundlage für etwas, das funktioniert.Hier haben Sie IDs aller Betriebssysteme. Für mich hat es keinen Sinn, etwas zu tun, was vom Betriebssystem abhängt, ohne dieses System zu überprüfen. Es ist, als würde man eine grüne Hose kaufen, ohne sie anzusehen.
quelle
without resorting to trying to detect the platform
...Sie haben angegeben, dass die Verwendung Ihrer eigenen Wrapper-Funktion akzeptabel ist. Sie scheinen auch bereit zu sein, mit Warnungen zu leben.
Schreiben Sie Ihren Wrapper also nicht in C ++, sondern in C, wo Sie nur auf einigen Systemen eine Warnung erhalten:
quelle
Wie wäre es mit
EDIT: Natürlich ist das "ohne die Plattform zu erkennen" ein kleines Problem. Hoppla :-(
EDIT 2: ok, verbesserte Version vielleicht?
quelle
const char**
,Wie wäre es mit:
Ich denke , das strenge Aliasing in C ++ verletzt 03, aber nicht in C ++ 11 , da in C ++ 11
const char**
undchar**
sind so genannte "ähnliche Typen". Sie werden diese Verletzung des strengen Aliasing nicht vermeiden, außer indem Sie ein erstellenconst char*
, es auf setzen*foo
,iconv
mit einem Zeiger auf das temporäre Element aufrufen und das Ergebnis*foo
nach a zurückkopierenconst_cast
:Dies ist sicher vor dem POV der Konstantenkorrektheit, weil alles damit zu
iconv
tun hatinbuf
Zuwachs ist der Zeiger in ihm gespeichert. Wir "werfen const weg" von einem Zeiger, der von einem Zeiger abgeleitet wurde, der nicht const war, als wir ihn zum ersten Mal sahen.Wir könnten auch eine Überlastung der schreiben
myconv
undmyconv_helper
das nimmtconst char **inbuf
und Verwirrungen Dinge über in der anderen Richtung, so dass der Anrufer die Wahl hat , ob in einem zu übergebenconst char**
oder einchar**
. Welches wohliconv
dem Aufrufer in C ++ eigentlich hätte geben sollen, aber natürlich wird die Schnittstelle nur von C kopiert, wo es keine Funktionsüberladung gibt.quelle
Update: Jetzt sehe ich, dass es möglich ist, es in C ++ ohne Autotools zu handhaben, aber ich lasse die Autoconf-Lösung für Leute, die danach suchen.
Was Sie suchen, ist
iconv.m4
das, was von gettext package installiert wird.AFAICS es ist nur:
in configure.ac, und es sollte den richtigen Prototyp erkennen.
Dann in dem Code, den Sie verwenden:
quelle
gettext
Paket installiert . Auch ist es durchaus üblich , für Verpackungen verwendet Makros im aufzunehmenm4/
Verzeichnis und habenACLOCAL_AMFLAGS = -I m4
inMakefile.am
. Ich denke, Autopoint kopiert es sogar standardmäßig in dieses Verzeichnis.Ich bin zu spät zu dieser Party, aber hier ist meine Lösung:
quelle