1

I am trying to write code to do something similar (code written for demonstration purposes) to this:

template <typename F, typename Args...>
inline auto runFunc(F func) -> foo
{
    return foo([func](Args... args) -> std::result_of<F>::type
        {
            // Do something before calling func
            func(args...);
            // Do something after call func
        });
}

So basically I am trying to write a function that returns an object that takes lambda that matches the templated function type. Obviously this code won't work because I do not have Args... defined. How would I solve this in C++11?

Cthutu
  • 8,713
  • 7
  • 33
  • 49
  • 4
    Can you provide more context about what you're trying to do? – templatetypedef Jul 29 '16 at 16:18
  • The `-> foo` after `runFunc(F func)` makes no sense. Try `decltype(foo([func](Args... args)` ... `))` – Czipperz Jul 29 '16 at 16:22
  • What is `foo()`? Are you just trying to decorate `func`? – Barry Jul 29 '16 at 16:51
  • Oh `foo` is a class? – Czipperz Jul 29 '16 at 17:01
  • foo is a class in this example. @templateypedef: Basically I create a task object that holds a functor (whether it is a std::function, lambda or anything else). However, I wish to wrap that function inside a lambda to do extra work, but have that lambda be able to forward parameters to that functor. – Cthutu Jul 29 '16 at 17:44
  • Does this help you?: http://stackoverflow.com/questions/25885893/how-to-create-a-variadic-generic-lambda – lorro Jul 29 '16 at 17:49
  • `std::result_of::type` is wrong, you meant `std::result_of::type`, and you meant to precede it with `typename`. – Oktalist Jul 29 '16 at 20:29
  • Actually you don't need to precede it with typename (at least with the microsoft compiler). Also I do mean since F represents the entire function including arguments. That's why this is so difficult for me as I need to extract the Args. – Cthutu Aug 02 '16 at 10:43

3 Answers3

1
template<class F_before, class F, class F_after>
struct decorate_func_t {
  F_before f0;
  F f1;
  F_after f2;

  template<class...Args>
  typename std::result_of<F(Args...)>::type operator()(Args&&...args)const{
    f0();
    auto r = f1(std::forward<Args>(args)...);
    f2();
    return r;
  }
};
template<class F_before, class F, class F_after>
decorate_func_t<F_before, F, F_after>
decorate_func( F_before before, F f, F_after after ){
  return {std::move(before), std::move(f), std::move(after)};
}

Then:

template <typename F, typename Args...>
inline auto runFunc(F func) -> foo
{
  return foo(decorate_func(
        []{/* Do something before calling func */},
        func,
        []{/* Do something after call func */ }
  };
}

the lack of auto parameters in C++11 lambdas makes this about the best you can do.

In C++14 this is trivial:

template <class F>
auto runFunc(F func)
{
  return foo(
    [func](auto&&... args) // ->decltype(auto) maybe
    {
      // Do something before calling func
      auto r = func(decltype(args)(args)...);
      // Do something after call func
      return r;
    }
  );
}

note that many nominally C++11 compilers actually support auto parameters on lambdas.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

Still being unsure it this is what you're searching for, I risk posting:

#include <iostream>

struct foo
{
    template<typename T>
    foo(T lambda)
    {
        lambda(1, 2);
    }
};

template <typename F, typename... Args>
inline typename std::result_of<F>::type runFunc(F func)
{
    return foo(
        [func](Args... args)
        {
            std::cout << "Before";
            func(args...);
            std::cout << "After";
        }
    );
}

struct print
{
    void operator()(int i) const
    {
        std::cout << i << std::endl;
    }

    void operator()(int i, int j) const
    {
        std::cout << i << " " << j << std::endl;
    }
};

int main()
{
    runFunc<print, int, int>(print());
}
lorro
  • 10,687
  • 23
  • 36
  • Note that your solution requires C++14. You cannot return a lambda from a function in C++11, you need to wrap it in a named type (ex: `std::function`, function pointer...). – KABoissonneault Jul 29 '16 at 18:04
  • Oh, I just realized the lambda is not the object returned by the function. Well, my first point still stands – KABoissonneault Jul 29 '16 at 18:06
  • @KABoissonneault: which part requires *more* than OP's code? – lorro Jul 30 '16 at 12:45
  • Auto-deduced return types. OP only uses trailing return types – KABoissonneault Jul 30 '16 at 13:24
  • @KABoissonneault: yep, thanks for spotting - fixed that. – lorro Jul 30 '16 at 20:34
  • Yes, unfortunately this doesn't work in C++11. If I had generic lambdas I could solve this. What I need to do is define a template function (F1) that wraps another function (F2) that could be a normal function, lambda, std::function or functor object (hence template parameter F). The reason I want to do F1 is to add functionality to F2 and have the signature of F1 matching F2. – Cthutu Aug 02 '16 at 12:05
0

You can use a support structure as in the following example:

#include<type_traits>
#include<cassert>

struct foo {
    template<typename F>
    foo(F f) { assert(42 == f(42)); }
};

template<typename>
struct S;

template<typename R, typename... Args>
struct S<R(*)(Args...)> {
    template <typename F>
    static auto runFunc(F func) -> foo
    {
        return foo{[func](Args... args) -> R
            {
                // Do something before calling func
                auto r = func(args...);
                // Do something after call func
                return r;
            }};
    }
};

template<typename F>
inline auto runFunc(F func) -> foo
{
    return S<F>::runFunc(func);
}

int f(int i) { return i; }

int main() {
    runFunc(f);
}

For it's not clear to me what's the context of the problem, I'm not sure I got exactly what you were asking for.
I hope the code above can help you.

skypjack
  • 49,335
  • 19
  • 95
  • 187