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:
#include <iostream>
#include <stddef.h>
#include <type_traits>
// I've declared this just so the example is portable:
struct iconv_t { };
// use_const<decltype(&iconv)>::value will be 'true' if the function is
// declared as taking a char const**, otherwise ::value will be false.
template <typename>
struct use_const;
template <>
struct use_const<size_t(*)(iconv_t, char**, size_t*, char**, size_t*)>
{
enum { value = false };
};
template <>
struct use_const<size_t(*)(iconv_t, char const**, size_t*, char**, size_t*)>
{
enum { value = true };
};
Aqui está um exemplo que demonstra o comportamento:
size_t iconv(iconv_t, char**, size_t*, char**, size_t*);
size_t iconv_const(iconv_t, char const**, size_t*, char**, size_t*);
int main()
{
using std::cout;
using std::endl;
cout << "iconv: " << use_const<decltype(&iconv) >::value << endl;
cout << "iconv_const: " << use_const<decltype(&iconv_const)>::value << endl;
}
Depois de detectar a qualificação do tipo de parâmetro, você pode escrever duas funções de wrapper que chamam iconv
: uma que chama iconv
com um char const**
argumento e outra que chama iconv
com um char**
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 chamar iconv
diretamente. A seguir está um padrão geral que mostra como isso pode ser escrito:
template <bool UseConst>
struct iconv_invoker
{
template <typename T>
static size_t invoke(T const&, /* arguments */) { /* etc. */ }
};
template <>
struct iconv_invoker<true>
{
template <typename T>
static size_t invoke(T const&, /* arguments */) { /* etc. */ }
};
size_t call_iconv(/* arguments */)
{
return iconv_invoker<
use_const<decltype(&iconv)>::value
>::invoke(&iconv, /* arguments */);
}
(Esta última lógica poderia ser limpa e generalizada; tentei tornar cada parte dela explícita para, espero, deixar mais claro como funciona.)