Моя проблема в том, что я пишу программу, которая должна быть читабельной в будущем, и в программе много исключений. Поэтому, когда мне нужно выдать исключение, мне нужно написать более 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 переменных мне пришлось написать все это …
Есть ли эффективный способ хранить всю информацию из программы (по крайней мере, переменные) и не переписывать все это каждый раз, когда я хочу вызвать исключение?
Заранее спасибо.
Вы можете использовать общую функцию (fill_out_exception_parameters), которая заполняет объект VariableList для универсального исключения и повторно использовать его в любых новых классах исключений, которые вы пишете
Если данные, добавленные в класс исключений, используются только для отображения сообщения об ошибке, можно использовать конкатенацию строк, чтобы уменьшить количество 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());
и пусть логика печати решит, как преобразовать ее в строку. Еще одним преимуществом является то, что он позволяет печатать одно и то же сообщение в разных форматах.
Вы можете использовать Boost Exception, чтобы упростить добавление произвольных данных к вашим объектам исключений и дополнить их более релевантными данными, когда они всплывают в стеке вызовов. Вам не нужно беспокоиться о предварительном определении всего, что может понадобиться для хранения в исключениях, поскольку вы можете буквально хранить любые данные, необходимые для любого исключения.
Я думаю, что я получил самый чистый способ сделать это. Пожалуйста, дайте мне услышать, что вы думаете.
Поэтому я инкапсулирую все соответствующие переменные в шаблонизированном классе следующим образом (просто быстрый и грязный пример)
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 (), который автоматически преобразует значение в строку и добавит его в мой вызов исключения, когда это необходимо 🙂
Любые дополнительные идеи высоко ценятся.
У меня была похожая проблема: как обогащать исключение с контекстной информацией?
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*>
как ты. Просто знайте, что вы будете делать много работы даром.
Если вы заинтересованы в этом, тогда следуйте.
thread_local
). Но даже тогда кто-то может случайно просочиться к нему наружу.catch
пункт.Итак, давайте разберемся с интерфейсом:
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
Эти текстовые описания должны быть изначально связаны с классом исключений, а не записываться в каждый экземпляр как данные времени выполнения.
Точно так же все эти информационные данные должны быть членами класса исключения, и вы можете отформатировать их для вывода в виде текста позже (возможно, в функции-члене самого класса исключения).