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:
#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 };
};
Voici un exemple qui illustre le comportement:
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;
}
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 appelle iconv
avec un char const**
argument et une qui appelle iconv
avec un char**
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'appeler iconv
directement. Voici un schéma général montrant comment cela peut être écrit:
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 */);
}
(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.)