Disabled external gits

This commit is contained in:
2022-04-07 18:46:57 +02:00
parent 88cb3426ad
commit 15e7120d6d
5316 changed files with 4563444 additions and 6 deletions

3
cs440-acg/ext/tinyformat/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
_bloat_test_tmp*
tinyformat_test_cxx*
tinyformat_speed_test

View File

@@ -0,0 +1,455 @@
============
tinyformat.h
============
----------------------------------------
Changes in this fork
----------------------------------------
This is a fork of tinyformat by Chris Foster where all C++98-specific workarounds have
been removed for an even tinier implementation. The original documentation follows below.
----------------------------------------
A minimal type safe printf() replacement
----------------------------------------
**tinyformat.h** is a type safe printf replacement library in a single C++
header file. If you've ever wanted ``printf("%s", s)`` to just work regardless
of the type of ``s``, tinyformat might be for you. Design goals include:
* Type safety and extensibility for user defined types.
* C99 ``printf()`` compatibility, to the extent possible using ``std::ostream``
* Simplicity and minimalism. A single header file to include and distribute
with your projects.
* Augment rather than replace the standard stream formatting mechanism
* C++98 support, with optional C++11 niceties
Example usage
-------------
To print a date to ``std::cout``::
std::string weekday = "Wednesday";
const char* month = "July";
size_t day = 27;
long hour = 14;
int min = 44;
tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min);
The strange types here emphasize the type safety of the interface, for example
it is possible to print a ``std::string`` using the ``"%s"`` conversion, and a
``size_t`` using the ``"%d"`` conversion. A similar result could be achieved
using either of the ``tfm::format()`` functions. One prints on a user provided
stream::
tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n",
weekday, month, day, hour, min);
The other returns a ``std::string``::
std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n",
weekday, month, day, hour, min);
std::cout << date;
It is safe to use tinyformat inside a template function. For any type which
has the usual stream insertion ``operator<<`` defined, the following will work
as desired::
template<typename T>
void myPrint(const T& value)
{
tfm::printf("My value is '%s'\n", value);
}
(The above is a compile error for types ``T`` without a stream insertion
operator.)
Function reference
------------------
All user facing functions are defined in the namespace ``tinyformat``. A
namespace alias ``tfm`` is provided to encourage brevity, but can easily be
disabled if desired.
Three main interface functions are available: an iostreams-based ``format()``,
a string-based ``format()`` and a ``printf()`` replacement. These functions
can be thought of as C++ replacements for C's ``fprintf()``, ``sprintf()`` and
``printf()`` functions respectively. All the interface functions can take an
unlimited number of input arguments if compiled with C++11 variadic templates
support. In C++98 mode, the number of arguments must be limited to some fixed
upper bound which is currently 16 as of version 1.3 [#]_.
The ``format()`` function which takes a stream as the first argument is the
main part of the tinyformat interface. ``stream`` is the output stream,
``formatString`` is a format string in C99 ``printf()`` format, and the values
to be formatted have arbitrary types::
template<typename... Args>
void format(std::ostream& stream, const char* formatString,
const Args&... args);
The second version of ``format()`` is a convenience function which returns a
``std::string`` rather than printing onto a stream. This function simply
calls the main version of ``format()`` using a ``std::ostringstream``, and
returns the resulting string::
template<typename... Args>
std::string format(const char* formatString, const Args&... args);
Finally, ``printf()`` and ``printfln()`` are convenience functions which call
``format()`` with ``std::cout`` as the first argument; both have the same
signature::
template<typename... Args>
void printf(const char* formatString, const Args&... args);
``printfln()`` is the same as ``printf()`` but appends an additional newline
for convenience - a concession to the author's tendency to forget the newline
when using the library for simple logging.
.. [#] Generating the code to support more arguments is quite easy using the
in-source code generator based on the excellent code generation script
``cog.py`` (http://nedbatchelder.com/code/cog): Set the ``maxParams``
parameter in the code generator and rerun cog using
``cog.py -r tinyformat.h``. Alternatively, it should be quite easy to simply
add extra versions of the associated macros by hand.
Format strings and type safety
------------------------------
Tinyformat parses C99 format strings to guide the formatting process --- please
refer to any standard C99 printf documentation for format string syntax. In
contrast to printf, tinyformat does not use the format string to decide on
the type to be formatted so this does not compromise the type safety: *you may
use any format specifier with any C++ type*. The author suggests standardising
on the ``%s`` conversion unless formatting numeric types.
Let's look at what happens when you execute the function call::
tfm::format(outStream, "%+6.4f", yourType);
First, the library parses the format string, and uses it to modify the state of
``outStream``:
1. The ``outStream`` formatting flags are cleared and the width, precision and
fill reset to the default.
2. The flag ``'+'`` means to prefix positive numbers with a ``'+'``; tinyformat
executes ``outStream.setf(std::ios::showpos)``
3. The number 6 gives the field width; execute ``outStream.width(6)``.
4. The number 4 gives the precision; execute ``outStream.precision(4)``.
5. The conversion specification character ``'f'`` means that floats should be
formatted with a fixed number of digits; this corresponds to executing
``outStream.setf(std::ios::fixed, std::ios::floatfield);``
After all these steps, tinyformat executes::
outStream << yourType;
and finally restores the stream flags, precision and fill.
What happens if ``yourType`` isn't actually a floating point type? In this
case the flags set above are probably irrelevant and will be ignored by the
underlying ``std::ostream`` implementation. The field width of six may cause
some padding in the output of ``yourType``, but that's about it.
Special cases for "%p", "%c" and "%s"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tinyformat normally uses ``operator<<`` to convert types to strings. However,
the "%p" and "%c" conversions require special rules for robustness. Consider::
uint8_t* pixels = get_pixels(/* ... */);
tfm::printf("%p", pixels);
Clearly the intention here is to print a representation of the *pointer* to
``pixels``, but since ``uint8_t`` is a character type the compiler would
attempt to print it as a C string if we blindly fed it into ``operator<<``. To
counter this kind of madness, tinyformat tries to static_cast any type fed to
the "%p" conversion into a ``const void*`` before printing. If this can't be
done at compile time the library falls back to using ``operator<<`` as usual.
The "%c" conversion has a similar problem: it signifies that the given integral
type should be converted into a ``char`` before printing. The solution is
identical: attempt to convert the provided type into a char using
``static_cast`` if possible, and if not fall back to using ``operator<<``.
The "%s" conversion sets the boolalpha flag on the formatting stream. This
means that a ``bool`` variable printed with "%s" will come out as ``true`` or
``false`` rather than the ``1`` or ``0`` that you would otherwise get.
Incompatibilities with C99 printf
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Not all features of printf can be simulated simply using standard iostreams.
Here's a list of known incompatibilities:
* The C99 ``"%a"`` and ``"%A"`` hexadecimal floating point conversions are not
supported since the iostreams don't have the necessary flags. Using either
of these flags will result in a call to ``TINYFORMAT_ERROR``.
* The precision for integer conversions cannot be supported by the iostreams
state independently of the field width. (Note: **this is only a
problem for certain obscure integer conversions**; float conversions like
``%6.4f`` work correctly.) In tinyformat the field width takes precedence,
so the 4 in ``%6.4d`` will be ignored. However, if the field width is not
specified, the width used internally is set equal to the precision and padded
with zeros on the left. That is, a conversion like ``%.4d`` effectively
becomes ``%04d`` internally. This isn't correct for every case (eg, negative
numbers end up with one less digit than desired) but it's about the closest
simple solution within the iostream model.
* The ``"%n"`` query specifier isn't supported to keep things simple and will
result in a call to ``TINYFORMAT_ERROR``.
* The ``"%ls"`` conversion is not supported, and attempting to format a
``wchar_t`` array will cause a compile time error to minimise unexpected
surprises. If you know the encoding of your wchar_t strings, you could write
your own ``std::ostream`` insertion operator for them, and disable the
compile time check by defining the macro ``TINYFORMAT_ALLOW_WCHAR_STRINGS``.
If you want to print the *address* of a wide character with the ``"%p"``
conversion, you should cast it to a ``void*`` before passing it to one of the
formatting functions.
Error handling
--------------
By default, tinyformat calls ``assert()`` if it encounters an error in the
format string or number of arguments. This behaviour can be changed (for
example, to throw an exception) by defining the ``TINYFORMAT_ERROR`` macro
before including tinyformat.h, or editing the config section of the header.
Formatting user defined types
-----------------------------
User defined types with a stream insertion operator will be formatted using
``operator<<(std::ostream&, T)`` by default. The ``"%s"`` format specifier is
suggested for user defined types, unless the type is inherently numeric.
For further customization, the user can override the ``formatValue()``
function to specify formatting independently of the stream insertion operator.
If you override this function, the library will have already parsed the format
specification and set the stream flags accordingly - see the source for details.
Wrapping tfm::format() inside a user defined format function
------------------------------------------------------------
Suppose you wanted to define your own function which wraps ``tfm::format``.
For example, consider an error function taking an error code, which in C++11
might be written simply as::
template<typename... Args>
void error(int code, const char* fmt, const Args&... args)
{
std::cerr << "error (code " << code << ")";
tfm::format(std::cerr, fmt, args...);
}
Simulating this functionality in C++98 is pretty painful since it requires
writing out a version of ``error()`` for each desired number of arguments. To
make this bearable tinyformat comes with a set of macros which are used
internally to generate the API, but which may also be used in user code.
The three macros ``TINYFORMAT_ARGTYPES(n)``, ``TINYFORMAT_VARARGS(n)`` and
``TINYFORMAT_PASSARGS(n)`` will generate a list of ``n`` argument types,
type/name pairs and argument names respectively when called with an integer
``n`` between 1 and 16. We can use these to define a macro which generates the
desired user defined function with ``n`` arguments. This should be followed by
a call to ``TINYFORMAT_FOREACH_ARGNUM`` to generate the set of functions for
all supported ``n``::
#define MAKE_ERROR_FUNC(n) \
template<TINYFORMAT_ARGTYPES(n)> \
void error(int code, const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
std::cerr << "error (code " << code << ")"; \
tfm::format(std::cerr, fmt, TINYFORMAT_PASSARGS(n)); \
}
TINYFORMAT_FOREACH_ARGNUM(MAKE_ERROR_FUNC)
Sometimes it's useful to be able to pass a list of format arguments through to
a non-template function. The ``FormatList`` class is provided as a way to do
this by storing the argument list in a type-opaque way. For example::
template<typename... Args>
void error(int code, const char* fmt, const Args&... args)
{
tfm::FormatListRef formatList = tfm::makeFormatList(args...);
errorImpl(code, fmt, formatList);
}
What's interesting here is that ``errorImpl()`` is a non-template function so
it could be separately compiled if desired. The ``FormatList`` instance can be
used via a call to the ``vformat()`` function (the name chosen for semantic
similarity to ``vprintf()``)::
void errorImpl(int code, const char* fmt, tfm::FormatListRef formatList)
{
std::cerr << "error (code " << code << ")";
tfm::vformat(std::cout, fmt, formatList);
}
The construction of a ``FormatList`` instance is very lightweight - it defers
all formatting and simply stores a couple of function pointers and a value
pointer per argument. Since most of the actual work is done inside
``vformat()``, any logic which causes an early exit of ``errorImpl()`` -
filtering of verbose log messages based on error code for example - could be a
useful optimization for programs using tinyformat. (A faster option would be
to write any early bailout code inside ``error()``, though this must be done in
the header.)
Benchmarks
----------
Compile time and code bloat
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The script ``bloat_test.sh`` included in the repository tests whether
tinyformat succeeds in avoiding compile time and code bloat for nontrivial
projects. The idea is to include ``tinyformat.h`` into 100 translation units
and use ``printf()`` five times in each to simulate a medium sized project.
The resulting executable size and compile time (g++-4.8.2, linux ubuntu 14.04)
is shown in the following tables, which can be regenerated using ``make
bloat_test``:
**Non-optimized build**
====================== ================== ==========================
test name compiler wall time executable size (stripped)
====================== ================== ==========================
libc printf 1.8s 48K (36K)
std::ostream 10.7s 96K (76K)
tinyformat, no inlines 18.9s 140K (104K)
tinyformat 21.1s 220K (180K)
tinyformat, c++0x mode 20.7s 220K (176K)
boost::format 70.1s 844K (736K)
====================== ================== ==========================
**Optimized build (-O3 -DNDEBUG)**
====================== ================== ==========================
test name compiler wall time executable size (stripped)
====================== ================== ==========================
libc printf 2.3s 40K (28K)
std::ostream 11.8s 104K (80K)
tinyformat, no inlines 23.0s 128K (104K)
tinyformat 32.9s 128K (104K)
tinyformat, c++0x mode 34.0s 128K (104K)
boost::format 147.9s 644K (600K)
====================== ================== ==========================
For large projects it's arguably worthwhile to do separate compilation of the
non-templated parts of tinyformat, as shown in the rows labelled *tinyformat,
no inlines*. These were generated by putting the implementation of ``vformat``
(``detail::formatImpl()`` etc) it into a separate file, tinyformat.cpp. Note
that the results above can vary considerably with different compilers. For
example, the ``-fipa-cp-clone`` optimization pass in g++-4.6 resulted in
excessively large binaries. On the other hand, the g++-4.8 results are quite
similar to using clang++-3.4.
Speed tests
~~~~~~~~~~~
The following speed tests results were generated by building
``tinyformat_speed_test.cpp`` on an Intel core i7-2600K running Linux Ubuntu
14.04 with g++-4.8.2 using ``-O3 -DNDEBUG``. In the test, the format string
``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` is filled 2000000 times with output sent to
``/dev/null``; for further details see the source and Makefile.
============== ========
test name run time
============== ========
libc printf 1.20s
std::ostream 1.82s
tinyformat 2.08s
boost::format 9.04s
============== ========
It's likely that tinyformat has an advantage over boost.format because it tries
reasonably hard to avoid formatting into temporary strings, preferring instead
to send the results directly to the stream buffer. Tinyformat cannot
be faster than the iostreams because it uses them internally, but it comes
acceptably close.
Rationale
---------
Or, why did I reinvent this particularly well studied wheel?
Nearly every program needs text formatting in some form but in many cases such
formatting is *incidental* to the main purpose of the program. In these cases,
you really want a library which is simple to use but as lightweight as
possible.
The ultimate in lightweight dependencies are the solutions provided by the C++
and C libraries. However, both the C++ iostreams and C's printf() have
well known usability problems: iostreams are hopelessly verbose for complicated
formatting and printf() lacks extensibility and type safety. For example::
// Verbose; hard to read, hard to type:
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
// The alternative using a format string is much easier on the eyes
tfm::printf("%.2f\n", 1.23456);
// Type mismatch between "%s" and int: will cause a segfault at runtime!
printf("%s", 1);
// The following is perfectly fine, and will result in "1" being printed.
tfm::printf("%s", 1);
On the other hand, there are plenty of excellent and complete libraries which
solve the formatting problem in great generality (boost.format and fastformat
come to mind, but there are many others). Unfortunately these kind of
libraries tend to be rather heavy dependencies, far too heavy for projects
which need to do only a little formatting. Problems include
1. Having many large source files. This makes a heavy dependency unsuitable to
bundle within other projects for convenience.
2. Slow build times for every file using any sort of formatting (this is very
noticeable with g++ and boost/format.hpp. I'm not sure about the various
other alternatives.)
3. Code bloat due to instantiating many templates
Tinyformat tries to solve these problems while providing formatting which is
sufficiently general and fast for incidental day to day uses.
License
-------
For minimum license-related fuss, tinyformat.h is distributed under the boost
software license, version 1.0. (Summary: you must keep the license text on
all source copies, but don't have to mention tinyformat when distributing
binaries.)
Author and acknowledgements
---------------------------
Tinyformat was written by Chris Foster, with contributions from various people
as recorded in the git repository.
The implementation owes a lot to ``boost::format`` for showing that it's fairly
easy to use stream based formatting to simulate most of the ``printf()``
syntax. Douglas Gregor's introduction to variadic templates --- see
http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html --- was
also helpful, especially since it solves exactly the ``printf()`` problem for
the case of trivial format strings.
Bugs
----
Here's a list of known bugs which are probably cumbersome to fix:
* Field padding won't work correctly with complicated user defined types. For
general types, the only way to do this correctly seems to be format to a
temporary string stream, check the length, and finally send to the output
stream with padding if necessary. Doing this for all types would be
quite inelegant because it implies extra allocations to make the temporary
stream. A workaround is to add logic to ``operator<<()`` for composite user
defined types so they are aware of the stream field width.

View File

@@ -0,0 +1,775 @@
// tinyformat.h
// Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com]
//
// Boost Software License - Version 1.0
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//------------------------------------------------------------------------------
// Tinyformat: A minimal type safe printf replacement
//
// tinyformat.h is a type safe printf replacement library in a single C++
// header file. Design goals include:
//
// * Type safety and extensibility for user defined types.
// * C99 printf() compatibility, to the extent possible using std::ostream
// * Simplicity and minimalism. A single header file to include and distribute
// with your projects.
// * Augment rather than replace the standard stream formatting mechanism
// * C++98 support, with optional C++11 niceties
//
//
// Main interface example usage
// ----------------------------
//
// To print a date to std::cout:
//
// std::string weekday = "Wednesday";
// const char* month = "July";
// size_t day = 27;
// long hour = 14;
// int min = 44;
//
// tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min);
//
// The strange types here emphasize the type safety of the interface; it is
// possible to print a std::string using the "%s" conversion, and a
// size_t using the "%d" conversion. A similar result could be achieved
// using either of the tfm::format() functions. One prints on a user provided
// stream:
//
// tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n",
// weekday, month, day, hour, min);
//
// The other returns a std::string:
//
// std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n",
// weekday, month, day, hour, min);
// std::cout << date;
//
// These are the three primary interface functions. There is also a
// convenience function printfln() which appends a newline to the usual result
// of printf() for super simple logging.
//
//
// User defined format functions
// -----------------------------
//
// Sometimes it's useful to be able to pass a list of format arguments through
// to a non-template function. The FormatList class is provided as a way to do
// this by storing the argument list in a type-opaque way. Continuing the
// example from above, we construct a FormatList using makeFormatList():
//
// FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min);
//
// The format list can now be passed into any non-template function and used
// via a call to the vformat() function:
//
// tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList);
//
//
// Additional API information
// --------------------------
//
// Error handling: Define TINYFORMAT_ERROR to customize the error handling for
// format strings which are unsupported or have the wrong number of format
// specifiers (calls assert() by default).
//
// User defined types: Uses operator<< for user defined types by default.
// Overload formatValue() for more control.
#ifndef TINYFORMAT_H_INCLUDED
#define TINYFORMAT_H_INCLUDED
namespace tinyformat {}
//------------------------------------------------------------------------------
// Config section. Customize to your liking!
// Namespace alias to encourage brevity
namespace tfm = tinyformat;
// Error handling; calls assert() by default.
// #define TINYFORMAT_ERROR(reasonString) your_error_handler(reasonString)
//------------------------------------------------------------------------------
// Implementation details.
#include <algorithm>
#include <cassert>
#include <iostream>
#include <sstream>
#ifndef TINYFORMAT_ERROR
# define TINYFORMAT_ERROR(reason) assert(0 && reason)
#endif
#ifdef __APPLE__
// Workaround OSX linker warning: xcode uses different default symbol
// visibilities for static libs vs executables (see issue #25)
# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden")))
#else
# define TINYFORMAT_HIDDEN
#endif
#if defined(__clang__)
# define TINYFORMAT_FALLTHROUGH [[clang::fallthrough]];
#elif defined(__GNUG__) && __GNUC__ >= 7
# define TINYFORMAT_FALLTHROUGH __attribute__ ((fallthrough));
#else
# define TINYFORMAT_FALLTHROUGH
#endif
namespace tinyformat {
//------------------------------------------------------------------------------
namespace detail {
// Test whether type T1 is convertible to type T2
template <typename T1, typename T2>
struct is_convertible
{
private:
// two types of different size
struct fail { char dummy[2]; };
struct succeed { char dummy; };
// Try to convert a T1 to a T2 by plugging into tryConvert
static fail tryConvert(...);
static succeed tryConvert(const T2&);
static const T1& makeT1();
public:
# ifdef _MSC_VER
// Disable spurious loss of precision warnings in tryConvert(makeT1())
# pragma warning(push)
# pragma warning(disable:4244)
# pragma warning(disable:4267)
# endif
// Standard trick: the (...) version of tryConvert will be chosen from
// the overload set only if the version taking a T2 doesn't match.
// Then we compare the sizes of the return types to check which
// function matched. Very neat, in a disgusting kind of way :)
static const bool value =
sizeof(tryConvert(makeT1())) == sizeof(succeed);
# ifdef _MSC_VER
# pragma warning(pop)
# endif
};
// Detect when a type is not a wchar_t string
template<typename T> struct is_wchar { typedef int tinyformat_wchar_is_not_supported; };
template<> struct is_wchar<wchar_t*> {};
template<> struct is_wchar<const wchar_t*> {};
template<int n> struct is_wchar<const wchar_t[n]> {};
template<int n> struct is_wchar<wchar_t[n]> {};
// Format the value by casting to type fmtT. This default implementation
// should never be called.
template<typename T, typename fmtT, bool convertible = is_convertible<T, fmtT>::value>
struct formatValueAsType
{
static void invoke(std::ostream& /*out*/, const T& /*value*/) { assert(0); }
};
// Specialized version for types that can actually be converted to fmtT, as
// indicated by the "convertible" template parameter.
template<typename T, typename fmtT>
struct formatValueAsType<T,fmtT,true>
{
static void invoke(std::ostream& out, const T& value)
{ out << static_cast<fmtT>(value); }
};
// Convert an arbitrary type to integer. The version with convertible=false
// throws an error.
template<typename T, bool convertible = is_convertible<T,int>::value>
struct convertToInt
{
static int invoke(const T& /*value*/)
{
TINYFORMAT_ERROR("tinyformat: Cannot convert from argument type to "
"integer for use as variable width or precision");
return 0;
}
};
// Specialization for convertToInt when conversion is possible
template<typename T>
struct convertToInt<T,true>
{
static int invoke(const T& value) { return static_cast<int>(value); }
};
// Format at most ntrunc characters to the given stream.
template<typename T>
inline void formatTruncated(std::ostream& out, const T& value, int ntrunc)
{
std::ostringstream tmp;
tmp << value;
std::string result = tmp.str();
out.write(result.c_str(), (std::min)(ntrunc, static_cast<int>(result.size())));
}
#define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type) \
inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \
{ \
std::streamsize len = 0; \
while(len < ntrunc && value[len] != 0) \
++len; \
out.write(value, len); \
}
// Overload for const char* and char*. Could overload for signed & unsigned
// char too, but these are technically unneeded for printf compatibility.
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char)
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char)
#undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR
} // namespace detail
//------------------------------------------------------------------------------
// Variable formatting functions. May be overridden for user-defined types if
// desired.
/// Format a value into a stream, delegating to operator<< by default.
///
/// Users may override this for their own types. When this function is called,
/// the stream flags will have been modified according to the format string.
/// The format specification is provided in the range [fmtBegin, fmtEnd). For
/// truncating conversions, ntrunc is set to the desired maximum number of
/// characters, for example "%.7s" calls formatValue with ntrunc = 7.
///
/// By default, formatValue() uses the usual stream insertion operator
/// operator<< to format the type T, with special cases for the %c and %p
/// conversions.
template<typename T>
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
const char* fmtEnd, int ntrunc, const T& value)
{
#ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS
// Since we don't support printing of wchar_t using "%ls", make it fail at
// compile time in preference to printing as a void* at runtime.
typedef typename detail::is_wchar<T>::tinyformat_wchar_is_not_supported DummyType;
(void) DummyType(); // avoid unused type warning with gcc-4.8
#endif
// The mess here is to support the %c and %p conversions: if these
// conversions are active we try to convert the type to a char or const
// void* respectively and format that instead of the value itself. For the
// %p conversion it's important to avoid dereferencing the pointer, which
// could otherwise lead to a crash when printing a dangling (const char*).
bool canConvertToChar = detail::is_convertible<T,char>::value;
bool canConvertToVoidPtr = detail::is_convertible<T, const void*>::value;
if(canConvertToChar && *(fmtEnd-1) == 'c')
detail::formatValueAsType<T, char>::invoke(out, value);
else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p')
detail::formatValueAsType<T, const void*>::invoke(out, value);
else if(ntrunc >= 0)
{
// Take care not to overread C strings in truncating conversions like
// "%.4s" where at most 4 characters may be read.
detail::formatTruncated(out, value, ntrunc);
}
else
out << value;
}
// Overloaded version for char types to support printing as an integer
#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \
const char* fmtEnd, int /**/, charType value) \
{ \
switch(*(fmtEnd-1)) \
{ \
case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \
out << static_cast<int>(value); break; \
default: \
out << value; break; \
} \
}
// per 3.9.1: char, signed char and unsigned char are all distinct types
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char)
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char)
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char)
#undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR
namespace detail {
// Type-opaque holder for an argument to format(), with associated actions on
// the type held as explicit function pointers. This allows FormatArg's for
// each argument to be allocated as a homogenous array inside FormatList
// whereas a naive implementation based on inheritance does not.
class FormatArg
{
public:
FormatArg() {}
template<typename T>
FormatArg(const T& value)
: m_value(static_cast<const void*>(&value)),
m_formatImpl(&formatImpl<T>),
m_toIntImpl(&toIntImpl<T>)
{ }
void format(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc) const
{
m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
}
int toInt() const
{
return m_toIntImpl(m_value);
}
private:
template<typename T>
TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc, const void* value)
{
formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast<const T*>(value));
}
template<typename T>
TINYFORMAT_HIDDEN static int toIntImpl(const void* value)
{
return convertToInt<T>::invoke(*static_cast<const T*>(value));
}
const void* m_value;
void (*m_formatImpl)(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc, const void* value);
int (*m_toIntImpl)(const void* value);
};
// Parse and return an integer from the string c, as atoi()
// On return, c is set to one past the end of the integer.
inline int parseIntAndAdvance(const char*& c)
{
int i = 0;
for(;*c >= '0' && *c <= '9'; ++c)
i = 10*i + (*c - '0');
return i;
}
// Print literal part of format string and return next format spec
// position.
//
// Skips over any occurrences of '%%', printing a literal '%' to the
// output. The position of the first % character of the next
// nontrivial format spec is returned, or the end of string.
inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
{
const char* c = fmt;
for(;; ++c)
{
switch(*c)
{
case '\0':
out.write(fmt, c - fmt);
return c;
case '%':
out.write(fmt, c - fmt);
if(*(c+1) != '%')
return c;
// for "%%", tack trailing % onto next literal section.
fmt = ++c;
break;
default:
break;
}
}
}
// Parse a format string and set the stream state accordingly.
//
// The format mini-language recognized here is meant to be the one from C99,
// with the form "%[flags][width][.precision][length]type".
//
// Formatting options which can't be natively represented using the ostream
// state are returned in spacePadPositive (for space padded positive numbers)
// and ntrunc (for truncating conversions). argIndex is incremented if
// necessary to pull out variable width and precision . The function returns a
// pointer to the character after the end of the current format spec.
inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive,
int& ntrunc, const char* fmtStart,
const detail::FormatArg* formatters,
int& argIndex, int numFormatters)
{
if(*fmtStart != '%')
{
TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string");
return fmtStart;
}
// Reset stream state to defaults.
out.width(0);
out.precision(6);
out.fill(' ');
// Reset most flags; ignore irrelevant unitbuf & skipws.
out.unsetf(std::ios::adjustfield | std::ios::basefield |
std::ios::floatfield | std::ios::showbase | std::ios::boolalpha |
std::ios::showpoint | std::ios::showpos | std::ios::uppercase);
bool precisionSet = false;
bool widthSet = false;
int widthExtra = 0;
const char* c = fmtStart + 1;
// 1) Parse flags
for(;; ++c)
{
switch(*c)
{
case '#':
out.setf(std::ios::showpoint | std::ios::showbase);
continue;
case '0':
// overridden by left alignment ('-' flag)
if(!(out.flags() & std::ios::left))
{
// Use internal padding so that numeric values are
// formatted correctly, eg -00010 rather than 000-10
out.fill('0');
out.setf(std::ios::internal, std::ios::adjustfield);
}
continue;
case '-':
out.fill(' ');
out.setf(std::ios::left, std::ios::adjustfield);
continue;
case ' ':
// overridden by show positive sign, '+' flag.
if(!(out.flags() & std::ios::showpos))
spacePadPositive = true;
continue;
case '+':
out.setf(std::ios::showpos);
spacePadPositive = false;
widthExtra = 1;
continue;
default:
break;
}
break;
}
// 2) Parse width
if(*c >= '0' && *c <= '9')
{
widthSet = true;
out.width(parseIntAndAdvance(c));
}
if(*c == '*')
{
widthSet = true;
int width = 0;
if(argIndex < numFormatters)
width = formatters[argIndex++].toInt();
else
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width");
if(width < 0)
{
// negative widths correspond to '-' flag set
out.fill(' ');
out.setf(std::ios::left, std::ios::adjustfield);
width = -width;
}
out.width(width);
++c;
}
// 3) Parse precision
if(*c == '.')
{
++c;
int precision = 0;
if(*c == '*')
{
++c;
if(argIndex < numFormatters)
precision = formatters[argIndex++].toInt();
else
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision");
}
else
{
if(*c >= '0' && *c <= '9')
precision = parseIntAndAdvance(c);
else if(*c == '-') // negative precisions ignored, treated as zero.
parseIntAndAdvance(++c);
}
out.precision(precision);
precisionSet = true;
}
// 4) Ignore any C99 length modifier
while(*c == 'l' || *c == 'h' || *c == 'L' ||
*c == 'j' || *c == 'z' || *c == 't')
++c;
// 5) We're up to the conversion specifier character.
// Set stream flags based on conversion specifier (thanks to the
// boost::format class for forging the way here).
bool intConversion = false;
switch(*c)
{
case 'u': case 'd': case 'i':
out.setf(std::ios::dec, std::ios::basefield);
intConversion = true;
break;
case 'o':
out.setf(std::ios::oct, std::ios::basefield);
intConversion = true;
break;
case 'X':
out.setf(std::ios::uppercase);
TINYFORMAT_FALLTHROUGH
case 'x': case 'p':
out.setf(std::ios::hex, std::ios::basefield);
intConversion = true;
break;
case 'E':
out.setf(std::ios::uppercase);
TINYFORMAT_FALLTHROUGH
case 'e':
out.setf(std::ios::scientific, std::ios::floatfield);
out.setf(std::ios::dec, std::ios::basefield);
break;
case 'F':
out.setf(std::ios::uppercase);
TINYFORMAT_FALLTHROUGH
case 'f':
out.setf(std::ios::fixed, std::ios::floatfield);
break;
case 'G':
out.setf(std::ios::uppercase);
TINYFORMAT_FALLTHROUGH
case 'g':
out.setf(std::ios::dec, std::ios::basefield);
// As in boost::format, let stream decide float format.
out.flags(out.flags() & ~std::ios::floatfield);
break;
case 'a': case 'A':
TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs "
"are not supported");
break;
case 'c':
// Handled as special case inside formatValue()
break;
case 's':
if(precisionSet)
ntrunc = static_cast<int>(out.precision());
// Make %s print booleans as "true" and "false"
out.setf(std::ios::boolalpha);
break;
case 'n':
// Not supported - will cause problems!
TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported");
break;
case '\0':
TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly "
"terminated by end of string");
return c;
default:
break;
}
if(intConversion && precisionSet && !widthSet)
{
// "precision" for integers gives the minimum number of digits (to be
// padded with zeros on the left). This isn't really supported by the
// iostreams, but we can approximately simulate it with the width if
// the width isn't otherwise used.
out.width(out.precision() + widthExtra);
out.setf(std::ios::internal, std::ios::adjustfield);
out.fill('0');
}
return c+1;
}
//------------------------------------------------------------------------------
inline void formatImpl(std::ostream& out, const char* fmt,
const detail::FormatArg* formatters,
int numFormatters)
{
// Saved stream state
std::streamsize origWidth = out.width();
std::streamsize origPrecision = out.precision();
std::ios::fmtflags origFlags = out.flags();
char origFill = out.fill();
for (int argIndex = 0; argIndex < numFormatters; ++argIndex)
{
// Parse the format string
fmt = printFormatStringLiteral(out, fmt);
bool spacePadPositive = false;
int ntrunc = -1;
const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt,
formatters, argIndex, numFormatters);
if (argIndex >= numFormatters)
{
// Check args remain after reading any variable width/precision
TINYFORMAT_ERROR("tinyformat: Not enough format arguments");
return;
}
const FormatArg& arg = formatters[argIndex];
// Format the arg into the stream.
if(!spacePadPositive)
arg.format(out, fmt, fmtEnd, ntrunc);
else
{
// The following is a special case with no direct correspondence
// between stream formatting and the printf() behaviour. Simulate
// it crudely by formatting into a temporary string stream and
// munging the resulting string.
std::ostringstream tmpStream;
tmpStream.copyfmt(out);
tmpStream.setf(std::ios::showpos);
arg.format(tmpStream, fmt, fmtEnd, ntrunc);
std::string result = tmpStream.str(); // allocates... yuck.
for(size_t i = 0, iend = result.size(); i < iend; ++i)
if(result[i] == '+') result[i] = ' ';
out << result;
}
fmt = fmtEnd;
}
// Print remaining part of format string.
fmt = printFormatStringLiteral(out, fmt);
if(*fmt != '\0')
TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
// Restore stream state
out.width(origWidth);
out.precision(origPrecision);
out.flags(origFlags);
out.fill(origFill);
}
} // namespace detail
/// List of template arguments format(), held in a type-opaque way.
///
/// A const reference to FormatList (typedef'd as FormatListRef) may be
/// conveniently used to pass arguments to non-template functions: All type
/// information has been stripped from the arguments, leaving just enough of a
/// common interface to perform formatting as required.
class FormatList
{
public:
FormatList(detail::FormatArg* formatters, int N)
: m_formatters(formatters), m_N(N) { }
friend void vformat(std::ostream& out, const char* fmt,
const FormatList& list);
private:
const detail::FormatArg* m_formatters;
int m_N;
};
/// Reference to type-opaque format list for passing to vformat()
typedef const FormatList& FormatListRef;
namespace detail {
// Format list subclass with fixed storage to avoid dynamic allocation
template<int N>
class FormatListN : public FormatList
{
public:
template<typename... Args>
FormatListN(const Args&... args)
: FormatList(&m_formatterStore[0], N),
m_formatterStore { FormatArg(args)... }
{ static_assert(sizeof...(args) == N, "Number of args must be N"); }
private:
FormatArg m_formatterStore[N];
};
// Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard
template<> class FormatListN<0> : public FormatList
{
public: FormatListN() : FormatList(0, 0) {}
};
} // namespace detail
//------------------------------------------------------------------------------
// Primary API functions
/// Make type-agnostic format list from list of template arguments.
///
/// The exact return type of this function is an implementation detail and
/// shouldn't be relied upon. Instead it should be stored as a FormatListRef:
///
/// FormatListRef formatList = makeFormatList( /*...*/ );
template<typename... Args>
detail::FormatListN<sizeof...(Args)> makeFormatList(const Args&... args)
{
return detail::FormatListN<sizeof...(args)>(args...);
}
/// Format list of arguments to the stream according to the given format string.
///
/// The name vformat() is chosen for the semantic similarity to vprintf(): the
/// list of format arguments is held in a single function argument.
inline void vformat(std::ostream& out, const char* fmt, FormatListRef list)
{
detail::formatImpl(out, fmt, list.m_formatters, list.m_N);
}
/// Format list of arguments to the stream according to given format string.
template<typename... Args>
void format(std::ostream& out, const char* fmt, const Args&... args)
{
vformat(out, fmt, makeFormatList(args...));
}
/// Format list of arguments according to the given format string and return
/// the result as a string.
template<typename... Args>
std::string format(const char* fmt, const Args&... args)
{
std::ostringstream oss;
format(oss, fmt, args...);
return oss.str();
}
/// Format list of arguments to std::cout, according to the given format string
template<typename... Args>
void printf(const char* fmt, const Args&... args)
{
format(std::cout, fmt, args...);
}
template<typename... Args>
void printfln(const char* fmt, const Args&... args)
{
format(std::cout, fmt, args...);
std::cout << '\n';
}
} // namespace tinyformat
#endif // TINYFORMAT_H_INCLUDED