Em minhas máquinas Linux (e OS X), a iconv()
função tem este protótipo:
size_t iconv (iconv_t, char **inbuf...
enquanto no FreeBSD é assim:
size_t iconv (iconv_t, const char **inbuf...
Eu gostaria que meu código C ++ fosse construído em ambas as plataformas. Com compiladores C, passar um char**
para um const char**
parâmetro (ou vice-versa) normalmente emite um mero aviso; entretanto, em C ++, é um erro fatal. Então, se eu passar um char**
, ele não compilará no BSD, e se eu passar um, const char**
ele não compilará no Linux / OS X. Como posso escrever código que compila em ambos, sem recorrer a tentar detectar a plataforma?
Uma ideia (falhada) que tive foi fornecer um protótipo local que substitui qualquer fornecido pelo cabeçalho:
void myfunc(void) {
size_t iconv (iconv_t, char **inbuf);
iconv(foo, ptr);
}
Isso falha porque iconv
precisa de ligação C e você não pode colocar extern "C"
dentro de uma função (por que não?)
A melhor ideia de trabalho que tive é lançar o próprio ponteiro de função:
typedef void (*func_t)(iconv_t, const char **);
((func_t)(iconv))(foo, ptr);
mas isso tem o potencial de mascarar outros erros mais sérios.
fonte
iconv
requer que oinbuf
seja não constante.iconv
sem oconst
: svnweb.freebsd.org/base/stable/9/include/…Respostas:
Se o que você quer é apenas fechar os olhos para alguns problemas const, você pode usar uma conversão que confunde a distinção, ou seja, torna char ** e const char ** interoperáveis:
Em seguida, mais tarde no programa:
sloppy () pega a
char**
ou aconst char*
e converte para achar**
ou aconst char*
, qualquer que seja o segundo parâmetro de iconv exigir.ATUALIZAÇÃO: alterado para usar const_cast e chamar sloppy not a como cast.
fonte
sloppy<char**>()
inicializador diretamente aqui.(char**)&in
menos que você primeiro fizesse um typedef parachar**
.Você pode eliminar a ambigüidade entre as duas declarações inspecionando a assinatura da função declarada. Aqui está um exemplo básico dos modelos necessários para inspecionar o tipo de parâmetro. Isso poderia ser facilmente generalizado (ou você poderia usar os traços da função de Boost), mas isso é suficiente para demonstrar uma solução para seu problema específico:
Aqui está um exemplo que demonstra o comportamento:
Depois de detectar a qualificação do tipo de parâmetro, você pode escrever duas funções de wrapper que chamam
iconv
: uma que chamaiconv
com umchar const**
argumento e outra que chamaiconv
com umchar**
argumento.Como a especialização do template de função deve ser evitada, usamos um template de classe para fazer a especialização. Observe que também tornamos cada um dos invocadores um modelo de função, para garantir que apenas a especialização que usamos seja instanciada. Se o compilador tentar gerar código para a especialização errada, você obterá erros.
Em seguida, envolvemos o uso deles com um
call_iconv
para tornar a chamada tão simples quanto chamariconv
diretamente. A seguir está um padrão geral que mostra como isso pode ser escrito:(Esta última lógica poderia ser limpa e generalizada; tentei tornar cada parte dela explícita para, espero, deixar mais claro como funciona.)
fonte
decltype
requer C ++ 11.#ifdef
verificação para a plataforma você acaba com 30 linhas de código estranhas :) Boa abordagem, embora (embora eu tenha me preocupado nos últimos dias olhando para perguntas sobre SO que as pessoas que não realmente entendo o que eles estão fazendo, comecei a usar SFINAE como um martelo dourado ... não é o seu caso, mas temo que o código se tornará mais complexo e difícil de manter ...)Você pode usar o seguinte:
Você pode passar
const char**
e no Linux / OSX irá passar pela função de template e no FreeBSD irá diretamente paraiconv
.Desvantagem: permitirá chamadas como as
iconv(foo, 2.5)
que colocarão o compilador em recorrência infinita.fonte
const_cast
precisaria ser movido para umadd_or_remove_const
que analisa oT**
para detectar seT
éconst
e adicionar ou remover a qualificação conforme apropriado. Isso ainda seria (muito) mais simples do que a solução que demonstrei. Com um pouco de trabalho, também pode ser possível fazer essa solução funcionar sem oconst_cast
(ou seja, usando uma variável local no seuiconv
).iconv
é não const, não éT
deduzido comoconst char**
, o que significa que o parâmetroinbuf
tem tipoconst T
, que éconst char **const
, e a chamada deiconv
no modelo apenas chama a si mesma? Porém, como James disse, com uma modificação adequada no tipo,T
esse truque é a base de algo que funciona.Aqui você tem ids de todos os sistemas operacionais. Para mim não adianta tentar fazer algo que depende do sistema operacional sem verificar este sistema. É como comprar calças verdes mas sem olhar para elas.
fonte
without resorting to trying to detect the platform
...Você indicou que usar sua própria função de wrapper é aceitável. Você também parece estar disposto a conviver com os avisos.
Portanto, em vez de escrever seu wrapper em C ++, escreva-o em C, onde você receberá apenas um aviso em alguns sistemas:
fonte
E se
EDIT: claro, o "sem detectar a plataforma" é um pouco problemático. Ops :-(
EDIT 2: ok, versão melhorada, talvez?
fonte
const char**
A respeito:
Eu acho que isso viola o aliasing estrito em C ++ 03, mas não em C ++ 11 porque em C ++ 11
const char**
echar**
são chamados de "tipos semelhantes". Você não vai evitar essa violação de aliasing estrito, exceto criando umconst char*
, defina-o igual a*foo
, chameiconv
com um ponteiro para o temporário e copie o resultado de volta para*foo
depois deconst_cast
:Isso está protegido do POV de const-correção, porque tudo o que
iconv
fazinbuf
é incrementar o ponteiro armazenado nele. Portanto, estamos "descartando const" de um ponteiro derivado de um ponteiro que não era const quando o vimos pela primeira vez.Também poderíamos escrever uma sobrecarga de
myconv
emyconv_helper
que levaconst char **inbuf
e bagunça as coisas na outra direção, de modo que o chamador tenha a opção de passar por aconst char**
ou achar**
. Que indiscutivelmenteiconv
deveria ter dado ao chamador em primeiro lugar em C ++, mas é claro que a interface é apenas copiada de C onde não há sobrecarga de função.fonte
Atualização: agora vejo que é possível lidar com isso em C ++ sem autotools, mas estou deixando a solução autoconf para quem procura por ela.
O que você está procurando é o
iconv.m4
que é instalado pelo pacote gettext.AFAICS é apenas:
em configure.ac, e deve detectar o protótipo correto.
Então, no código que você usa:
fonte
gettext
pacote. Além disso, é bastante comum que os pacotes incluam macros usadas nom4/
diretório e tenhamACLOCAL_AMFLAGS = -I m4
emMakefile.am
. Acho que o autopoint até copia para esse diretório por padrão.Estou atrasado para esta festa, mas mesmo assim, aqui está a minha solução:
fonte