Vous pouvez faire la distinction entre les deux déclarations en inspectant la signature de la fonction déclarée. Voici un exemple de base des modèles nécessaires pour inspecter le type de paramètre. Cela pourrait facilement être généralisé (ou vous pourriez utiliser les traits de fonction de Boost), mais cela est suffisant 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 démontre ce 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 enveloppes qui appellent iconv
: celui qui appelle iconv
avec un char const**
et un autre qui appelle iconv
avec un char**
argument.
Comme la spécialisation des modèles 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 invokers un template 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 ces éléments avec un call_iconv
pour rendre cet appel aussi simple que d'appeler iconv
directement. Ce qui suit est un modèle 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é d'en rendre chaque élément explicite pour espérer que son fonctionnement soit plus clair).