Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

This question is very similar to: "Extract just the argument type list from decltype(someFunction)". I'm not sure the answers there work for what I'm intending though. I'd like to be able to create a template function that deduces the type of its runtime arguments based on the type of a function pointer template argument (whistles).

For an example use case, let's say I want to instrument straight C POSIX file I/O using a shim library loaded with LD_PRELOAD. I could write separate wrappers for fopen, fread, fwrite, fclose... If all of those wrappers do similar stuff though, wouldn't it be nice if I could define a template that captures the common behavior?

Partial example NOT using templates that demonstrates how much boilerplate is involved:

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

}

We can save some code using a variadic template function:

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

There's gotta be some way we can avoid passing all of those redundant types in the list of template parameters though. What I would like to do that I haven't found valid syntax for yet (presumes the existence of something I'll call std::arguments_of that's sort of like the opposite 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);
}

Is there a valid way to do this in C++11, 14 or 17? How, or if not, why not?

like image 591
Aaron Altman Avatar asked Jan 30 '15 16:01

Aaron Altman


1 Answers

You use partial specialization:

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

The wrinkle is that because you can't partially specialize functions you need to use a class template - here Wrapper.

like image 85
ecatmur Avatar answered Sep 18 '22 05:09

ecatmur