2015-01-30 10 views
6

Questa domanda è molto simile a "Extract just the argument type list from decltype(someFunction)". Non sono sicuro che le risposte funzionino per quello che intendo. Mi piacerebbe essere in grado di creare una funzione modello che deduca il tipo dei suoi argomenti di runtime in base al tipo di un argomento modello di puntatore a funzione (fischietti).C++ 11, 14 o 17 fornisce un modo per ottenere solo gli argomenti da un decltype()?

Per un esempio di utilizzo, diciamo che voglio strumentare direttamente l'I/O del file POSIX C utilizzando una libreria shim caricata con LD_PRELOAD. Potrei scrivere wrapper separati per fopen, fread, fwrite, fclose ... Se tutti questi wrapper fanno cose simili, non sarebbe bello se potessi definire un template che cattura il comportamento comune?

esempio parziale non utilizzando i modelli che dimostra quanto boilerplate è coinvolto:

extern "C" { 

FILE *(*real_fopen)(const char *, const char *) = NULL; 
FILE *fopen(const char *path, const char *mode) 
{ 
    FILE *returned_file; 

    if (real_fopen == NULL) { 
     real_fopen = ((FILE *)(const char *, const char *))dlsym("fopen", RTLD_NEXT); 
    } 

    ... do pre-call instrumentation ... 
    returned_file = real_fopen(path, mode); 
    ... do post-call instrumentation ... 

    return returned_file; 
} 

int (*real_fclose)(FILE *) = NULL; 
int fclose(FILE *fp) 
{ 
    int retval; 

    if (real_fclose == NULL) { 
     real_fclose = ((int)(FILE *))dlsym("fclose", RTLD_NEXT); 
    } 

    ... do pre-call instrumentation ... 
    retval = real_fclose(path, mode); 
    ... do post-call instrumentation ... 

    return retval; 
} 

... additional definitions following the same general idea ... 

} 

possiamo risparmiare un po 'di codice utilizzando una funzione template variadic:

template <typename func_ptr_type, func_ptr_type real_func_ptr, 
    const char *dl_name, typename... Args> 
std::result_of<func_type> wrap_func(Args... args) 
{ 
    std::result_of<func_type> retval; 
    if (real_func_ptr == NULL) { 
     real_func_ptr = (func_ptr_type)dlsym(dl_name, RTLD_NEXT); 
    } 

    ... do pre-call instrumentation ... 
    retval = real_func_ptr(args...); 
    ... do post-call instrumentation ... 

    return retval; 
} 

FILE *(*real_fopen)(const char *, const char *) = NULL; 
FILE *fopen(const char *path, const char *mode) 
{ 
    return wrap_func<decltype(real_fopen), real_fopen, "fopen", const char *, const char *>(path, mode); 
} 

int (*real_fclose)(FILE *) = NULL; 
int fclose(FILE *fp) 
{ 
    return wrap_func<decltype(real_fclose), real_fclose, "fclose", FILE *>(fp); 
} 

Ci deve essere qualche modo possiamo evitare passando comunque tutti quei tipi ridondanti nell'elenco dei parametri del template. Quello che mi piacerebbe fare che non ho trovato la sintassi valida per ancora (presuppone l'esistenza di qualcosa che chiamerò std :: arguments_of che è un po 'come l'opposto di std :: result_of):

template <typename func_ptr_type, func_ptr_type real_func_ptr, 
    const char *dl_name, std::arguments_of(func_ptr_type)> 
std::result_of<func_type> wrap_func(std::arguments_of(func_ptr_type)... args) 
{ 
    std::result_of<func_type> retval; 
    if (real_func_ptr == NULL) { 
     real_func_ptr = (func_ptr_type)dlsym(dl_name, RTLD_NEXT); 
    } 

    ... do pre-call instrumentation ... 
    retval = real_func_ptr(args...); 
    ... do post-call instrumentation ... 

    return retval; 
} 

FILE *(*real_fopen)(const char *, const char *) = NULL; 
FILE *fopen(const char *path, const char *mode) 
{ 
    return wrap_func<decltype(real_fopen), real_fopen, "fopen">(path, mode); 
} 

int (*real_fclose)(FILE *) = NULL; 
int fclose(FILE *fp) 
{ 
    return wrap_func<decltype(real_fclose), real_fclose, "fclose">(fp); 
} 

Esiste un modo valido per farlo in C++ 11, 14 o 17? Come, o se no, perché no?

+0

Ci sono diversi altri problemi con il vostro disegno. Una stringa letterale non può essere utilizzata come argomento modello, né un'espressione non costante come 'real_fclose'. E non puoi assegnare ad un parametro template. –

+0

Ho potuto vedere dove sarebbe più idiota avere real_func_ptr come parametro per wrap_func della funzione piuttosto che del modello. Sembra funzionare come con Intel C++ 15 e -std = C++ 11. ICPC 15 mi lascia semplicemente sfuggire le regole qui? –

risposta

9

si utilizza la specializzazione parziale:

template <typename func_ptr_type, func_ptr_type real_func_ptr, 
    const char *dl_name> 
struct Wrapper; 

template <typename Ret, typename... Args, Ret (*real_func_ptr)(Args...), 
    const char *dl_name> 
struct Wrapper<Ret(*)(Args...), real_func_ptr, dl_name> 
{ 
    static Ret func(Args... args) { /* ... */ } 
}; 

La ruga è che perché non si può parzialmente specializzarsi funzioni è necessario utilizzare un modello di classe - qui Wrapper.

1

nella soluzione modello, Args si può dedurre, quindi

return wrap_func<decltype(real_fopen), real_fopen, "fopen">(path, mode); 

è sufficiente.

3

si potrebbe utilizzare il puntatore a funzione di dedurre i tipi restituiti e di argomenti:

#include <iostream> 

FILE *fake_fopen(const char *path, const char *mode) 
{ 
    std::cout << "Library Function: " << path << " " << mode << "\n"; 
    return nullptr; 
} 

template <typename Result, typename... Arguments> 
Result dl_wrap(
    const char* dl_name, 
    Result (*&function)(Arguments...), // Reference to function pointer 
    Arguments... arguments) 
{ 
    function = (Result (*)(Arguments...))fake_fopen; //dlsym 
    return function(arguments ...); 
} 

FILE *(*real_fopen)(const char *, const char *) = nullptr; 
FILE *fopen(const char *path, const char *mode) 
{ 
    return dl_wrap("fopen", real_fopen, path, mode); 
} 


int main(int argc, char* argv[]) 
{ 
    fopen("Hello", "World"); 
}