Disabled external gits
This commit is contained in:
3
cs440-acg/ext/tinyformat/.gitignore
vendored
Normal file
3
cs440-acg/ext/tinyformat/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
_bloat_test_tmp*
|
||||
tinyformat_test_cxx*
|
||||
tinyformat_speed_test
|
455
cs440-acg/ext/tinyformat/README.rst
Normal file
455
cs440-acg/ext/tinyformat/README.rst
Normal 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.
|
775
cs440-acg/ext/tinyformat/tinyformat.h
Normal file
775
cs440-acg/ext/tinyformat/tinyformat.h
Normal 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
|
Reference in New Issue
Block a user