parent
e1b9b5b135
commit
37384f1b68
5 changed files with 142 additions and 31 deletions
|
|
@ -24,11 +24,13 @@
|
|||
** Implementation for printf-style formatting, based on boost::format.
|
||||
** This file holds the generic implementation of our format frontend,
|
||||
** which actually just invokes boost::format. The corresponding header
|
||||
** format-string.hpp contains some template functions and classes,
|
||||
** which select an appropriate wrapper to pass the calls down.
|
||||
** format-string.hpp provides some template functions and classes,
|
||||
** either invoking a custom string conversion, or passing primitive
|
||||
** values down unaltered.
|
||||
**
|
||||
** Here, we define explicit specialisations for the frontend to invoke,
|
||||
** which in turn just pass on the given argument value to the embedded
|
||||
** boost::format object, which in turn integrates the formatted result
|
||||
** boost::format object, which in turn dumps the formatted result
|
||||
** into an embedded string stream.
|
||||
**
|
||||
** To avoid exposing boost::format in the frontend header, we use an
|
||||
|
|
@ -61,6 +63,7 @@ namespace util {
|
|||
|
||||
using boost::format;
|
||||
|
||||
|
||||
namespace { // implementation details...
|
||||
|
||||
inline boost::format&
|
||||
|
|
@ -78,8 +81,7 @@ namespace util {
|
|||
|
||||
|
||||
/** in case the formatting of a (primitive) value fails,
|
||||
* we try to use a error indicator instead
|
||||
*/
|
||||
* we try to supply an error indicator instead */
|
||||
void
|
||||
pushFailsafeReplacement (char* formatter, const char* errorMsg =NULL)
|
||||
try {
|
||||
|
|
@ -109,14 +111,26 @@ namespace util {
|
|||
|
||||
|
||||
|
||||
/** */
|
||||
/** Build a formatter object based on the given format string.
|
||||
* The actual implementation is delegated to an boost::format object,
|
||||
* which is placement-constructed into an opaque buffer embedded into
|
||||
* this object. Defining the necessary size for this buffer relies
|
||||
* on a implementation details of boost::format (and might break)
|
||||
*/
|
||||
_Fmt::_Fmt (string formatString)
|
||||
{
|
||||
BOOST_STATIC_ASSERT (sizeof(boost::format) <= FORMATTER_SIZE);
|
||||
|
||||
new(formatter_) boost::format(formatString);
|
||||
suppressInsufficientArgumentErrors (formatter_);
|
||||
}
|
||||
try {
|
||||
BOOST_STATIC_ASSERT (sizeof(boost::format) <= FORMATTER_SIZE);
|
||||
|
||||
new(formatter_) boost::format(formatString);
|
||||
suppressInsufficientArgumentErrors (formatter_);
|
||||
}
|
||||
catch (boost::io::bad_format_string& syntaxError)
|
||||
{
|
||||
throw lumiera::error::Fatal (syntaxError
|
||||
, _Fmt("Format string '%s' is broken") % formatString
|
||||
, LUMIERA_ERROR_FORMAT_SYNTAX);
|
||||
}
|
||||
|
||||
|
||||
_Fmt::~_Fmt ()
|
||||
{
|
||||
|
|
@ -152,6 +166,7 @@ namespace util {
|
|||
}
|
||||
catch (std::exception& failure)
|
||||
{
|
||||
_clear_errorflag();
|
||||
WARN (progress, "Format: Parameter '%s' causes problems: %s"
|
||||
, cStr(str(val))
|
||||
, failure.what());
|
||||
|
|
@ -159,6 +174,7 @@ namespace util {
|
|||
}
|
||||
catch (...)
|
||||
{
|
||||
_clear_errorflag();
|
||||
WARN (progress, "Format: Unexpected problems accepting format parameter '%s'", cStr(str(val)));
|
||||
pushFailsafeReplacement (formatter);
|
||||
}
|
||||
|
|
@ -198,11 +214,13 @@ namespace util {
|
|||
|
||||
catch (std::exception& failure)
|
||||
{
|
||||
_clear_errorflag();
|
||||
WARN (progress, "Format: Failure to receive formatted result: %s", failure.what());
|
||||
return "<formatting failure>";
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_clear_errorflag();
|
||||
WARN (progress, "Format: Unexpected problems while formatting output.");
|
||||
return "<unexpected problems>";
|
||||
}
|
||||
|
|
@ -222,15 +240,20 @@ namespace util {
|
|||
|
||||
catch(std::exception& failure)
|
||||
{
|
||||
_clear_errorflag();
|
||||
WARN (progress, "Format: Failure when outputting formatted result: %s", failure.what());
|
||||
return os << "<formatting failure>";
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
_clear_errorflag();
|
||||
WARN (progress, "Format: Unexpected problems while producing formatted output.");
|
||||
return os << "<unexpected problems>";
|
||||
}
|
||||
|
||||
|
||||
LUMIERA_ERROR_DEFINE (FORMAT_SYNTAX, "Syntax error in format string for boost::format");
|
||||
|
||||
|
||||
|
||||
} // namespace util
|
||||
|
|
|
|||
|
|
@ -25,19 +25,77 @@
|
|||
** Front-end for printf-style string template interpolation.
|
||||
** While the actual implementation just delegates to boost::format, this front-end
|
||||
** hides the direct dependency, additionally invokes a custom toSting conversion
|
||||
** whenever possible, provides a direct conversion to string and catches any exceptions.
|
||||
** whenever possible, provides a direct automatic conversion to the formatted result
|
||||
** to string and catches any exceptions.
|
||||
**
|
||||
** This front-end is used pervasively for diagnostics and logging, so keeping down the
|
||||
** compilation and object size cost and reliably handling any error is more important
|
||||
** than the (small) performance gain of directly invoking boost::format (which is
|
||||
** known to be 10 times slower than printf anyway).
|
||||
**
|
||||
** @remarks The implementation is invoked through a set of explicit specialisations.
|
||||
** For custom types, we prefer to invoke operator string(), which is determined
|
||||
** by a directly coded metaprogramming test. The compile time and object size
|
||||
** overhead incurred by using this header was verified to be negligible.
|
||||
** \par Implementation notes
|
||||
** To perform the formatting, usually a \c _Fmt object is created as an anonymous
|
||||
** temporary, but it may as well be stored into a variable. Copying is not permitted.
|
||||
** Individual parameters are then fed for formatting through the \c '%' operator.
|
||||
** Each instance of _Fmt uses its own embedded boost::format object for implementation,
|
||||
** but this formatter resides within an opaque buffer embedded into the frontend object.
|
||||
** The rationale for this admittedly tricky approach is to confine any usage of boost::format
|
||||
** to the implementation translation unit (format-string.cpp).
|
||||
**
|
||||
** The implementation is invoked by the frontend through a set of explicit specialisations
|
||||
** for all the relevant \em primitive data types. For custom types, we prefer to invoke
|
||||
** operator string() if possible, which is determined by a simple metaprogramming test,
|
||||
** which is defined in lib/meta/util.pp, without relying on boost. As a fallback, for
|
||||
** all other types without built-in or custom string conversion, we use the mangled
|
||||
** type string produced by RTTI.
|
||||
**
|
||||
** The compile time and object size overhead incurred by using this header was verified
|
||||
** to be negligible, in comparison to using boost::format. When compiling a demo example
|
||||
** on x86_64, the following executable sizes could be observed:
|
||||
**
|
||||
** debug stripped
|
||||
** just string concatenation ............... 42k 8.8k
|
||||
** including and using format-string.hpp ... 50k 9.4k
|
||||
** including and using boost::format ....... 420k 140k
|
||||
**
|
||||
** In addition, we need to take the implementation translation unit (format-string.cpp)
|
||||
** into account, which is required once per application and contains the specialisations
|
||||
** for all primitive types. In the test showed above, the corresponding object file
|
||||
** had a size of 1300k (with debug information) resp. 290k (stripped).
|
||||
**
|
||||
** \par Usage
|
||||
** The syntax of the format string is defined by boost::format and closely mimics
|
||||
** the printf formatting directives. The notable difference is that boost::format
|
||||
** uses the C++ stream output framework, and thus avoiding the perils of printf.
|
||||
** The individual formatting placeholders just set the corresponding flags on
|
||||
** an embedded string stream, thus the actual parameter types cause the
|
||||
** selection of a suitable format, not the definitions within the
|
||||
** format string.
|
||||
**
|
||||
** An illegal format string will raise an error::Fatal. Any other error during usage of
|
||||
** the formatter is caught, logged and suppressed, inserting an error indicator into
|
||||
** the formatted result instead
|
||||
**
|
||||
** A formatter is usually created as an anonymous object, at places where a string
|
||||
** is expected. An arbitrary number of parameters is then supplied using the \c '%' operator.
|
||||
** The result can be obtained
|
||||
** - by string conversion
|
||||
** - by feeding into an output stream.
|
||||
**
|
||||
** Code example:
|
||||
** \code
|
||||
** double total = 22.9499;
|
||||
** const char * currency = "€";
|
||||
** cout << _Fmt("price %+5.2f %s") % total % currency << endl;
|
||||
** \endcode
|
||||
**
|
||||
** @remarks See the unit-test for extensive usage examples and corner cases.
|
||||
** The header format-util.hpp provides an alternative string conversion,
|
||||
** using a bit of boost type traits and lexical_cast, but no boost::format.
|
||||
** @warning not suited for performance critical code. About 10 times slower than printf.
|
||||
**
|
||||
** @see FormatString_test
|
||||
** @see format-helper
|
||||
** @see format-util.hpp
|
||||
**
|
||||
*/
|
||||
|
||||
|
|
@ -71,26 +129,33 @@ namespace std { // forward declaration to avoid including <iostream>
|
|||
|
||||
namespace util {
|
||||
|
||||
typedef unsigned char uchar;
|
||||
|
||||
using boost::enable_if;
|
||||
using std::string;
|
||||
|
||||
using boost::enable_if;
|
||||
|
||||
typedef unsigned char uchar;
|
||||
|
||||
LUMIERA_ERROR_DECLARE (FORMAT_SYNTAX); ///< "Syntax error in format string for boost::format"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @todo write type comment
|
||||
* A front-end for using printf-style formatting.
|
||||
* Values to be formatted can be supplied through the
|
||||
* operator%. Custom defined string conversions on objects
|
||||
* will be used, any errors while invoking the format operation
|
||||
* will be suppressed. The implementation is based on boost::format,
|
||||
* but kept opaque to keep code size and compilation times down.
|
||||
* @see FormatString_test
|
||||
*/
|
||||
class _Fmt
|
||||
: boost::noncopyable
|
||||
{
|
||||
/** size of an internal implementation Buffer */
|
||||
/** size of an opaque implementation Buffer */
|
||||
enum{ FORMATTER_SIZE = lib::meta::SizeTrait::BOOST_FORMAT };
|
||||
|
||||
typedef char Implementation[FORMATTER_SIZE];
|
||||
|
||||
|
||||
/** @internal buffer to hold a boost::format */
|
||||
mutable Implementation formatter_;
|
||||
|
||||
|
|
@ -143,9 +208,10 @@ namespace util {
|
|||
* \par type specific treatment
|
||||
* Basic types (numbers, chars, strings) are passed to the implementation
|
||||
* (= boost::format) literally. For custom types, we try to use a custom
|
||||
* string conversion, if applicable. Any other type gets just translated
|
||||
* into a type-ID (using the mangled RTTI info). In case of errors during
|
||||
* the conversion, a string representation of the error is returned
|
||||
* string conversion, if applicable. Non-NULL pointers will be dereferenced,
|
||||
* with the exception of C-Strings and \c void*. Any other type gets just
|
||||
* translated into a type-ID (using the mangled RTTI info). In case of errors
|
||||
* during the conversion, a string representation of the error is returned
|
||||
* @param val arbitrary value or pointer to be included into the result
|
||||
* @warning you need to provide exactly the right number of parameters,
|
||||
* i.e. matching the number of fields in the format string.
|
||||
|
|
@ -203,9 +269,17 @@ namespace util {
|
|||
};
|
||||
|
||||
|
||||
inline void
|
||||
_clear_errorflag()
|
||||
{
|
||||
const char* errID = lumiera_error();
|
||||
TRACE_IF (errID, progress, "Lumiera errorstate '%s' cleared.", errID);
|
||||
}
|
||||
|
||||
inline string
|
||||
_log_and_stringify (std::exception const& ex)
|
||||
{
|
||||
_clear_errorflag();
|
||||
WARN (progress, "Error while invoking custom string conversion: %s", ex.what());
|
||||
try {
|
||||
return string("<string conversion failed: ")+ex.what()+">";
|
||||
|
|
@ -276,7 +350,7 @@ namespace util {
|
|||
}
|
||||
};
|
||||
|
||||
/** some custom types explicitly provide string representation */
|
||||
/** some custom types explicitly provide a string representation */
|
||||
template<typename VAL>
|
||||
struct _Fmt::Converter<VAL, typename enable_if< _shall_convert_toString<VAL> >::type>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -329,7 +329,18 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
PLANNED "formatting by string template" FormatString_test <<END
|
||||
TEST "formatting by string template" FormatString_test <<END
|
||||
out-lit: --format-template--int=0012--double=+1.23--string=Lumiera --
|
||||
out: 0x....+ _____ .
|
||||
out: «.+util.+test.+Silent.»
|
||||
out-lit: __nix_
|
||||
out-lit: ____
|
||||
out-lit: __1__
|
||||
out-lit: __1__
|
||||
out-lit: __↯__
|
||||
out-lit: __dirt__
|
||||
out-lit: __1234__
|
||||
out-lit: __0xff__
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
|
@ -394,7 +405,7 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
TEST "metaprogramming helpers" Metautils_test <<END
|
||||
TEST "metaprogramming helpers" MetaUtils_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/format-string.hpp"
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
|
@ -263,6 +264,8 @@ namespace test {
|
|||
cout << _Fmt("__%d__") % "dirt" << endl;
|
||||
cout << _Fmt("__%d__") % "1234" << endl;
|
||||
cout << _Fmt("__%d__") % "0xff" << endl;
|
||||
|
||||
VERIFY_ERROR(FORMAT_SYNTAX, _Fmt("%broken"));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ namespace test{
|
|||
FixedFrameQuantiser fixQ(25);
|
||||
|
||||
uint frames = (rand() % MAX_FRAMES);
|
||||
FSecs dirt = (F25 / (1 + rand() % DIRT_GRAIN));
|
||||
FSecs dirt = (F25 / (2 + rand() % DIRT_GRAIN));
|
||||
|
||||
Time rawTime (dirt + frames*F25);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue