2011-12-27 06:14:46 +01:00
|
|
|
|
/*
|
2023-05-23 01:06:22 +02:00
|
|
|
|
FormatString - string template formatting based on boost::format
|
2011-12-27 06:14:46 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
2011-12-27 06:14:46 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**Lumiera** is free software; you can redistribute it and/or modify it
|
|
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
|
|
Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
|
|
option) any later version. See the file COPYING for further details.
|
2011-12-27 06:14:46 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
* *****************************************************************/
|
2011-12-27 06:14:46 +01:00
|
|
|
|
|
|
|
|
|
|
/** @file format-string.cpp
|
|
|
|
|
|
** 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
|
2011-12-31 06:38:31 +01:00
|
|
|
|
** format-string.hpp provides some template functions and classes,
|
|
|
|
|
|
** either invoking a custom string conversion, or passing primitive
|
|
|
|
|
|
** values down unaltered.
|
|
|
|
|
|
**
|
2011-12-27 06:14:46 +01:00
|
|
|
|
** Here, we define explicit specialisations for the frontend to invoke,
|
|
|
|
|
|
** which in turn just pass on the given argument value to the embedded
|
2011-12-31 06:38:31 +01:00
|
|
|
|
** boost::format object, which in turn dumps the formatted result
|
2011-12-27 06:14:46 +01:00
|
|
|
|
** into an embedded string stream.
|
|
|
|
|
|
**
|
|
|
|
|
|
** To avoid exposing boost::format in the frontend header, we use an
|
|
|
|
|
|
** opaque buffer of appropriate size to piggyback the formatter object.
|
|
|
|
|
|
** @warning unfortunately this requires a hard coded buffer size constant
|
|
|
|
|
|
** in the front-end, which we define there manually, based on
|
|
|
|
|
|
** the current implementation layout found in the boost libraries.
|
|
|
|
|
|
** If Boost eventually changes the implementation, the assertion
|
|
|
|
|
|
** in our constructor will trigger.
|
|
|
|
|
|
**
|
|
|
|
|
|
** @see FormatString_test
|
|
|
|
|
|
**
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/error.hpp"
|
2011-12-30 20:20:11 +01:00
|
|
|
|
#include "lib/format-util.hpp"
|
2011-12-27 06:14:46 +01:00
|
|
|
|
#include "lib/format-string.hpp"
|
|
|
|
|
|
|
2011-12-28 05:44:57 +01:00
|
|
|
|
#include <boost/static_assert.hpp>
|
2011-12-27 06:14:46 +01:00
|
|
|
|
#include <boost/format.hpp>
|
|
|
|
|
|
#include <iostream>
|
2023-12-02 23:56:46 +01:00
|
|
|
|
#include <cstddef>
|
|
|
|
|
|
#include <new>
|
2011-12-27 06:14:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace util {
|
|
|
|
|
|
|
2011-12-28 06:40:51 +01:00
|
|
|
|
using boost::format;
|
|
|
|
|
|
|
2011-12-31 06:38:31 +01:00
|
|
|
|
|
2011-12-27 06:14:46 +01:00
|
|
|
|
namespace { // implementation details...
|
|
|
|
|
|
|
2011-12-28 06:40:51 +01:00
|
|
|
|
inline boost::format&
|
2023-12-02 23:56:46 +01:00
|
|
|
|
accessImpl (std::byte* buffer)
|
2011-12-28 06:40:51 +01:00
|
|
|
|
{
|
2023-12-02 23:56:46 +01:00
|
|
|
|
return * std::launder (reinterpret_cast<boost::format*> (buffer));
|
2011-12-28 06:40:51 +01:00
|
|
|
|
}
|
2011-12-27 06:14:46 +01:00
|
|
|
|
|
2011-12-31 01:30:07 +01:00
|
|
|
|
|
2011-12-31 03:52:10 +01:00
|
|
|
|
inline void
|
2023-12-02 23:56:46 +01:00
|
|
|
|
destroyImpl (std::byte* buffer)
|
2011-12-31 03:52:10 +01:00
|
|
|
|
{
|
2023-05-23 01:06:22 +02:00
|
|
|
|
accessImpl(buffer).~format();
|
2011-12-31 03:52:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-12-30 20:20:11 +01:00
|
|
|
|
/** in case the formatting of a (primitive) value fails,
|
2011-12-31 06:38:31 +01:00
|
|
|
|
* we try to supply an error indicator instead */
|
2011-12-31 01:30:07 +01:00
|
|
|
|
void
|
2023-12-02 23:56:46 +01:00
|
|
|
|
pushFailsafeReplacement (std::byte* formatter, const char* errorMsg =NULL)
|
2011-12-30 20:20:11 +01:00
|
|
|
|
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
|
2023-12-02 23:56:46 +01:00
|
|
|
|
suppressInsufficientArgumentErrors (std::byte* formatter)
|
2011-12-30 20:20:11 +01:00
|
|
|
|
{
|
|
|
|
|
|
using namespace boost::io;
|
2023-05-23 01:06:22 +02:00
|
|
|
|
accessImpl(formatter).exceptions (all_error_bits ^ too_few_args_bit);
|
2011-12-30 20:20:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-12-27 06:14:46 +01:00
|
|
|
|
}//(End) implementation details
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-12-30 20:20:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-12-31 06:38:31 +01:00
|
|
|
|
/** 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
|
2016-01-10 11:21:34 +01:00
|
|
|
|
* on implementation details of boost::format (and might break)
|
|
|
|
|
|
* @see lib::meta::SizeTrait::BOOST_FORMAT
|
2011-12-31 06:38:31 +01:00
|
|
|
|
*/
|
2011-12-27 06:14:46 +01:00
|
|
|
|
_Fmt::_Fmt (string formatString)
|
2011-12-31 06:38:31 +01:00
|
|
|
|
try {
|
2016-01-10 11:21:34 +01:00
|
|
|
|
static_assert (sizeof(boost::format) <= FORMATTER_SIZE,
|
|
|
|
|
|
"opaque working buffer insufficient "
|
|
|
|
|
|
"to hold a boost::format instance. "
|
|
|
|
|
|
"Maybe boost implementation change. "
|
|
|
|
|
|
"Please verify lib/meta/size-trait.hpp");
|
2011-12-31 06:38:31 +01:00
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-12-28 06:40:51 +01:00
|
|
|
|
|
|
|
|
|
|
_Fmt::~_Fmt ()
|
|
|
|
|
|
{
|
2011-12-31 03:52:10 +01:00
|
|
|
|
destroyImpl (formatter_);
|
2011-12-27 06:14:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-12-31 03:52:10 +01:00
|
|
|
|
/** @internal access point for the frontend,
|
|
|
|
|
|
* allowing to push a single parameter value down
|
|
|
|
|
|
* into the implementation for the actual formatting.
|
|
|
|
|
|
* Only selected primitive types are handled directly this way,
|
|
|
|
|
|
* while all custom conversions, the handling of pointers and the
|
|
|
|
|
|
* fallback (showing a type string) is done in the frontend.
|
2011-12-30 06:10:47 +01:00
|
|
|
|
* @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''
|
|
|
|
|
|
* for template instantiation, which means there is no compiler magic
|
|
|
|
|
|
* involved and a template function either needs to be \em defined in
|
|
|
|
|
|
* a header or explicitly instantiated in some translation unit.
|
|
|
|
|
|
*/
|
2011-12-27 07:43:39 +01:00
|
|
|
|
template<typename VAL>
|
|
|
|
|
|
void
|
2011-12-31 03:52:10 +01:00
|
|
|
|
_Fmt::format (const VAL val, Implementation& formatter)
|
|
|
|
|
|
try {
|
|
|
|
|
|
accessImpl(formatter) % val;
|
|
|
|
|
|
}
|
2011-12-27 07:43:39 +01:00
|
|
|
|
|
2011-12-31 03:52:10 +01:00
|
|
|
|
catch (boost::io::too_many_args& argErr)
|
|
|
|
|
|
{
|
2016-01-08 08:20:59 +01:00
|
|
|
|
WARN (progress, "Format: excess argument '%s' of type «%s» ignored."
|
2016-01-08 09:14:46 +01:00
|
|
|
|
, cStr(toString(val))
|
2016-01-08 08:20:59 +01:00
|
|
|
|
, cStr(typeStr(val)));
|
2011-12-31 03:52:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
catch (std::exception& failure)
|
|
|
|
|
|
{
|
2011-12-31 06:38:31 +01:00
|
|
|
|
_clear_errorflag();
|
2011-12-31 03:52:10 +01:00
|
|
|
|
WARN (progress, "Format: Parameter '%s' causes problems: %s"
|
2016-01-08 09:14:46 +01:00
|
|
|
|
, cStr(toString(val))
|
2011-12-31 03:52:10 +01:00
|
|
|
|
, failure.what());
|
|
|
|
|
|
pushFailsafeReplacement (formatter, failure.what());
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (...)
|
|
|
|
|
|
{
|
2011-12-31 06:38:31 +01:00
|
|
|
|
_clear_errorflag();
|
2016-01-08 09:14:46 +01:00
|
|
|
|
WARN (progress, "Format: Unexpected problems accepting format parameter '%s'", cStr(toString(val)));
|
2011-12-31 03:52:10 +01:00
|
|
|
|
pushFailsafeReplacement (formatter);
|
|
|
|
|
|
}
|
2011-12-27 07:43:39 +01:00
|
|
|
|
|
2011-12-30 05:13:27 +01:00
|
|
|
|
|
|
|
|
|
|
|
2011-12-27 07:43:39 +01:00
|
|
|
|
|
|
|
|
|
|
/* ===== explicitly supported =================== */
|
|
|
|
|
|
|
2011-12-31 03:52:10 +01:00
|
|
|
|
template void _Fmt::format (const char, Implementation&);
|
|
|
|
|
|
template void _Fmt::format (const uchar, Implementation&);
|
2013-11-09 02:30:16 +01:00
|
|
|
|
template void _Fmt::format (const int16_t, Implementation&);
|
|
|
|
|
|
template void _Fmt::format (const uint16_t,Implementation&);
|
|
|
|
|
|
template void _Fmt::format (const int32_t, Implementation&);
|
|
|
|
|
|
template void _Fmt::format (const uint32_t,Implementation&);
|
2011-12-31 03:52:10 +01:00
|
|
|
|
template void _Fmt::format (const int64_t, Implementation&);
|
|
|
|
|
|
template void _Fmt::format (const uint64_t,Implementation&);
|
|
|
|
|
|
template void _Fmt::format (const float, Implementation&);
|
|
|
|
|
|
template void _Fmt::format (const double, Implementation&);
|
|
|
|
|
|
template void _Fmt::format (const string, Implementation&);
|
|
|
|
|
|
template void _Fmt::format (const void *, Implementation&);
|
|
|
|
|
|
template void _Fmt::format (const char *, Implementation&);
|
2013-11-09 02:30:16 +01:00
|
|
|
|
#ifndef __x86_64__
|
|
|
|
|
|
template void _Fmt::format (const long, Implementation&);
|
|
|
|
|
|
template void _Fmt::format (const ulong, Implementation&);
|
|
|
|
|
|
#endif
|
2011-12-27 07:43:39 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-12-30 06:10:47 +01:00
|
|
|
|
/** @remarks usually the _Fmt helper is used inline
|
|
|
|
|
|
* at places where a string is expected. The '%' operator
|
|
|
|
|
|
* used to fed parameters has a higher precedence than the
|
|
|
|
|
|
* assignment or comparison operators, ensuring that all parameters
|
|
|
|
|
|
* are evaluated and formatted prior to receiving the formatted result
|
2012-11-25 02:04:19 +01:00
|
|
|
|
* @note EX_FREE
|
2011-12-30 06:10:47 +01:00
|
|
|
|
*/
|
2011-12-27 06:14:46 +01:00
|
|
|
|
_Fmt::operator string() const
|
2011-12-30 20:20:11 +01:00
|
|
|
|
try {
|
|
|
|
|
|
return accessImpl(formatter_).str();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
catch (std::exception& failure)
|
|
|
|
|
|
{
|
2011-12-31 06:38:31 +01:00
|
|
|
|
_clear_errorflag();
|
2011-12-30 20:20:11 +01:00
|
|
|
|
WARN (progress, "Format: Failure to receive formatted result: %s", failure.what());
|
|
|
|
|
|
return "<formatting failure>";
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (...)
|
|
|
|
|
|
{
|
2011-12-31 06:38:31 +01:00
|
|
|
|
_clear_errorflag();
|
2011-12-30 20:20:11 +01:00
|
|
|
|
WARN (progress, "Format: Unexpected problems while formatting output.");
|
|
|
|
|
|
return "<unexpected problems>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-12-27 06:14:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
2011-12-30 06:10:47 +01:00
|
|
|
|
/** send the formatted buffer directly to the output stream.
|
|
|
|
|
|
* @note this is more efficient than creating a string and outputting that,
|
2011-12-30 20:20:11 +01:00
|
|
|
|
* because boost::format internally uses a string-stream to generate
|
2011-12-30 06:10:47 +01:00
|
|
|
|
* the formatted representation, relying on the C++ output framework
|
|
|
|
|
|
*/
|
2011-12-27 06:14:46 +01:00
|
|
|
|
std::ostream&
|
|
|
|
|
|
operator<< (std::ostream& os, _Fmt const& fmt)
|
2011-12-30 20:20:11 +01:00
|
|
|
|
try {
|
|
|
|
|
|
return os << accessImpl(fmt.formatter_);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
catch(std::exception& failure)
|
|
|
|
|
|
{
|
2011-12-31 06:38:31 +01:00
|
|
|
|
_clear_errorflag();
|
2011-12-30 20:20:11 +01:00
|
|
|
|
WARN (progress, "Format: Failure when outputting formatted result: %s", failure.what());
|
|
|
|
|
|
return os << "<formatting failure>";
|
|
|
|
|
|
}
|
|
|
|
|
|
catch(...)
|
|
|
|
|
|
{
|
2011-12-31 06:38:31 +01:00
|
|
|
|
_clear_errorflag();
|
2011-12-30 20:20:11 +01:00
|
|
|
|
WARN (progress, "Format: Unexpected problems while producing formatted output.");
|
|
|
|
|
|
return os << "<unexpected problems>";
|
|
|
|
|
|
}
|
2011-12-27 06:14:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
2011-12-31 06:38:31 +01:00
|
|
|
|
LUMIERA_ERROR_DEFINE (FORMAT_SYNTAX, "Syntax error in format string for boost::format");
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-12-27 06:14:46 +01:00
|
|
|
|
|
|
|
|
|
|
} // namespace util
|