我的问题是我正在编写一个应该可读的程序,并且程序有很多例外情况。因此,每当我必须抛出异常时,我必须编写超过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个变量,我不得不写下所有...... 是否有一种有效的方法来包含程序中的所有信息(至少是变量),并且每次我想抛出异常时都不会重写所有这些信息?
提前致谢。
答案 0 :(得分:4)
您可以使用一个通用函数(fill_out_exception_parameters)来填充一般异常的VariableList对象,并在您编写的任何新异常类中重用它
答案 1 :(得分:2)
如果添加到异常类的数据仅用于显示错误消息,则可以使用字符串连接来减少使用的push_back()
个数。
例如,您可以使用:
ex.VariableList()[0].push_back(string("Pin state: ") + ToString(pin.getPinState());
您甚至可以连接所有其他消息,而不是使用单独的索引(1,2,3,4等)。
此外,对于每个字段,您可以使用专用的setter方法来提供适当的值。例如:
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 :(得分:1)
您可以使用Boost Exception简化向异常对象添加任意数据的方法,并在它们冒泡调用堆栈时使用更相关的数据来扩充它们。您不必担心预先定义可能需要存储在异常中的所有内容,因为您可以根据需要将任何数据存储到任何异常中。
答案 3 :(得分:0)
我认为我有最干净的方法。请让我听听你的想法。
所以我将所有相关变量封装在一个模板化的类中,如下所示(只是一个快速而肮脏的例子)
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(),这将自动将值转换为字符串并在必要时将其添加到我的异常calss :)
非常感谢任何其他想法。
答案 4 :(得分:0)
我遇到了类似的问题:如何通过上下文信息来充实异常?
Boost提出了一个解决方案:try/catch
并在重新抛出之前在catch
块中丰富异常。它确实丰富了异常,但不是自动。
我最终提出的解决方案非常简单,并且基于C ++析构函数的强度。但首先,如何使用它:
void foo(int i) {
LOG_EX_VAR(i);
// do something that might throw
}
是的,就是这样,一个宏调用和i
被添加到异常上下文中,同时扩展了宏的函数名,文件名和行号。
背后是什么? most important 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
答案 5 :(得分:0)
这些文本描述应该固有地链接到异常类,而不是作为运行时数据写入每个实例。
类似地,所有信息数据都应该是异常类的成员,并且您可以将其格式化为稍后输出的文本(可能在异常类本身的成员函数中)。