add more robust error handling

...incl handling of secondary errors
This commit is contained in:
Fischlurch 2011-12-30 20:20:11 +01:00
parent e838fb9799
commit 35ed2dcf5c
3 changed files with 101 additions and 26 deletions

View file

@ -46,6 +46,7 @@
#include "lib/error.hpp"
#include "lib/format-util.hpp"
#include "lib/format-string.hpp"
#include <boost/static_assert.hpp>
@ -68,15 +69,45 @@ namespace util {
return reinterpret_cast<boost::format&> (*buffer);
}
/** in case the formatting of a (primitive) value fails,
* we try to use a error indicator instead
*/
inline void
pushFailsafeReplacement (char* formatter, const char* errorMsg =NULL)
try {
string placeholder("<Error");
if (errorMsg){
placeholder += ": ";
placeholder += errorMsg;
}
placeholder += ">";
accessImpl(formatter) % placeholder;
}
ERROR_LOG_AND_IGNORE (progress, "Supplying placeholder for problematic format parameter")
inline void
suppressInsufficientArgumentErrors (char* formatter)
{
using namespace boost::io;
accessImpl(formatter).exceptions (all_error_bits ^ too_few_args_bit);
}
}//(End) implementation details
/** */
_Fmt::_Fmt (string formatString)
{
BOOST_STATIC_ASSERT (sizeof(boost::format) <= FORMATTER_SIZE);
new(formatter_) boost::format(formatString);
suppressInsufficientArgumentErrors (formatter_);
}
_Fmt::~_Fmt ()
@ -86,8 +117,8 @@ namespace util {
/** @internal access points for the frontend,
* allowing to push a parameter value down into
* the implementation for the actual formatting.
* allowing to push a parameter value down into the implementation
* for the actual formatting.
* @note we need to generate instantiations of this template function
* explicitly for all basic types to be supported for direct handling,
* otherwise we'll get linker errors. Lumiera uses the ``inclusion model''
@ -98,18 +129,38 @@ namespace util {
template<typename VAL>
void
_Fmt::pushParameter (VAL const& val)
{
accessImpl(formatter_) % val;
}
try {
accessImpl(formatter_) % val;
}
catch (boost::io::too_many_args& argErr)
{
WARN (progress, "Format: excess argument '%s' of type %s ignored."
, cStr(str(val))
, cStr(tyStr(val)));
}
catch (std::exception& failure)
{
WARN (progress, "Format: Parameter '%s' causes problems: %s"
, cStr(str(val))
, failure.what());
pushFailsafeReplacement (formatter_, failure.what());
}
catch (...)
{
WARN (progress, "Format: Unexpected problems accepting format parameter '%s'", cStr(str(val)));
pushFailsafeReplacement (formatter_);
}
template<typename VAL>
void
_Fmt::pushParameter (const VAL * const pVal)
{
if (pVal)
pushParameter(*pVal);
pushParameter (*pVal);
else
pushParameter(string("(null)"));
pushParameter (string("<null>"));
}
template<>
@ -157,21 +208,44 @@ namespace util {
* are evaluated and formatted prior to receiving the formatted result
*/
_Fmt::operator string() const
{
return accessImpl(formatter_).str();
}
try {
return accessImpl(formatter_).str();
}
catch (std::exception& failure)
{
WARN (progress, "Format: Failure to receive formatted result: %s", failure.what());
return "<formatting failure>";
}
catch (...)
{
WARN (progress, "Format: Unexpected problems while formatting output.");
return "<unexpected problems>";
}
/** send the formatted buffer directly to the output stream.
* @note this is more efficient than creating a string and outputting that,
* because boost::format internally uses a stringstream to generate
* because boost::format internally uses a string-stream to generate
* the formatted representation, relying on the C++ output framework
*/
std::ostream&
operator<< (std::ostream& os, _Fmt const& fmt)
{
return os << accessImpl(fmt.formatter_);
}
try {
return os << accessImpl(fmt.formatter_);
}
catch(std::exception& failure)
{
WARN (progress, "Format: Failure when outputting formatted result: %s", failure.what());
return os << "<formatting failure>";
}
catch(...)
{
WARN (progress, "Format: Unexpected problems while producing formatted output.");
return os << "<unexpected problems>";
}

View file

@ -201,7 +201,11 @@ namespace util {
_log_and_stringify (std::exception const& ex)
{
WARN (progress, "Error while invoking custom string conversion: %s", ex.what());
return ex.what();
try {
return string("<string conversion failed: ")+ex.what()+">";
}
catch(...) { /* secondary errors ignored */ }
return "(formatting failure)";
}
inline string
@ -209,15 +213,10 @@ namespace util {
{
const char* errID = lumiera_error();
if (errID)
{
ERROR (progress, "Unknown error while invoking custom string conversion. Lumiera error flag = %s", errID);
return string("Unknown error in string conversion. FLAG=")+errID;
}
ERROR (progress, "Unknown error while invoking custom string conversion. Lumiera error flag = %s", errID);
else
{
ERROR (progress, "Unknown error while invoking custom string conversion. No Lumiera error flag set.");
return "Unknown error in string conversion";
}
ERROR (progress, "Unknown error while invoking custom string conversion. No Lumiera error flag set.");
return "<Unknown error in string conversion>";
}
}//(End) guards/helpers

View file

@ -237,12 +237,12 @@ namespace test {
CHECK (contains (mangledType, "Silent"));
CHECK (_Fmt("!!%s!!") % v == "!!Number-013!!");
CHECK (_Fmt("!!%s!!") % x == "!!LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).!!");
CHECK (_Fmt("!!%s!!") % x == "!!<string conversion failed: LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).>!!");
CHECK (contains (_Fmt("%s") % rs1, "Silent"));
CHECK (contains (_Fmt("%s") % rs2, "Silent"));
CHECK (_Fmt("!!%s!!") % rv == "!!LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).!!");
CHECK (_Fmt("!!%s!!") % rv == "!!<string conversion failed: LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).>!!");
x.i_ = 42;
CHECK (_Fmt("!!%s!!") % rv == "!!Number-042!!");
@ -258,6 +258,8 @@ namespace test {
cout << _Fmt("__%d__") % 1 << endl;
cout << _Fmt("__%d__") % 1 % 2 << endl;
const char* evil = NULL;
cout << _Fmt("__%d__") % evil << endl;
cout << _Fmt("__%d__") % "dirt" << endl;
cout << _Fmt("__%d__") % "1234" << endl;
cout << _Fmt("__%d__") % "0xff" << endl;
@ -307,7 +309,7 @@ namespace test {
pv = NULL;
vv = NULL;
CHECK (_Fmt("__%s__") % pv == "__(null)__");
CHECK (_Fmt("__%s__") % pv == "__<null>__");
CHECK (_Fmt("__%s__") % pv == "__0__");
}
};