Frage

Suppose I have a class:

class Widget {
public:
    void initialize() {
        // hurr-durr
    };

    int computeAnswer() {
        return -42;
    };

    std::string getQuestion() {
        return "The question";
    };
};

It performs some computation, can do whatever it wants.

Now I want to augment it - apply an aspect, say one that logs each method call.

If I implemented this by hand, I'd implement all methods in this fashion:

  int LoggingWidget::computeAnswer(){
    log << 'Calling method computeAnswer';
    int result = Widget::computerAnswer();
    log << 'Result = ' << result;
    return result;
  }

I'd like the solution to be as generic as possible (I don't want to manually forward all calls), so the possible usages could include one of these (whichever is possible)

Widget* w = new LoggingWidget();  // either a class that inherits from Widget
                                  // and automatically forwards all calls.

Widget* w = new Logging<Widget>(); // or a template that does this.

so that when I call

 int result =  w.computeAnswer();

The calls will be logged. Perhaps the new ellipsis operator (...) could come in handy here?

War es hilfreich?

Lösung

This isn't directly possible, since you can't inspect a class to see which members it has.

However, you can do something close:

Logging<Widget> w(widget);
w([&](Widget& w){
  return w.computeAnswer();
});

Where Logging::operator() looks like follows:

/* somewhere in class: T wrapped; */

template<class F>
auto operator()(F&& f)
  -> decltype(f(wrapped))
{
  pre_log();
  auto&& result = f(wrapped);
  post_log(result);
  return result;
}

It won't get better much better than this for totally generic code, since C++ has no (static) reflection.

Andere Tipps

Expanding on Xeo's answer, if you use decltype or result_of rather than auto && you also get copy elision.

template<typename F>
auto operator()(F &&f) -> decltype(std::forward<F>(f)(wrapped))
{
    pre_log();
    decltype(std::forward<F>(f)(wrapped)) result = std::forward<F>(f)(wrapped);
    post_log(result);
    return result;
}

In C++14 you can shorten this to:

template<typename F>
decltype(auto) operator()(F &&f)
{
    pre_log();
    decltype(auto) result = std::forward<F>(f)(wrapped);
    post_log(result);
    return result;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top