2012-03-08 14 views
10

Sto tentando di includere le funzioni API di Windows per controllare gli errori quando lo scelgo. Come ho scoperto in una precedente domanda SO, potrei utilizzare una funzione modello per chiamare la funzione API e quindi chiamare GetLastError() per recuperare qualsiasi errore che potrebbe essere impostato. Potrei quindi passare questo errore alla mia classe Error per farmelo sapere.Verifica in fase di compilazione se Argomento modello non è valido

Ecco il codice per la funzione template:

template<typename TRet, typename... TArgs> 
TRet Wrap(TRet(WINAPI *api)(TArgs...), TArgs... args) 
{ 
    TRet ret = api(args...); 
    //check for errors 
    return ret; 
} 

Usando questo posso avere codice come segue

int WINAPI someFunc (int param1, BOOL param2); //body not accessible 

int main() 
{ 
    int ret = someFunc (5, true); //works normally 
    int ret2 = Wrap (someFunc, 5, true); //same as above, but I'll get a message if there's an error 
} 

Questo funziona meravigliosamente. Tuttavia, c'è un possibile problema. Prendere la funzione

void WINAPI someFunc(); 

Quando subbing questo nella funzione del modello, appare come segue:

void Wrap(void(WINAPI *api)()) 
{ 
    void ret = api(); //<-- ahem! Can't declare a variable of type void... 
    //check for errors 
    return ret; //<-- Can't return a value for void either 
} 

Per aggirare il problema, ho cercato di creare una versione del modello in cui ho sostituito TRet con void. Sfortunatamente, questo in realtà causa solo un'ambiguità di cui uno da usare.

A parte questo, ho provato ad utilizzare

if (strcmp (typeid (TRet).name(), "v") != 0) //typeid(void).name() == "v" 
{ 
    //do stuff with variable to return 
} 

else 
{ 
    //do stuff without returning anything 
} 

Tuttavia, typeid è un confronto di esecuzione, in modo che il codice ancora non compilare dovuta al tentativo di dichiarare una variabile vuoto, anche se non lo sarà mai.

Successivamente, ho provato a utilizzare std::is_same <TRet, void>::value anziché typeid, ma ho scoperto che si trattava anche di un confronto di runtime.

A questo punto, non so cosa provare dopo. C'è qualche possibilità di far credere al compilatore che so che cosa sto facendo funzionerà? Non mi interessa allegare un argomento in più al numero Wrap, ma non sono riuscito a ottenere nulla da questo.

Uso Code :: Blocks con GNU G ++ 4.6.1 e Windows XP, oltre a Windows 7. Grazie per qualsiasi aiuto, anche se mi sta dicendo che dovrò finire semplicemente non usando Wrap per le funzioni che restituiscono nulla.

+1

Comprendo perché la specializzazione del tipo restituito come void non ha funzionato (non si può disambiguare sui tipi restituiti) ma la specializzazione con void ** e ** l'aggiunta di un parametro aggiuntivo dovrebbe funzionare, cosa è successo? Potrebbe essere necessario invocare con tipi espliciti. – Tod

+1

Interessante domanda. Nota a margine: oltre a non essere controllata staticamente, anche la soluzione 'typeid' non è portabile. Lo standard non garantisce la stringa restituita da 'type_info :: name()'. – Thomas

+0

Non ho pensato di combinare i due. Ci proverò. Grazie anche per l'heads-up sul problema dei typeid. – chris

risposta

8

È possibile utilizzare una classe di supporto per specializzazioni sintonizzare bene:

template <typename F> 
struct wrapper 
{}; 

template <typename Res, typename... Args> 
struct wrapper<Res(Args...)> 
{ 
    static Res wrap(Res (WINAPI *f)(Args...), Args&& args...) 
    { 
     Res r = f(std::forward<Args>(args)...); 
     // Blah blah 
     return r; 
    } 
}; 

template <typename... Args> 
struct wrapper<void(Args...)> 
{ 
    static void wrap(void (WINAPI *f)(Args...), Args&& args...) 
    { 
     f(std::forward<Args>(args)...); 
     // Blah blah 
    } 
}; 

Ora, è possibile scrivere il wrapper:

template <typename Res, typename... Args> 
Res Wrap(Res (WINAPI *f)(Args...), Args&& args...) 
{ 
    return wrapper<Res(Args...)>::wrap(f, std::forward<Args>(args)...); 
} 

Nota che funziona anche quando è Resvoid. Hai il permesso di return un'espressione che restituisce il vuoto in una funzione che restituisce il nulla.

Il tipo corretto viene dedotto, come in Wrap(someFunc, 5, true), anche per le funzioni che restituiscono nulla.

+0

Buono a sapersi, grazie. Elimina la necessità di un tipo in <> (che avrei appena svalutato e ho detto '#define vwrap wrap ' o qualcosa del genere). Vedrò se funziona pure. Per qualsiasi motivo 'std :: forward' e' Args && args ... 'non mi piace lavorare, ma il winapi è in C, quindi apparentemente (domanda precedente) forward non fornisce alcun beneficio comunque. – chris

+0

Non importa, avanti ha deciso di lavorare di nuovo. Potrebbe essere una discrepanza tra il compilatore che uso a casa e quello che uso sul mio flash drive a scuola. – chris

+0

Sì, funziona perfettamente e mantiene la stessa sintassi. Una modifica che dovevo fare era cambiare 'Args && args ...' in 'Args && ... args ...'. Grazie. – chris

1

Promuovere dal commento alla risposta Comprendo perché la specializzazione del tipo restituito come void non ha funzionato (non posso disambiguare sui tipi restituiti) ma la specializzazione con void e l'aggiunta di un parametro aggiuntivo dovrebbero funzionare, cosa è successo? Potrebbe essere necessario invocare con tipi espliciti.

+0

Mi scuso per aver cambiato la risposta accettata, perché ha funzionato bene, ma la risposta di Alexandre C. mantiene la * esatta * stessa sintassi che usavo prima senza l'uso di alcuna affermazione #define o cose del genere. Il trade off è solo un po 'di codice extra; nulla in confronto all'intero involucro. – chris

+0

Nessun problema, penso che la risposta accettata dovrebbe essere quella che si vuole che qualcuno in una situazione simile veda quando va in cerca. Inoltre il fatto che la risposta di Alexandre deduca automaticamente è un bug in più. Ottenere il upvote è bello. – Tod

2

Per ovviare a ciò, ho provato a creare una versione del modello in cui ho sostituito TRet con void. Sfortunatamente, questo in realtà causa solo un'ambiguità di cui uno da usare.

Che dovrebbe lavoro, credo, perché void è più specializzata rispetto TRet, ma come fai notare non è così. Potrei mancare qualcosa, ma in ogni caso, non importa, puoi evitare che il sovraccarico di TRet venga selezionato.

template<typename TFun, typename... TArgs> 
auto Wrap(TFun api, TArgs&&... args) -> 
typename std::enable_if< 
    !std::is_void<typename std::result_of<TFun(TArgs...)>::type>::value, 
    typename std::result_of<TFun(TArgs...)>::type 
>::type 
{ 
    auto result = api(std::forward<TArgs&&>(args)...); 
    return result; 
} 

template<typename TFun, typename... TArgs> 
auto Wrap(TFun api, TArgs&&... args) -> 
typename std::enable_if< 
    std::is_void<typename std::result_of<TFun(TArgs...)>::type>::value, 
    typename std::result_of<TFun(TArgs...)>::type 
>::type 
{ 
    api(std::forward<TArgs&&>(args)...); 
} 

void WINAPI f1() 
{ 
} 

void WINAPI f2(double) 
{ 
} 

int WINAPI f3() 
{ 
    return 0; 
} 

int WINAPI f4(double) 
{ 
    return 0; 
} 

int main() { 
    Wrap(f1); 
    Wrap(f2, 0); 
    return Wrap(f3) * Wrap(f4, 0); 
} 

Aggiornamento: rettificato per consentire le conversioni da tipo di argomento per tipo di parametro.

+0

Questo codice funziona correttamente. Tuttavia, quando ho provato ad abbinare il mio altro codice, ho scoperto che l'ambiguità si verifica quando la funzione ha un numero qualsiasi di parametri. Quando non ha parametri, l'ambiguità viene rimossa. – chris

+0

@chris Ah, giusto, allora inizia anche a mancare per me. Tuttavia, è facilmente risolvibile rimuovendo il sovraccarico che non dovrebbe essere usato, verrà modificato. – hvd

+0

Questo è un lavoro di fantasia lì: p Non ho davvero fatto molto con modelli diversi da alcune funzioni matematiche di base, quindi sono un po 'inutile con tutta la sintassi relativa a quelli più recenti.Funziona bene anche per me, e ora non so di chi sia la risposta da usare:/ – chris

Problemi correlati