C / C ++ Можно ли использовать в цикле «for loop» вместо «do while»? - PullRequest
2 голосов
/ 11 декабря 2019

Являются ли две приведенные ниже конструкции эквивалентными в предположении, что "cond" не конфликтует ни с одним именем в программе

#define DOWHILE do { (some_code); } while (0)

#define FORLOOP for(bool cond = true; cond; cond = false) (some_code)

Цель этого вопроса:

У меня есть что-то вродеthis

bool printLogs; // nonconstant dynamic variable

И у меня есть макрос (я не могу делать большие изменения, это большой проект; мне приходится иметь дело с этим макросом) #define LOG ..., который используется как

LOG << "message" << 1 << 0.5 << 'a';

И я хочу, чтобы этот макрос превратился в

if (printLogs) {
    PrinterClass() << "message" << 1 << 0.5 << 'a';
}

Таким образом, напечатанные аргументы не вычисляются, если не печатаются. В этом случае мое решение будет

#define LOG for(cond = printLogs; cond; cond = false) PrinterClass()

Это решение правильно? Существуют ли другие способы?

ОБНОВЛЕНИЕ: Вы не можете использовать простой if здесь. Например, этот код не будет работать

#define LOG if(printLogs) PrinterClass()

int main() {
    if (1)
        LOG << 1;
    else
        LOG << 2;
}

ОБНОВЛЕНИЕ 2: Я ожидаю увидеть объяснение правильности моего или вашего решения. Я должен быть уверен, что решение не вызовет никаких проблем. Вы можете вставить конструкцию «do while» в любом месте вашего кода, где вы можете вставить оператор. Так что «делай пока» ведет себя как простое утверждение. Это правда для моей конструкции?

ОБНОВЛЕНИЕ 3: Решение с глобальным объектом не удовлетворяет, поскольку это вызовет огромные накладные расходы

#include <atomic>
void printImpl(...);

std::atomic<bool> printLog;

struct Log {
    template <typename T>
    Log& operator<<(const T& t) {
        if (printLog) { 
            printImpl(t);
        }
        return *this;
    }
};

int main() {
   Log() << 1 << 2;
}

После всех оптимизаций будетпревращается в

int main() {
    if (printLog) {
        printImpl(1);
    }
// Still should check because printImpl could have changed this variable.
// C++ does not provide any way to say "I promise it won't change printLog"
    if (printLog) { 
        printImpl(2);
    }
}

Таким образом, у вас есть атомное сравнение для каждого использования <<. См <a href="https://godbolt.org/z/sEoUCw" rel="nofollow noreferrer">https://godbolt.org/z/sEoUCw

Ответы [ 2 ]

2 голосов
/ 11 декабря 2019

Вы можете сделать это так:

#define LOG if (!printLogs){} else PrinterClass()
0 голосов
/ 11 декабря 2019

Если вам нужно объектно-ориентированное решение без дополнительных затрат на множественные проверки, рассмотрите что-то вроде этого:

#include <atomic>
#include <utility>
void printImpl(...);

std::atomic<bool> printLog;

class Log {
 public:
  template <typename T>
  const auto& operator<<(T&& t) {
    if (printLog) {
      ulog.active = true;
      return ulog << std::forward<T>(t);
    } else {
      ulog.active = false;
      return ulog;
    }
  }

 private:
  struct unchecked_log {
    template <typename T>
    const auto& operator<<(T&& t) const {
      if (active) {
        printImpl(std::forward<T>(t));
      }
      return *this;
    }
    bool active{false};
  };
  unchecked_log ulog{};
};

// Instead of the macro. Doesn't break backward compatibility
Log LOG;

void test(bool) { LOG << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10; }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...