Дополнить класс/применить аспект в чистом C++ (C++11)

Предположим, у меня есть класс:

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

    int computeAnswer() {
        return -42;
    };

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

Он выполняет некоторые вычисления, может делать все, что захочет.

Теперь я хочу дополнить его — применить аспект, скажем, тот, который регистрирует каждый вызов метода.

Если бы я реализовал это вручную, я бы реализовал все методы следующим образом:

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

Я хотел бы, чтобы решение было как можно более общим (я не хочу вручную переадресовывать все вызовы), поэтому возможные варианты использования могут включать одно из этих (в зависимости от того, что возможно)

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.

так что когда я позвоню

 int result =  w.computeAnswer();

Звонки будут регистрироваться. Возможно, здесь может пригодиться новый оператор многоточия (...)?


person Luce Ian    schedule 06.08.2013    source источник
comment
Шаблон декоратора stackoverflow.com/questions/2988066/decorator-pattern-in- с   -  person doctorlove    schedule 06.08.2013


Ответы (2)


Это невозможно напрямую, так как вы не можете проверить класс, чтобы увидеть, какие члены у него есть.

Однако вы можете сделать что-то близкое:

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

Где Logging::operator() выглядит следующим образом:

/* 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;
}

Это не будет лучше, чем это, для полностью универсального кода, поскольку C++ не имеет (статического) отражения.

person Xeo    schedule 06.08.2013
comment
это приятно. Я считаю, что хороший макрос может скрыть большую часть лямбда-выражения. - person Luce Ian; 06.08.2013

Расширяя ответ Xeo, если вы используете decltype или result_of, а не auto &&, вы также получаете исключение копирования.

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;
}

В С++ 14 вы можете сократить это до:

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