Обнаружение дублирующихся работ во время компиляции в коде C ++ - PullRequest
2 голосов
/ 28 марта 2020

давайте рассмотрим следующий код:

struct Foo {
    Foo(Bar b1, Bar b2) : b1(b1), b2(b2) {}
    void work() {
         b1.work();
         b2.work();
         //something
    }
    Bar& b1;
    Bar& b2;
};

struct Bar {
    void work() { /* something */ }
};

int main() {
    Bar a, b;
    a.work();
    //something...
    Foo c(a,b);
    c.work();
    //something...
}

По тому, как я это написал (или намеревался написать), a.work () будет выполнен дважды. Но скажем, я, как программист, знаю, что выполнение его дважды - пустая трата времени на выполнение, и, скажем, это было частью гораздо более сложного программного обеспечения, где было бы слишком хлопотно отслеживать вручную, что такое работа и не сделано.

Очевидно, я мог бы сохранить некоторый логический флаг в Bar и каждый раз проверять, была ли работа уже выполнена, но я хочу знать, есть ли способ, где я уже могу это поймать во время компиляции. Потому что во время компиляции уже ясно, что работа была выполнена.

Ответы [ 3 ]

2 голосов
/ 28 марта 2020

Другой подход. Имейте указатель функции внутри объекта Bar, а в функции work() вызовите pointer. В конструкторе определите указатель как фактическую рабочую функцию. В конце функции переназначьте указатель на пустую функцию.

В этом случае первое выполнение выполнит работу. Но последующие исполнения ничего не сделают (также не проверяя флаг boolean)

struct Bar {
  typedef void (*Bar::fptr_t)();
  Bar() : fptr(actual_work) {}
  void actual_work() {
    /*something*/;
    fptr = &Bar::empty_work;
  }

  void empty_work() {}
  void work() {fptr();}
  fptr_t fptr;
};

Что-то вроде выше.

1 голос
/ 28 марта 2020

На самом деле, во время компиляции не очевидно, был ли выполнен код или нет. Предположим, у вас есть оператор if, и в нем вы звоните a.work(). Как компилятор узнает, был ли в это время a.work() выполнен? Как вы говорите, не думайте, что оператор if очень прост (предположим, он ищет какой-то внешний сигнал и выполняет код в зависимости от этого сигнала). Лучший способ избежать поведения - сохранять логическое значение.

1 голос
/ 28 марта 2020

Нет, не совсем.

Компилятор способен некоторый stati c анализ, и если вы могли бы попросить его диагностировать это состояние, он может быть в состоянии сделать это в некоторых простых случаях. Но как только у вас появляется нетривиальный поток (например, условия выполнения if), он очень быстро исчезает из окна. Вероятно, это одна из причин того, что никто не создал такую ​​программируемую функцию для компиляторов: высокая сложность, с незначительной утилитой.

Может быть возможно запрограммировать некоторые сторонние анализаторы состояния (c) (или создать один! ) для диагностики вашего простого случая, но опять-таки это большая работа для обработки только самых простых случаев, которые вы уже можете заметить своими глазами.

Вместо этого вы можете заставить work() произойти в Bar конструктор. Тогда невозможно выполнить работу дважды над одним и тем же объектом. Однако выполнение большого количества работы в конструкторе часто не одобряется.

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

Флаг состояния не обязательно должен быть логическим; это может быть enum. Надежные конечные автоматы внутри ваших объектов могут быть очень полезны.

При этом я бы посоветовал пересмотреть ваш текущий подход, когда вы передаете ссылки на вещи в другие вещи, которые работают на них; это не простой дизайн, это всего лишь простой пример вашего дизайна! Вы можете sh рассмотреть возможность передачи какого-либо одноразового типа прокси.

...