2015-01-30 3 views
6

Diese Frage ist vergleichbar mit: "Extract just the argument type list from decltype(someFunction)". Ich bin mir nicht sicher, ob die Antworten dort für das funktionieren, was ich beabsichtige. Ich möchte in der Lage sein, eine Vorlagefunktion zu erstellen, die den Typ seiner Laufzeitargumente basierend auf dem Typ eines Funktionszeigervorlagenarguments ableitet (pfeift).Bietet C++ 11, 14 oder 17 eine Möglichkeit, nur die Argumente aus einem decltype() zu bekommen?

Als Beispiel für einen Anwendungsfall möchte ich sagen, dass ich gerade C POSIX-Datei-E/A mit einer mit LD_PRELOAD geladenen Shim-Bibliothek instrumentieren möchte. Ich könnte separate Wrapper für fopen, fread, fwrite, fclose schreiben ... Wenn all diese Wrapper ähnliche Dinge tun, wäre es nicht schön, wenn ich eine Vorlage definieren könnte, die das übliche Verhalten erfasst?

Teil Beispiel Vorlagen nicht verwenden, wie viel vorformulierten demonstriert beteiligt: ​​

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 ... 

} 

Wir einige Code speichern können eine variadische Template-Funktion:

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); 
} 

Es muss einen Weg geben, wir vermeiden können Übergeben Sie jedoch alle diese redundanten Typen in der Liste der Vorlagenparameter. Was möchte ich tun, dass ich nicht gültige Syntax für noch gefunden haben (setzt die Existenz von etwas werde ich std :: nennen arguments_of dass Art ist wie das Gegenteil von 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); 
} 

Gibt es einen gültigen Weg, dies in C++ 11, 14 oder 17 zu tun? Wie oder wenn nicht, warum nicht?

+0

mehr andere Probleme mit Ihrem Design Es gibt keine. Ein String-Literal kann nicht als Template-Argument verwendet werden, noch ein nicht-konstanter Ausdruck wie 'real_fclose'. Und Sie können einem Vorlagenparameter nicht zuweisen. –

+0

Ich konnte sehen, wo es idiomatisch wäre, real_func_ptr als Parameter zu haben, um die Funktion wrap_func anstelle der Vorlage zu haben. Es scheint so zu funktionieren, wie ich es mit Intel C++ 15 und -std = C++ 11 habe. Lässt ICPC 15 mich hier einfach die Regeln umgehen? –

Antwort

9

Sie verwenden Teil Spezialisierung:

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) { /* ... */ } 
}; 

Die Falte ist, dass, weil Sie nicht teilweise Funktionen spezialisieren können Sie eine Klassenvorlage verwenden müssen - hier Wrapper.

1

in Ihrer Vorlage Lösung können Args,

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

ist genug abgeleitet werden.

3

Sie können die Funktionszeiger verwenden, um die Rückkehr und Argumenttypen ableiten:

#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"); 
} 
Verwandte Themen