简体   繁体   中英

C++17 decorate lambda or member function with non-capturing lambda

I'm trying to craft a function that returns a non-capturing lambda (so it can be converted to a function pointer) that decorates an inner function, where the inner function can be either a lambda or a member function pointer.

Compiler Explorer link for reference, which I'll dissect in the following.

I've come up with two strategies, one for lambdas and the other for member functions. Specifically, for lambdas

template <typename Callable>
auto decorate_lambda(Callable&& lambda) {
    static const Callable callable = std::forward<Callable>(lambda);

    return [](auto... args) {
        return 100 + callable(std::forward<decltype(args)>(args)...);
    };
}

Saving off the lambda as a static allows callable to be used in a non-capturing lambda. This is fine because the decorate_lambda template instantiation will be unique for each lambda (IIRC).

For member functions the strategy is somewhat different (note the template parameter)

template <auto callable>
auto decorate_memfn() {
    return [](auto... args) {
        return 100 + std::mem_fn(callable)(std::forward<decltype(args)>(args)...);
    };
}

I can then do something like

const auto decorated_lambda =
    decorate_lambda([](int i) { return i + 1; });

const auto decorated_memfn =
    decorate_memfn<&Worker::work>();

int (*lambda_fnptr)(int) = decorated_lambda;
int (*memfn_fnptr)(Worker&, int) = decorated_memfn;

resulting in function pointers that can be used eg in a C interface (ultimately).

The end result that I would like is to roll decorate_lambda and decorate_memfn into a single decorate(lambda_or_memfn) function (using template specialisation, if constexpr , or whatever). Eg

decorate([](int i) { return i + 1; });
decorate(&Worker::work);

Ie essentially I would like to have decorate_memfn(&Worker::work) rather than decorate_memfn<&Worker::work>() . The problem is that

  • Passing a member function pointer as a parameter, rather than template argument, means the member function pointer is no longer considered a static/global variable within decorate_memfn . Is there a way to force the compiler to recognize that a parameter is from a static/global and hence allow it's use within a lambda without capturing?
  • Doing something similar to decorate_lambda with it's static trick doesn't work for member function pointers because the template instantiation is not necessarily unique (ie if two Callable s have the same signature). Maybe there is a way to make it unique, though?

I understand C++20 could help, but unfortunately I'm stuck with C++17.

Any hints much appreciated!

First, your decorate_lambda is buggy: it silently breaks if you call it with a stateful callable. As a simple check, you could allow callables only if std::is_empty_v is true.


The end result that I would like is to roll decorate_lambda and decorate_memfn into a single decorate(lambda_or_memfn) function

You can use std::integral_constant

template<auto x>
inline constexpr std::integral_constant<decltype(x), x> constant{};

template<typename T, typename F, F T::* x>
auto decorate(std::integral_constant<F T::*, x>)
{
    return [](auto&&... args) {
        return 100 + std::mem_fn(x)(decltype(args)(args)...);
    };
}

auto d = decorate(constant<&Worker::work>);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM