En mis máquinas Linux (y OS X), la iconv()
función tiene este prototipo:
size_t iconv (iconv_t, char **inbuf...
mientras que en FreeBSD se ve así:
size_t iconv (iconv_t, const char **inbuf...
Me gustaría que mi código C ++ se construyera en ambas plataformas. Con los compiladores de C, pasar a char**
para un const char**
parámetro (o viceversa) normalmente emite una simple advertencia; sin embargo, en C ++ es un error fatal. Entonces, si paso a char**
, no se compilará en BSD, y si paso a const char**
, no se compilará en Linux / OS X. ¿Cómo puedo escribir código que se compile en ambos, sin tener que intentar detectar la plataforma?
Una idea (fallida) que tuve fue proporcionar un prototipo local que anule cualquiera proporcionado por el encabezado:
void myfunc(void) {
size_t iconv (iconv_t, char **inbuf);
iconv(foo, ptr);
}
Esto falla porque iconv
necesita un enlace C y no puede poner extern "C"
dentro de una función (¿por qué no?)
La mejor idea de trabajo que se me ocurrió es lanzar el puntero de función en sí:
typedef void (*func_t)(iconv_t, const char **);
((func_t)(iconv))(foo, ptr);
pero esto tiene el potencial de enmascarar otros errores más graves.
fuente
iconv
requiereinbuf
que no sea constante.iconv
sinconst
: svnweb.freebsd.org/base/stable/9/include/…Respuestas:
Si lo que desea es hacer la vista gorda ante algunos problemas de constantes, puede utilizar una conversión que difumine la distinción, es decir, hace que char ** y const char ** sean interoperables:
Luego, más adelante en el programa:
sloppy () toma a
char**
o aconst char*
y lo convierte en achar**
o aconst char*
, lo que sea que exija el segundo parámetro de iconv.ACTUALIZACIÓN: cambiado para usar const_cast y llamar a descuidado no como cast.
fuente
sloppy<char**>()
inicializador directamente allí.(char**)&in
menos que primero hagas un typedef parachar**
.Puede eliminar la ambigüedad entre las dos declaraciones inspeccionando la firma de la función declarada. A continuación, se muestra un ejemplo básico de las plantillas necesarias para inspeccionar el tipo de parámetro. Esto podría generalizarse fácilmente (o podría usar los rasgos de función de Boost), pero esto es suficiente para demostrar una solución para su problema específico:
Aquí hay un ejemplo que demuestra el comportamiento:
Una vez que pueda detectar la calificación del tipo de parámetro, puede escribir dos funciones contenedoras que llaman
iconv
: una que llamaiconv
con unchar const**
argumento y otra que llamaiconv
con unchar**
argumento.Debido a que debe evitarse la especialización de la plantilla de función, usamos una plantilla de clase para realizar la especialización. Tenga en cuenta que también hacemos de cada uno de los invocadores una plantilla de función, para asegurarnos de que solo se instancia la especialización que usamos. Si el compilador intenta generar código para la especialización incorrecta, obtendrá errores.
Luego ajustamos el uso de estos con un
call_iconv
para que llamar a esto sea tan simple como llamariconv
directamente. El siguiente es un patrón general que muestra cómo se puede escribir esto:(Esta última lógica podría limpiarse y generalizarse; he intentado hacer que cada parte de ella sea explícita para, con suerte, dejar más claro cómo funciona).
fuente
decltype
requiere C ++ 11.#ifdef
verificación de la plataforma, terminas con 30 líneas impares de código :) Sin embargo, un buen enfoque (aunque me he estado preocupando en los últimos días mirando preguntas sobre SO que las personas que no realmente entiendo lo que están haciendo han comenzado a usar SFINAE como un martillo de oro ... no es su caso, pero me temo que el código se volverá más complejo y difícil de mantener ...)Puede utilizar lo siguiente:
Puede pasar
const char**
y en Linux / OSX pasará por la función de plantilla y en FreeBSD irá directamente aiconv
.Inconveniente: permitirá llamadas como las
iconv(foo, 2.5)
que pondrán al compilador en repetición infinita.fuente
const_cast
sería necesario moverlo a unadd_or_remove_const
dispositivo que profundice en elT**
para detectar si loT
estáconst
y agregar o eliminar la calificación según corresponda. Esto aún sería (mucho) más sencillo que la solución que he demostrado. Con un poco de trabajo, también podría ser posible hacer que esta solución funcione sin elconst_cast
(es decir, usando una variable local en suiconv
).iconv
no sea constante, no seT
deduce comoconst char**
, lo que significa que el parámetroinbuf
tiene tipoconst T
, que esconst char **const
, y la llamada aiconv
en la plantilla simplemente se llama a sí misma. Sin embargo, como dice James, con una modificación adecuada del tipo,T
este truco es la base de algo que funciona.Aquí tienes los identificadores de todos los sistemas operativos. Para mí no tiene ningún sentido intentar hacer algo que depende del sistema operativo sin verificar este sistema. Es como comprar unos pantalones verdes pero sin mirarlos.
fuente
without resorting to trying to detect the platform
...Ha indicado que utilizar su propia función de contenedor es aceptable. También parece estar dispuesto a vivir con las advertencias.
Entonces, en lugar de escribir su contenedor en C ++, escríbalo en C, donde solo recibirá una advertencia en algunos sistemas:
fuente
Qué tal si
EDITAR: por supuesto, el "sin detectar la plataforma" es un problema. Vaya :-(
EDIT 2: ok, versión mejorada, ¿quizás?
fuente
const char**
fallará)Qué pasa:
Creo que esto viola el alias estricto en C ++ 03, pero no en C ++ 11 porque en C ++ 11
const char**
ychar**
son los llamados "tipos similares". No va a evitar esa violación del aliasing estricto más que creando unconst char*
, configúrelo igual a*foo
, llameiconv
con un puntero al temporal y luego vuelva a copiar el resultado*foo
después de unconst_cast
:Esto está a salvo del punto de vista de la corrección constante, porque todo lo que
iconv
haceinbuf
es incrementar el puntero almacenado en él. Así que estamos "desechando const" de un puntero derivado de un puntero que no era constante cuando lo vimos por primera vez.También podríamos escribir una sobrecarga de
myconv
ymyconv_helper
que tomeconst char **inbuf
y altere las cosas en la otra dirección, de modo que la persona que llama tenga la opción de pasar aconst char**
o achar**
. Lo que podría decirse queiconv
debería haberle dado a la persona que llama en primer lugar en C ++, pero, por supuesto, la interfaz simplemente se copia de C donde no hay sobrecarga de funciones.fuente
Actualización: ahora veo que es posible manejarlo en C ++ sin autotools, pero dejo la solución de autoconf para las personas que la buscan.
Lo que está buscando es
iconv.m4
cuál está instalado por el paquete gettext.AFAICS es solo:
en configure.ac, y debería detectar el prototipo correcto.
Luego, en el código que usa:
fuente
gettext
paquete. Además, es bastante común que los paquetes incluyan macros usadas en elm4/
directorio y tenganACLOCAL_AMFLAGS = -I m4
archivosMakefile.am
. Creo que autopoint incluso lo copia en ese directorio de forma predeterminada.Llego tarde a esta fiesta pero aún así, aquí está mi solución:
fuente