Sur mes machines Linux (et OS X), la iconv()
fonction a ce prototype:
size_t iconv (iconv_t, char **inbuf...
tandis que sur FreeBSD, cela ressemble à ceci:
size_t iconv (iconv_t, const char **inbuf...
J'aimerais que mon code C ++ soit basé sur les deux plates-formes. Avec les compilateurs C, passer un char**
pour un const char**
paramètre (ou vice versa) émet généralement un simple avertissement; cependant en C ++ c'est une erreur fatale. Donc si je passe a char**
, il ne se compilera pas sur BSD, et si je passe a, const char**
il ne se compilera pas sous Linux / OS X. Comment puis-je écrire du code qui compile sur les deux, sans avoir recours à la détection de la plate-forme?
Une idée (ratée) que j'ai eue était de fournir un prototype local qui remplace tous ceux fournis par l'en-tête:
void myfunc(void) {
size_t iconv (iconv_t, char **inbuf);
iconv(foo, ptr);
}
Cela échoue car iconv
nécessite une liaison C et vous ne pouvez pas mettre extern "C"
dans une fonction (pourquoi pas?)
La meilleure idée de travail que j'ai proposée est de lancer le pointeur de fonction lui-même:
typedef void (*func_t)(iconv_t, const char **);
((func_t)(iconv))(foo, ptr);
mais cela a le potentiel de masquer d'autres erreurs plus graves.
la source
iconv
nécessite que leinbuf
soit non-const.iconv
sans leconst
: svnweb.freebsd.org/base/stable/9/include/…Réponses:
Si vous voulez simplement fermer les yeux sur certains problèmes de const, vous pouvez utiliser une conversion qui brouille la distinction, c'est-à-dire rend les char ** et const char ** interopérables:
Puis plus tard dans le programme:
bâclée () prend un
char**
ouconst char*
et le convertit en unchar**
ouconst char*
, quel que soit le deuxième paramètre de iconv demande.UPDATE: changé pour utiliser const_cast et appeler sloppy not a as cast.
la source
sloppy<char**>()
initialiseur directement là-bas.(char**)&in
sauf si vous créez d'abord un typedef pourchar**
.Vous pouvez lever l'ambiguïté entre les deux déclarations en inspectant la signature de la fonction déclarée. Voici un exemple de base des modèles requis pour inspecter le type de paramètre. Cela pourrait facilement être généralisé (ou vous pouvez utiliser les traits de fonction de Boost), mais cela suffit pour démontrer une solution à votre problème spécifique:
Voici un exemple qui illustre le comportement:
Une fois que vous pouvez détecter la qualification du type de paramètre, vous pouvez écrire deux fonctions wrapper qui appellent
iconv
: une qui appelleiconv
avec unchar const**
argument et une qui appelleiconv
avec unchar**
argument.Étant donné que la spécialisation du modèle de fonction doit être évitée, nous utilisons un modèle de classe pour effectuer la spécialisation. Notez que nous faisons également de chacun des invocateurs un modèle de fonction, pour nous assurer que seule la spécialisation que nous utilisons est instanciée. Si le compilateur essaie de générer du code pour la mauvaise spécialisation, vous obtiendrez des erreurs.
Nous enveloppons ensuite l'utilisation de ceux-ci avec un
call_iconv
pour rendre cet appel aussi simple que d'appelericonv
directement. Voici un schéma général montrant comment cela peut être écrit:(Cette dernière logique pourrait être nettoyée et généralisée; j'ai essayé de rendre chaque élément explicite pour, espérons-le, clarifier son fonctionnement.)
la source
decltype
nécessite C ++ 11.#ifdef
vérifier la plate-forme, vous vous retrouvez avec 30 lignes de code impaires :) Bonne approche cependant (même si je me suis inquiété ces derniers jours en regardant des questions sur SO que les gens qui ne le font pas vraiment comprendre ce qu'ils font ont commencé à utiliser SFINAE comme un marteau d'or ... ce n'est pas votre cas, mais je crains que le code ne devienne plus complexe et difficile à maintenir ...)Vous pouvez utiliser les éléments suivants:
Vous pouvez passer
const char**
et sous Linux / OSX, il passera par la fonction de modèle et sur FreeBSD, il ira directementiconv
.Inconvénient: cela permettra des appels comme ceux
iconv(foo, 2.5)
qui mettront le compilateur en récurrence infinie.la source
const_cast
devrait être déplacé dans unadd_or_remove_const
qui creuse dans leT**
pour détecter siT
estconst
et ajouter ou supprimer la qualification le cas échéant. Ce serait encore (beaucoup) plus simple que la solution que j'ai démontrée. Avec un peu de travail, il pourrait également être possible de faire fonctionner cette solution sans leconst_cast
(c'est-à-dire en utilisant une variable locale dans votreiconv
).iconv
est non-const, n'est pasT
déduit commeconst char**
, ce qui signifie que le paramètreinbuf
a le typeconst T
, qui estconst char **const
, et l'appel àiconv
dans le modèle s'appelle simplement lui-même? Comme James le dit cependant, avec une modification appropriée du type,T
cette astuce est la base de quelque chose qui fonctionne.Ici, vous avez les identifiants de tous les systèmes d'exploitation. Pour moi, cela ne sert à rien d'essayer de faire quelque chose qui dépend du système d'exploitation sans vérifier ce système. C'est comme acheter un pantalon vert mais sans les regarder.
la source
without resorting to trying to detect the platform
...Vous avez indiqué que l'utilisation de votre propre fonction wrapper est acceptable. Vous semblez également vouloir vivre avec des avertissements.
Donc, au lieu d'écrire votre wrapper en C ++, écrivez-le en C, où vous n'obtiendrez un avertissement que sur certains systèmes:
la source
Que diriez-vous
EDIT: bien sûr, le "sans détecter la plate-forme" est un peu un problème. Oups :-(
EDIT 2: ok, version améliorée, peut-être?
la source
const char**
cela échouera)Qu'en est-il de:
Je pense que cela viole l'aliasing strict en C ++ 03, mais pas en C ++ 11 car en C ++ 11
const char**
etchar**
sont des soi-disant «types similaires». Vous n'allez pas éviter cette violation de l'alias strict autrement qu'en créant unconst char*
, définissez-le égal à*foo
, appeleziconv
avec un pointeur vers le temporaire, puis recopiez le résultat*foo
après unconst_cast
:Ceci est à l'abri du POV de const-correctness, car il
iconv
neinbuf
s'agit que d'incrémenter le pointeur qui y est stocké. Donc, nous «rejetons const» à partir d'un pointeur dérivé d'un pointeur qui n'était pas const lorsque nous l'avons vu pour la première fois.Nous pourrions également écrire une surcharge de
myconv
etmyconv_helper
qui prennentconst char **inbuf
et gâchent les choses dans l'autre sens, de sorte que l'appelant ait le choix de passer en aconst char**
ou en achar**
. Ce quiiconv
aurait sans doute dû être donné à l'appelant en premier lieu en C ++, mais bien sûr, l'interface est simplement copiée à partir de C où il n'y a pas de surcharge de fonction.la source
Mise à jour: maintenant je vois qu'il est possible de le gérer en C ++ sans autotools, mais je laisse la solution autoconf aux personnes qui la recherchent.
Ce que vous recherchez est celui
iconv.m4
qui est installé par le package gettext.AFAICS c'est juste:
dans configure.ac, et il devrait détecter le bon prototype.
Ensuite, dans le code que vous utilisez:
la source
gettext
package. De plus, il est assez courant pour les packages d'inclure des macros utilisées dans lem4/
répertoire et d'avoir desACLOCAL_AMFLAGS = -I m4
fichiersMakefile.am
. Je pense qu'autopoint le copie même dans ce répertoire par défaut.Je suis en retard à cette soirée mais quand même, voici ma solution:
la source