Как передать номер строки в исключение? - PullRequest
2 голосов
/ 19 мая 2019

Я не знаю, является ли это общей рекомендацией для всего кода C ++ в целом, но, по крайней мере, в некоторых контекстах рекомендуется не использовать макрос assert и вместо этого генерировать исключения .

У меня всегда была проблема с этим подходом.Как узнать, какая строка вызвала исключение?

Ну, да, у нас есть константа препроцессора __LINE__.Однако, проходить его нетривиально.

Подходы, которые я пробовал:

#include <stdexcept>

int main() {
  throw std::logic_error("__LINE__");
}

terminate called after throwing an instance of 'std::logic_error'
  what():  __LINE__
Aborted (core dumped)

Ну, это не совсем то, что я хотел.Давайте попробуем еще раз:

#include <stdexcept>

int main() {
  throw std::logic_error(__LINE__);
}

wtf.cc: In function ‘int main()’:
wtf.cc:4:34: error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]
   throw std::logic_error(__LINE__);
                                  ^
In file included from wtf.cc:1:0:
/usr/include/c++/7/stdexcept:124:5: note:   initializing argument 1 of ‘std::logic_error::logic_error(const char*)’
     logic_error(const char*) _GLIBCXX_TXN_SAFE;
     ^~~~~~~~~~~

Дух.Что я хотел?Хорошо, давайте попробуем еще раз, на этот раз правильно:

#include <stdexcept>
#include <sstream>

std::ostringstream lineno;

int main() {
  throw std::logic_error((lineno << __LINE__, lineno.str()));
}

terminate called after throwing an instance of 'std::logic_error'
  what():  7
Aborted (core dumped)

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

Однако типичные способы удаления дублирования кода здесь явно не пройдут:

#include <stdexcept>
#include <sstream>

void fatal() {
  std::ostringstream slineno;
  slineno << __LINE__;
  std::string lineno = slineno.str();
  throw std::logic_error(lineno);
}

int main() {
  fatal();
}

terminate called after throwing an instance of 'std::logic_error'
  what():  6
Aborted (core dumped)

К сожалению, не этот точный номер строки.

И, наконец, лучшее, что я могу предложить:

#include <stdexcept>
#include <sstream>

#define FATAL {std::ostringstream slineno; \
               slineno << __LINE__; \
               std::string lineno = slineno.str(); \
               throw std::logic_error(lineno);}

int main() {
  FATAL;
}

terminate called after throwing an instance of 'std::logic_error'
  what():  10
Aborted (core dumped)

Этоправильный подход?Мои сомнения проистекают из того факта, что (а) я слышал, что макросы в C ++ рекомендуются против;(б) если бы это было правильно, я полагаю, что людям придется изобретать это снова и снова;Я имею в виду, что это такая простая утилита, которая должна быть в стандартной библиотеке, верно?Так что я либо что-то пропустил из стандартной библиотеки, либо «Doing Things Wrong ™», я полагаю.

Как это сделать правильно?

1 Ответ

6 голосов
/ 19 мая 2019

Я слышал, что макросы в C ++ не рекомендуются;

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

Если бы это было правильно, я полагаю, что людям пришлось бы изобретать это снова и снова; Я имею в виду, что это такая простая утилита, которая должна быть в стандартной библиотеке, верно?

Это: В форме assert. Прежде всего, std::logic_error является довольно плохим исключением, поскольку логические ошибки являются ошибками программирования и не могут быть обработаны каким-либо кодом обработки исключений в целом.

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

assert - это не плохой стиль; на самом деле, мы получаем контракты в C ++ 20, где у нас будут не-макро assert и условия до / после. :) Для дальнейшего понимания: LLVM полон assert с, и это далеко не плохая кодовая база.

...