Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic function pointer conversion

I am writing a library with many function objects whose classes have several operator() overloads that do not depend on the state of the classes and do not alter it. Now, I tried to make my code work with many old-style APIs (it is not a random need, I actually had to deal with such APIs) and therefore decided to make the function objects convertible to any function pointer corresponding to one of the overloads. At some point, I realized that I had too many such conversions to function pointer operators and that I should theorically be able to write a single variadic conversion operator. Here is a class implementing such a variadic operator:

struct foobar
{
    template<typename... Args>
    using fptr_t = void(*)(Args... args);

    template<typename... Args>
    operator fptr_t<Args...>() const
    {
        return [](Args... args) {
            // Whatever
        };
    }
};

As you can see, I used the lambda conversion to function pointer to implement the conversion operator, which is not a problem since every function object I have is stateless. The goal was to be able to use the class as follows:

int main()
{
    void(*foo)(int) = foobar();
    void(*bar)(float, double) = foobar();
}

g++ has no problem compiling this code with the expected semantics. However, clang++ rejects it with a template substitution failure error:

main.cpp:21:11: error: no viable conversion from 'foobar' to 'void (*)(int)'
    void(*foo)(int) = foobar();
          ^           ~~~~~~~~
main.cpp:11:5: note: candidate function [with Args = int]
    operator fptr_t<Args...>() const
    ^
1 error generated.

Note that clang++ has no problem with such conversion operators as long as no variadic templates are involved. If I use a single template parameter, it will have no problem compiling the code. Now, should the code above be accepted or rejected by the compiler?

like image 849
Morwenn Avatar asked Dec 11 '15 23:12

Morwenn


People also ask

How do you use variadic arguments?

It takes one fixed argument and then any number of arguments can be passed. The variadic function consists of at least one fixed variable and then an ellipsis(…) as the last parameter. This enables access to variadic function arguments. *argN* is the last fixed argument in the variadic function.

Is printf Variadic function?

Variadic functions are functions (e.g. printf) which take a variable number of arguments. The declaration of a variadic function uses an ellipsis as the last parameter, e.g. int printf(const char* format, ...);.

What is variadic template in C++?

Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration.

What is the use of Variadic templates?

With the variadic templates feature, you can define class or function templates that have any number (including zero) of parameters. To achieve this goal, this feature introduces a kind of parameter called parameter pack to represent a list of zero or more parameters for templates.


1 Answers

A lambda can only be converted to a function pointer if it does not capture, so your code should work. This is justified in the standard 5.1.2/p6 Lambda expressions [expr.prim.lambda] (Emphasis Mine):

The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function with C++ language linkage (7.5) having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

So I would file it as a CLANG bug.

As a work around for CLANG, you can convert it to a std::function as shown below:

struct foobar
{
    template<typename... Args>
    using fptr_t = void(*)(Args... args);

    template<typename... Args>
    operator std::function<void(Args...)>() const
    {
        return [](Args... args) {
            //...
        };
    }
};

int main()
{
    std::function<void(int)> f1 = foobar();
    std::function<void(double, float)> f2 = foobar();
    f1(1);
    f2(2.0, 1.0f);
}

Live Demo

like image 148
101010 Avatar answered Sep 18 '22 17:09

101010