Я пытаюсь получить список кодов ошибок для своего приложения на C++, чтобы ошибки распространялись на несколько проектов. Каждая ошибка содержит просто уникальный код (который можно передать exit()
и подобным) и сообщение для отображения. Помимо конструктора и некоторых методов приведения, мне также нужна какая-то функция для получения ошибки по ее коду, чтобы при выходе из программы A я мог прочитать код возврата из программы B и отобразить его пользователю/журналу/что угодно. Это подразумевает, очевидно, отслеживание всего списка ошибок. Для этого я сохраняю статический std::unordered_map
внутри класса Error, чтобы конструктор Error добавлял к нему создаваемый экземпляр. Однако по какой-то причине, которую я не совсем понимаю, я продолжаю получать ошибку разыменования нулевого указателя при добавлении Error
на карту. Мой код выглядит следующим образом:
error_codes.h
namespace ErrorCodes{
class Error{
private:
int code;
std::string message;
static int counter;
static std::unordered_map<int, Error*> allErrors;
Error(int code, std::string message);
public:
Error(): code(1), message(""){}
explicit Error(std::string message);
Error getByCode(int code);
operator int() const{
return code;
}
operator std::string() const{
return message;
}
std::ostream& operator<<(std::ostream& os)
{
os << std::string("ERR_")+std::to_string(code)+std::string(": ") + message;
return os;
}
operator QString() const{
return QString::fromStdString(message);
}
bool operator == (Error other){
return other.code == code;
}
};
//List all errors here
const Error ERR_ERROR_CODE_NOT_FOUND("Couldn\'t find error with code %1"); //Follow QString formatting style for more complex messages
const Error ERR_GLOBAL_ID_NOT_FOUND("Problem with the global Id, identifier not found.");
const Error ERR_DIR_NOT_FOUND_OR_CREATED("Dir not found or created, check configuration.");
}
а затем мой error_codes.cpp:
using namespace ErrorCodes;
int Error::counter = 0;
std::unordered_map<int, Error*> Error::allErrors = std::unordered_map<int, Error*>();
Error::Error(int code, std::string message) : code(code), message(message){
std::pair pair = std::make_pair(code, this);
allErrors.emplace(pair); //It fails here
}
Error::Error(std::string message){
int code = --counter;
Error(code, message);
}
Error Error::getByCode(int code){
auto it = allErrors.find(code);
if (it!=allErrors.end()){
return *(it->second);
}else{
qCritical() << QString(ERR_ERROR_CODE_NOT_FOUND).arg(code);
exit(ERR_ERROR_CODE_NOT_FOUND);
}
}
PS: В идеале я бы Error
создавал не класс, а структуру и имел бы какую-то коллекцию (например, массив), которая содержала бы все ошибки и позволяла бы мне получать их по коду ошибки. Однако я не нашел способа сделать ошибки доступными в массиве И как «константу» (чтобы в любом месте моего кода я мог просто написать ErrorCodes::XXX
) без необходимости вручную создавать массив со всеми Error
(например, , для этой цели идеально подойдет перечисление). Я был бы признателен, если бы кто-нибудь еще знал лучший способ сделать это.
пожалуйста, попробуйте создать минимально воспроизводимый пример
Error(code, message);
в вашем конструкторе Error::Error(std::string message)
не делает то, что вы думаете. Если только вы не хотели создать экземпляр Error
, который немедленно выбрасывается, оставляя висячий указатель на карте ошибок...
В дополнение к тому, что упомянул Ботье, вы хотите, чтобы ваши переменные static
инициализировались до глобальных экземпляров Error
.
@ 463035818_is_not_an_ai Он сохраняет ошибку*, потому что я (ошибочно) подумал, что ошибка может возникнуть из-за попытки разыменования this
внутри конструктора. Что касается PS, я на самом деле имел в виду, что мне не понадобится «умный» конструктор, заполняющий карту, а просто простая структура данных для хранения кода и сообщения. Я отредактирую вопрос.
Следует отметить, что сначала необходимо создать «фиктивную» ошибку, чтобы получить ошибку «по коду», — это неуклюжий интерфейс. Сделайте getByCode
статичным. Также не имеет особого смысла иметь ошибку «по умолчанию».
Похоже, вы используете неправильный синтаксис конструктора делегата.
Error::Error(std::string message){
int code = --counter;
Error(code, message); //temporary
}
Должно быть
Error::Error(std::string message) : Error(--counter, message) {}
В противном случае вы создаете временную ошибку, которая существует только в кадре стека конструктора делегата. При выходе из конструктора указатель «this» временной ошибки указывает на недопустимую область памяти.
Error::Error(int code, std::string message) : code(code), message(message){
std::pair pair = std::make_pair(code, this); // <- as soon as the function exits, "this" will be an invalid pointer
allErrors.emplace(pair);
}
Я не совсем понимаю, почему, используя только этот код, вы получаете ошибку сегментации напрямую, а не при попытке разыменовать ошибку с карты, но это хорошее место для начала.
В настоящее время вы создаете экземпляры Error
до инициализации unordered_map
. Инициализируйте переменные static
перед их использованием. Пример:
Заголовок:
namespace ErrorCodes {
// class definition
// List all errors here
extern const Error ERR_ERROR_CODE_NOT_FOUND;
extern const Error ERR_GLOBAL_ID_NOT_FOUND;
extern const Error ERR_DIR_NOT_FOUND_OR_CREATED;
} // namespace ErrorCodes
.cpp
файл:
namespace ErrorCodes {
// Initialize the static variables:
int Error::counter = 0;
std::unordered_map<int, Error*> Error::allErrors{};
// Create all the errors afterwards:
const Error ERR_ERROR_CODE_NOT_FOUND("Couldn\'t find error with code %1");
const Error ERR_GLOBAL_ID_NOT_FOUND("Problem with the global Id, identifier not found.");
const Error ERR_DIR_NOT_FOUND_OR_CREATED("Dir not found or created, check configuration.");
//...
} // namespace ErrorCodes
Также обратите внимание, что делегирование конструктора должно использовать список инициализаторов членов.
Пример:
Error::Error(int code, std::string message) : code(code), message(std::move(message)) {
allErrors.emplace(code, this); // It doesn't fail here anymore
}
Error::Error(std::string message) : Error(--counter, std::move(message)) {}
Альтернативно, определите переменные static
и константы встроенными.
Заголовок:
namespace ErrorCodes {
class Error {
private:
inline static int counter = 0;
inline static std::unordered_map<int, Error*> allErrors{};
//...
};
// List all errors here
inline const Error ERR_ERROR_CODE_NOT_FOUND("Couldn\'t find error with code %1");
inline const Error ERR_GLOBAL_ID_NOT_FOUND("Problem with the global Id, identifier not found.");
inline const Error ERR_DIR_NOT_FOUND_OR_CREATED("Dir not found or created, check configuration.");
} // namespace ErrorCodes
Итак, чтобы создать экземпляр статики перед кодами ошибок, мне нужно будет использовать ключевое слово extern
и впоследствии определить их в cpp... Есть ли способ объявить/определить их в одном месте?
@RomoloCaponera Да, я только что добавил для этого опцию.
почему карта хранит указатели? Почему бы и нет
std::unordered_map<int, Error>
? А ваш PS неясен. Нет никакой разницы между структурами и классами (struct
иclass
— два ключевых слова для определения типа класса (с разным доступом по умолчанию))