Как правильно обработать исключение в Stack Overflow

Моя проблема в том, что я пишу программу, которая должна быть читабельной в будущем, и в программе много исключений. Поэтому, когда мне нужно выдать исключение, мне нужно написать более 10 строк, чтобы инициализировать мой класс исключения и добавить в него информацию из программы. Например, следующим образом:

MyExceptionClass ex;
ex.setErrorMessage("PIN_CANNOT_GO_IN");
ex.setErrorDetails("The pin is asked to go to the state IN while the depth of the r-coordinate does not support it");
ex.setResolutionMessage("Configure your coordinates-file to move first to the correct position before changing the state of the pin");
ex.VariableList().resize(5);
ex.VariableList()[0].push_back("Pin state: ");
ex.VariableList()[0].push_back(ToString(pin.getPinState()));
ex.VariableList()[1].push_back("Pin target state: ");
ex.VariableList()[1].push_back(ToString(coordinatesData[coordinatesIndex].targetPinState));
ex.VariableList()[2].push_back("Current r Value: ");
ex.VariableList()[2].push_back(ToString(EncoderPosition.r));
ex.VariableList()[3].push_back("Current phi Value: ");
ex.VariableList()[3].push_back(ToString(EncoderPosition.phi));
ex.VariableList()[4].push_back("Current z Value: ");
ex.VariableList()[4].push_back(ToString(EncoderPosition.z));

ex.printLog();
ex.writeLog(exceptionLogFilePath.getValue());

throw ex;

Так что только для 5 переменных мне пришлось написать все это …
Есть ли эффективный способ хранить всю информацию из программы (по крайней мере, переменные) и не переписывать все это каждый раз, когда я хочу вызвать исключение?

Заранее спасибо.

3

Решение

Вы можете использовать общую функцию (fill_out_exception_parameters), которая заполняет объект VariableList для универсального исключения и повторно использовать его в любых новых классах исключений, которые вы пишете

4

Другие решения

Если данные, добавленные в класс исключений, используются только для отображения сообщения об ошибке, можно использовать конкатенацию строк, чтобы уменьшить количество push_back() используемый.

Например, вы можете использовать:

ex.VariableList()[0].push_back(string("Pin state: ") + ToString(pin.getPinState());

Вы можете даже объединить все другие сообщения вместо использования отдельных индексов (1, 2, 3, 4 и т. Д.) Для каждого.

Более того, для каждого поля вы можете использовать специальный метод установки для подачи соответствующего значения. Например:

ex.VariableList()[0].setPinState(ToString(pin.getPinState()));

а затем "Pin state: " часть должна быть перемещена в место, где сообщение об ошибке печатные.


Если пойти еще дальше, ваш класс исключений может иметь специальный метод, который принимает все объекты, которые вносят вклад в сообщение об ошибке, и вместо этого вызывает это сообщение. Например:

void MyExceptionClass::setMessage(Pin& pin, CoordinatesData& cd, EncoderPosition& ep) {
setPinState(ToString(pin.getPinState()));
// set whatever else you want here
}

Кроме того, переместить ToString() часть, где сообщение печатается, просто сохраните значения в классе исключений. Например, измените строку выше на (необходимо изменить подпись соответственно):

setPinState(pin.getPinState());

и пусть логика печати решит, как преобразовать ее в строку. Еще одним преимуществом является то, что он позволяет печатать одно и то же сообщение в разных форматах.

2

Вы можете использовать Boost Exception, чтобы упростить добавление произвольных данных к вашим объектам исключений и дополнить их более релевантными данными, когда они всплывают в стеке вызовов. Вам не нужно беспокоиться о предварительном определении всего, что может понадобиться для хранения в исключениях, поскольку вы можете буквально хранить любые данные, необходимые для любого исключения.

1

Я думаю, что я получил самый чистый способ сделать это. Пожалуйста, дайте мне услышать, что вы думаете.

Поэтому я инкапсулирую все соответствующие переменные в шаблонизированном классе следующим образом (просто быстрый и грязный пример)

class VarBase
{
VarBase();
static std::vector<VarBase*> __allParams;
string getStringValue() = 0;
};

template <typename T>
class Var : public VarBase
{
T value;
string name;
string description;
toString();
operator T();
string getStringValue();
};

VarBase::VarBase()
{
__allParams.push_back(this);
}
VarBase::~VarBase()
{
//handle removing from __allParams vector or whatever container
}
template <typename T>
std::string Var<T>::getStringValue()
{
std::stringstream s;
s << paramValue;
return s.str();
}

Теперь, если мой класс исключений дружит с классом VarBase, он может обращаться к __allParams, проходить через него и вызывать getStringValue (), который автоматически преобразует значение в строку и добавит его в мой вызов исключения, когда это необходимо 🙂

Любые дополнительные идеи высоко ценятся.

0

У меня была похожая проблема: как обогащать исключение с контекстной информацией?

Boost предлагает одно решение: try/catch и обогатить исключение в catch заблокировать, прежде чем выбросить его. Это обогащает исключение, но не автоматически.

Решение, которое я придумал, в конце концов, чрезвычайно просто и основано на силе деструкторов C ++. Но сначала, как его использовать:

void foo(int i) {
LOG_EX_VAR(i);
// do something that might throw
}

Да, вот и все, один вызов макроса и i добавляется в контекст исключения вместе с именем функции, именем файла и номером строки, в которой был развернут макрос.

Что позади? самый важный const и немного магии.

class LogVar {
public:
LogVar(LogVar const&) = delete;
LogVar& operator=(LogVar const&) = delete;

virtual ~LogVar() {}

protected:
LogVar();
}; // class LogVar

template <typename T>
class LogVarT: public LogVar {
public:
LogVarT(char const* fc, char const* fl, int l, char const* n, T const& t):
_function(fc), _filename(fl), _line(l), _name(n), _t(t) {}

~LogVar() {
ContextInterface::AddVariable(_function, _filename, _line, _name, _t);
}

private:
char const* _function;
char const* _filename;
int _line;

char const* _name;
T const& _t;
}; // class LogVarT

template <typename T>
LogVarT make_log_var(char const* fc,
char const* fl,
int l,
char const* n,
T const& t)
{
return LogVarT(fc, fl, l, n, t);
}

#define LOG_EX_VAR(Var_)                                                      \
LogVar const& BOOST_PP_CAT(_5416454614, Var_) =                           \
make_log_var(__func__, __FILE__, __LINE__, #Var_, Var_);

Это работает довольно хорошо, если вы можете получить сложную часть ( ContextInterface::AddVariable() функция) работать.

Если вы не хотите беспокоиться об этом, а затем пойти на thread_local std::vector<LogVar*> как ты. Просто знайте, что вы будете делать много работы даром.

Если вы заинтересованы в этом, тогда следуйте.

  1. Давайте осознаем, что самая важная часть здесь — это получить что-то поточно-ориентированное. Таким образом, контекст будет глобальным … для каждого потока (иначе thread_local). Но даже тогда кто-то может случайно просочиться к нему наружу.
  2. Важно понимать, что несколько исключений могут сосуществовать, хотя в любой момент времени может быть исключено только одно: это исключение может быть выброшено в пределах catch пункт.
  3. Мы можем только обрабатывать исключения, которые мы сами себе создаем, поэтому нам нужна какая-то политика по умолчанию для остальных. Ведение журнала, например.

Итак, давайте разберемся с интерфейсом:

class ContextInterface {
public:
typedef std::unique_ptr<ContextInterface> UPtr;
typedef std::shared_ptr<ContextInterface> SPtr;
typedef std::weak_ptr<ContextInterface> WPtr;

static UPtr SetDefault(UPtr d) {
std::swap(d, DefaultContext);
return d;
}

template <typename T, typename... Args>
static SPtr SetActive(Args&&... args) {
SPtr ci = ExceptionContext.lock();
if (ci.get()) { return ci; }

ci.reset(new T(std::forward<Args>(args)...));
ExceptionContext = ci;
return ci;
}

template <typename T>
static void AddVariable(char const* fc,
char const* fl,
int l,
char const* n,
T const& t)
{
SPtr sp = ExceptionContext.lock();
ContextInterface* ci = sp.get();

if (not ci) { ci = DefaultContext.get(); }

if (not ci) { return; }

ci->report(fc, fl, l, n) << t;
}

virtual ~ContextInterface() {}

private:
static thread_local UPtr DefaultContext;
static thread_local WPtr ExceptionContext;

virtual std::ostream& report(char const* fc,
char const* fl,
int l,
char const* n) = 0;
}; // class ContextInterface

И, наконец, последний отсутствующий фрагмент (ну, кроме фактического контекста, который вы хотели бы предположить): пример базового класса исключений.

class ContextualException: public virtual std::exception {
public:
ContextualException(): _c(ContextInterface::SetActive<ExceptionContext>()) {}

ContextInterface const& context() const { return *_c; }

private:
ContextInterface::SPtr _c;
}; // class ContextualException
0

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

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

0
По вопросам рекламы [email protected]