È possibile chiarire le ambiguità tra le due dichiarazioni controllando la firma della funzione dichiarata. Ecco un esempio di base dei modelli richiesti per ispezionare il tipo di parametro. Questo potrebbe essere facilmente generalizzato (o potresti usare i tratti della funzione di Boost), ma questo è sufficiente per dimostrare una soluzione per il tuo problema specifico:
#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 };
};
Ecco un esempio che dimostra il 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;
}
Una volta rilevata la qualificazione del tipo di parametro, è possibile scrivere due funzioni wrapper che chiamano iconv
: una che chiama iconv
con un char const**
argomento e una che chiama iconv
con un char**
argomento.
Poiché la specializzazione del modello di funzione dovrebbe essere evitata, utilizziamo un modello di classe per eseguire la specializzazione. Nota che rendiamo anche ogni invokers un modello di funzione, per assicurarci che venga istanziata solo la specializzazione che usiamo. Se il compilatore tenta di generare codice per la specializzazione sbagliata, verranno visualizzati degli errori.
Quindi includiamo l'uso di questi con un call_iconv
per rendere la chiamata semplice come chiamare iconv
direttamente. Quello che segue è un modello generale che mostra come questo può essere scritto:
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 */);
}
(Quest'ultima logica potrebbe essere ripulita e generalizzata; Ho cercato di rendere esplicita ogni parte di essa per rendere, si spera, più chiaro come funziona.)