2

How to return an arbitrary type (either void or non-void) from a template functor wrapper? I use the wrapper for pre- and post- conditions, so I need to store the returned value in a local variable before returning it from wrapper. But when the returned type is void the compiler gives and error, because variables can't have a void type. What can be done here?

template <typename Functor, typename... Args>
auto Decorate(Functor f, Args&&... args)
-> decltype(f(std::forward<Args>(args)...)) {
    // preconditions
    const auto result = f(std::forward<Args>(args)...);
    // postconditions
    return result;
}
lizarisk
  • 7,562
  • 10
  • 46
  • 70
  • what are the pre/post conditions for void? – yuri kilochek Aug 29 '13 at 17:24
  • 1
    I can think of two quick solutions: 1) put the pre and post conditions as ctor and dtor of a separate class and then `return f(...)` (you are allowed to return void from templates as an exception to the rules), or 2) make an overload for void-returning callables, but that means duplicated code. – R. Martinho Fernandes Aug 29 '13 at 17:24
  • I use this to measure the execution time of a function. – lizarisk Aug 29 '13 at 17:25

2 Answers2

5

Run your pre- and post-conditions in the constructor/destructor of a suitable class and return the value directly! As long as you don't need to touch the return value in your post-condition that shouldn't be a problem!

struct condition
{
    condition() { /* do pre-condition checks */ }
    ~condition() { /* do post-condition checks */ }
    condition(condition&) = delete;
    void operator= (condition&) = delete;
};
template <typename Functor, typename... Args>
auto Decorate(Functor f, Args&&... args)
    -> decltype(f(std::forward<Args>(args)...)) {
    condition checker;
    return f(std::forward<Args>(args)...);
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
1

Use sfinae:

#include <type_traits>

template <typename Functor, typename... Args>
auto Decorate(Functor f, Args&&... args)
    -> typename std::enable_if<std::is_same<decltype(f(std::forward<Args>(args)...)), void>::value, void>::type
{
    // preconditions
    f(std::forward<Args>(args)...);
    // postconditions
    return;
}

template <typename Functor, typename... Args>
auto Decorate(Functor f, Args&&... args)
    -> typename std::enable_if<!std::is_same<decltype(f(std::forward<Args>(args)...)), void>::value, decltype(f(std::forward<Args>(args)...))>::type
{
    // preconditions
    auto result = f(std::forward<Args>(args)...);
    // postconditions
    return result;
}

test:

void f(int x) {
    std::cout << "f(" << x << ")" << std::endl;
    return;
}

int g(int x) {
    std::cout << "g(" << x << ")" << std::endl;
    return x * x;
}

int main()
{       
    Decorate(f, 3);
    std::cout << Decorate(g, 3);
    return 0;
}

f(3)
g(3)
9

yuri kilochek
  • 12,709
  • 2
  • 32
  • 59