Workaround for "use of variable before deduction of auto"

122 views Asked by At

I am creating wrapper functions, that modify existing functions (targets). The targets are determined at runtime as pointers without type information (void*). Each of them has different parameters and return type. The wrapped functions are called from external code, so they have to match the signature of the target.

The basic scheme is like this:

bool wrap(void* f, void** t)
{
    *t = magic();  // target determined at runtime as void*
    return true;
}

// define new wrapping function and storage for target
int(*foo_target)(int);

int foo(int data)
{
    return foo_target(data) + 10;
}

auto res = wrap(reinterpret_cast<void*>(&foo), reinterpret_cast<void**>(&foo_target));

The wrap function is a placeholder, that does all the heavy lifting. For each target I define a wrapper function (foo) and a variable to store the function pointer to the target function with the correct type (foo_target).

This works, but requires ugly type casts and I have to keep the signatures of foo_target and foo in sync manually. What I would like to have is something like this:

template <class F>
struct wrap
{
    F* target;

    wrap(F&& f)
    {
        target = reinterpret_cast<F*>(magic());
    }
};

static auto foo = wrap(
    [](int data) -> int   // <- signature of the wrapper and target function
    {
        return foo.target(data) + 10;     // ERROR
        // return 10;                     // WORKS
    });
}

The single type cast required is hidden in the wrap function, and the signature of the target function is in a single place. Of course, this does not work because foo cannot be used in the lambda like that.

If I use a common base class, I can make this solution work. However, in this case I need a type cast in every wrapper function and have to define the function signature twice.

struct wrap_base
{
    void* target;
};

template <class F>
struct wrap : wrap_base
{
    wrap(F&& f)
    {
        target = magic();
    }
};


static wrap_base foo = wrap(
    [](int data) -> int        // <- signature of the wrapper and target function
    {
        using func = int(int); // <- repeated here
        return reinterpret_cast<func*>(foo.target)(data) + 10;
    });

Edit The target functions may have different calling conventions. This probably means that I cannot use (non-capturing) lambdas, although I have not had any issues so far.

1

There are 1 answers

3
Jarod42 On

It seems you want a recursive-lambda. One way is a Y-combinator, i.e adding Self parameter:

template <class F>
struct wrap;

template <class Ret, typename... Args>
struct wrap<Ret(Args...)>
{
    Ret(*target)(Args...);
    std::function<Ret(wrap&, Args...)> func;

    template <typename F>
    requires(requires(F f, wrap self, Args... args) {
        f(self, args...);
    })
    wrap(F f) : func(f)
    {
        target = reinterpret_cast<Ret(*)(Args...)>(magic());
    }

    Ret operator()(Args... args) {
        return func(*this, args...);
    }
};

static auto foo = wrap<int(int)>(
    [](auto& self, int data) -> int
    {
        return self.target(data) + 10;
    });
};

Demo