C++11、14 或 17 是否提供了一种从 decltype() 中获取参数的方法?

Does C++11, 14 or 17 provide a way to get just the arguments out of a decltype()?

这个问题与“Extract just the argument type list from decltype(someFunction)”非常相似。我不确定那里的答案是否适合我的意图。我希望能够创建一个模板函数,该函数根据函数指针模板参数的类型推导其运行时参数的类型 (whistles).

对于示例用例,假设我想使用加载了 LD_PRELOAD 的垫片库直接检测 C POSIX 文件 I/O。我可以为 fopen、fread、fwrite、fclose 编写单独的包装器...如果所有这些包装器都做类似的事情,如果我可以定义一个捕获常见行为的模板不是很好吗?

不使用模板的部分示例演示了涉及多少样板文件:

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

}

我们可以使用可变参数模板函数保存一些代码:

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

我们必须有一些方法可以避免在模板参数列表中传递所有这些冗余类型。我想做的是我还没有找到有效的语法(假设存在我称之为 std::arguments_of 的东西,有点像 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);
}

是否有在 C++11、14 或 17 中执行此操作的有效方法?如何,或者如果不是,为什么不呢?

您使用部分专业化:

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

问题在于,因为您不能部分特化函数,所以您需要使用 class 模板 - 这里 Wrapper.

在你的模板解中,可以推导出Args,所以

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

够了。

您可以使用函数指针来推断 return 和参数类型:

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