Compare commits

...

409 Commits
2.1.1 ... 4.0.0

Author SHA1 Message Date
398343897f Update version 2017-06-27 19:02:24 +02:00
749ed39d79 Bump version number and update changelog 2017-06-25 19:40:23 +02:00
aba72982df remove 'FMT_CPPFORMAT' CMake option 2017-06-25 08:08:26 -07:00
e8ef103799 Fix undefined-var-template warning on clang when not compiling with -std=c++11 2017-06-25 07:49:31 -07:00
5e23fff052 remove unnecessary method 2017-06-25 06:07:57 -07:00
f61e71ccb9 Add Drake to projects 2017-06-22 06:31:50 -07:00
d9f0c58c65 Add Lyft Envoy to the list of projects 2017-06-22 06:20:51 -07:00
569c5bdbf1 Workaround broken __builtin_clz in clang with MS codegen (#519) 2017-06-17 07:57:07 -07:00
eafc6a3292 Update Changelog 2017-06-14 18:20:51 +02:00
dc8c943372 Describe cmake use of header-only target 2017-05-29 16:31:52 -07:00
ac5484c4e7 add alias targets with fmt namespace
For the consumer it should not matter if fmt has been added to the
project as subdirectory or via find_package. With the alias targets
the library can be always imported via fmt::fmt.
2017-05-25 14:36:16 +03:00
746adc5e71 added new namespace to the header only target in the find package test 2017-05-25 14:36:16 +03:00
048d2aec27 CMake imported targets should be namespaced 2017-05-21 14:17:12 +03:00
e14bac62a0 Changing ArgArray template argument from unsigned to size_t
Each instantiation of ArgArray template uses sizeof operator, which
returns a std::size_t value. GCC 7.1 warns about invalid conversion
(error: conversion to ‘unsigned int’ from ‘long unsigned
int’ may alter its value [-Werror=conversion]).
2017-05-20 07:19:16 +02:00
0284a2893a Use Ubuntu Trusty on Travis for a new CMake 2017-05-20 08:07:57 +03:00
07ed421521 Fix handling of implicit conversion to integral types larger than int (#507) 2017-05-06 10:23:20 -07:00
c56d345985 Don't define WIN32_LEAN_AND_MEAN in header-only mode 2017-05-06 09:13:32 -07:00
dcfd40a1b8 Revert #456
Revert #456 because it causes issues for known types (#495) and is not C++98-compatible.
2017-05-06 08:36:54 -07:00
79f11dbaa7 Allow compiling and using as DLL in windows (#502)
* printf.h fixed to compile clean - need to check whether this is the right
thing to do

* fix warnings and errors in test compiles with BUILD_SHARED_LIBS

* did requested changes and added one change to allow all tests to succeed
in windows DLL
2017-05-05 14:58:09 +02:00
52aabbe7ef Workaround MSVC lookup issue in ArgFormatterBase
Fixes #505.
2017-05-05 14:40:58 +02:00
4423490d0b Don't include the world with WIN32_LEAN_AND_MEAN (#503) 2017-05-03 21:22:01 +02:00
d49f206183 fmt::internal::is_streamable works on gcc 4.7
i test the the code on https://gcc.godbolt.org/, and only gcc 4.7 works,
gcc 4.6 fail to complie
2017-04-16 21:27:28 -07:00
7a4ac9ec9c add SOURCELINK_SUFFIX for compatibility with Sphinx 1.5
With Sphinx 1.5, this is needed by searchtools.js to display the source
snippets (see sphinx-doc/sphinx#2454).

With earlier Sphinx versions, this is a no-op because the undefined variable
will evaluate to an empty string.
2017-04-15 08:42:05 -07:00
82bb4f4e89 Fix Visual Studio 2017 new warning (C4244: 'argument': conversion from 'int' to 'const char', possible loss of data) 2017-04-15 08:38:43 -07:00
73ca9948fe Fix FormatBuf implementation (#491)
Fixes #491 (and probably #480) Before, the put-area of the custom streambuf
implementation was (sometimes) incorrectly extended beyond the writeable buffer.
The new implementation is in some cases not as efficient as the old, but avoids
to write into uninitialized memory.
2017-04-08 09:07:33 -07:00
cbac016cce %.f should have zero precision, not default precision 2017-04-08 07:18:10 -07:00
c03f55ec3a Add Kodi (xbmc) to the list of projects using fmt 2017-03-11 08:28:52 -08:00
fbc8ea8c3e False -> FalseType (#483) 2017-03-08 07:17:08 -08:00
6c3aa28c94 fix for nvcc_wrapper compiler 2017-03-08 06:20:52 -08:00
e3b5d806a8 Don't bring Arg struct into global namespace
This fixes compiling fmtlib in header-only mode when user code also has
something called 'Arg' defined. Now qualifying Arg struct with internal
namespace.
2017-03-08 06:19:11 -08:00
cc736e7611 Remove redundant include (#479) 2017-03-03 06:06:34 -08:00
789ebea863 Merge branch 'printf' of github.com:fmtlib/fmt 2017-02-26 07:17:38 -08:00
6f8006c2ce Add printf overloads that takes a writer (#476) 2017-02-25 09:58:42 -08:00
589b93de45 Add default copy constructor to SystemError (#475)
* Add default copy constructor to SystemError
* Add FMT_DEFAULTED_COPY_CTOR macro
2017-02-25 09:37:06 -08:00
db0d54f855 Fix error on MinGW 2017-02-25 09:00:56 -08:00
703960aa54 Merge branch 'master' of github.com:fmtlib/fmt 2017-02-24 07:06:12 -08:00
e208fbff52 Document which header defines formatting functions 2017-02-24 07:06:05 -08:00
20089c23ca Added missing FMT_OVERRIDE specifier to FormatBuf::overflow (#473) 2017-02-22 20:55:15 +01:00
ad6d78f2a8 added FMT_API declarations where needed when using FMT_EXPORT from a separate dll 2017-02-12 12:14:58 -05:00
a00006119f Add join argument allowing formating list of values separated by a (#466)
Add join argument allowing formating list of values separated by a
string. Each value is formated according the format specifier.
2017-02-08 06:55:12 -08:00
bc6af7548b Fix Android not being detected with latest NDK toolchain (#458)
When using the NDK 13b toolchain standalone or with CMake, ANDROID is not defined,
but __ANDROID__ is defined instead.
2017-01-20 11:20:43 +01:00
67662702aa allow to stream user defined types in a MemoryWriter (#456)
* allow to stream user defined types in a MemoryWriter

* fix indent

* follow Google C++ Style

* make code c++98 compatible

* fix macro usage

* disable ability to stream user defined types if not at least c++11

* fix for disable ability to stream user defined types if not at least c++11

* use FMT_STATIC_ASSERT
2017-01-19 20:47:43 +01:00
9e9ad57f58 Workaround an nvcc bug 2017-01-19 06:24:15 -08:00
a2596d685d Fix missing intrinsic when included from C++/CLI
Managed C++ code doesn't have the _BitScanReverse* intrinsics, we must use the fallback code for count_digits etc.
2017-01-18 06:23:25 -08:00
d8754af063 Allow %s as generic format specifier in printf (#453)
* Allow %s as generic format specifier in printf

Signed integers are formatted as %d
Unsigned integers are formatted as %u
Doubles are formatted as %f
Chars are formatted as %c
Void Pointers are formatted as %p

* Remove '%S' handling and use visitor for generic format strings

* Default for floating point is now "%g" rather than "%f"
2017-01-14 11:45:48 -08:00
e0251fdcef BasicContainerWriter utility added 2017-01-05 20:44:35 -08:00
e1689cb985 Custom FormatSpec (#439) (#444)
* A custom FormatSpec type can be passed as a template argument to the ArgFormatter chain (#439)

* Corrected nested-name-specifier error

* Spec template argument defaulted to FormatSpec

* Forward declaration of FormatSpec

* Style

* Style (part 2)

* Style (part 3)
2016-12-30 11:29:40 -08:00
b9aaa507fc Don't export the -std=c++11 flag from the fmt target 2016-12-29 16:04:16 -08:00
02553d13a0 Use https to fetch dependencies from github 2016-12-29 10:44:02 -08:00
db780cb119 Add std::basic_string allocator support to StringRef, StringBuffer and relatives (#441)
* - added basic_string allocator support to BasicStringRef, BasicCStringRef, BasicWriter::str, StringBuffer and BasicStringWriter

* - removed templated str() and to_string() function
- code style fixes
2016-12-26 08:36:56 -08:00
fac5546321 Fix test compilation for FreeBSD (#433) 2016-12-14 06:28:41 -08:00
796beaaddb Fix collision with global convert function (#425) 2016-11-19 12:05:49 -08:00
def687462c Fix signbit detection (#423) 2016-11-14 20:14:52 -08:00
25f8ad13dd Correction of 418 (#420) 2016-11-11 18:27:39 +01:00
1d2049b53e Issue #418 - Minimal supported library subset (#419) 2016-11-10 20:29:12 +01:00
f5b1c16e2c Add version macro FMT_VERSION (#411) 2016-11-07 19:04:44 -08:00
cfd00af37a Fix warnings on C++98 2016-11-06 12:58:49 -08:00
6274401919 Fix warnings 2016-11-06 12:37:31 -08:00
2fa4655af6 Fix warning in FMT_STATIC_ASSERT on older GCC (#414) 2016-11-06 11:48:01 -08:00
8cef95f7c3 Create pull_request_template 2016-11-06 20:22:43 +01:00
922ae4896b Add CONTRIBUTING file 2016-11-06 11:00:58 -08:00
05ba3e7888 Fix Klockwork compilation warning 2016-11-06 10:01:55 -08:00
1daddec151 FMT_NULLPTR -> FMT_NULL and improve formatting 2016-11-06 09:05:58 -08:00
d8867a2b07 Fix missing '>' 2016-11-05 21:09:31 +01:00
6c0125785b Add extern templates for format_float
Fixes #413.
2016-11-05 21:03:40 +01:00
49ccb2e449 Using FMT_NULLPTR instead of literal 0 2016-11-03 06:39:40 -07:00
7ce7def515 argument index -> argument id 2016-11-01 17:00:59 -07:00
8f455c10b0 fix newer clang warnings 2016-11-01 07:05:34 -07:00
e599fe7436 Add 3.0.1 Changelog entry 2016-11-01 10:49:34 +01:00
c577f72596 Update README.rst 2016-10-31 18:19:49 -07:00
15d0f32773 Fix Windows compilation with -fno-exceptions (#405) 2016-10-31 21:55:52 +01:00
8c63ea432c Fix Linux compilation with -fno-exceptions (#402) 2016-10-29 16:16:40 -07:00
abbefd7166 CHAR_WIDTH -> CHAR_SIZE to avoid collision with ISO/IEC TS 18661-1:2014 macro 2016-10-19 06:52:55 -07:00
ed874df293 Merge branch 'master' of github.com:fmtlib/fmt 2016-10-17 07:00:58 -07:00
1e018e65cb Thread-safe time formatting
This adds thread-safe (at least on platforms that provide necessary extensions) replacement functions for std::localtime and std::gmtime. Alternatively they could be placed in a new source file time.cc, but time.h seems so empty right now...
2016-10-17 07:00:04 -07:00
1500f00525 Remove unnecessary "fmt/" prefix which should be maintained with additional include_directories() in each project. (#397) 2016-10-11 06:45:04 -07:00
4809e2956a Minor documentation changes 2016-10-06 07:38:19 -07:00
61fb85618c Merge pull request #393 from pwm1234/master-pwm
Document use of format_arg for user-defined type
2016-10-06 06:49:34 -07:00
88c4bc33d2 Document use of format_arg for user-defined type 2016-10-05 15:04:08 -04:00
cee50b7572 Fix compilation on Cygwin (#388) 2016-10-01 21:32:16 -07:00
f66d37fb1c Merge pull request #390 from chronoxor/master
Introduced NamedArgWithType<Char, T> : NamedArg<Char>
2016-09-29 08:11:46 -07:00
1546b9e336 Introduced NamedArgWithType<Char, T> : NamedArg<Char> 2016-09-28 00:18:09 +03:00
17c17d1585 Merge pull request #389 from chronoxor/master
Extend ArgLists to support serialization/deserialization in third-party components
2016-09-27 08:11:36 -07:00
d09dd1a81a Extend ArgLists to support serialization/deserialization in third-party components 2016-09-27 17:40:05 +03:00
64a0016680 Merge pull request #385 from jcelerier/master
Add FMT_OVERRIDE macro to allow specifying overriding functions
2016-09-25 07:44:37 -07:00
9ec3bea2d6 Add FMT_OVERRIDE macro to allow specifying overriding functions in c++11 compilers 2016-09-24 21:10:11 +02:00
1fb0586b06 Merge pull request #381 from hghwng/master
Fix compilation on Android.
2016-09-13 08:07:43 -07:00
5f26b5da28 Fix compilation on Android. 2016-09-13 10:34:54 +08:00
0d25f6fcbb Move the paper to the docs repo 2016-08-27 08:26:47 -07:00
2ae6bca488 Complete syntax section 2016-08-25 07:23:04 -07:00
04335aeadb Correct syntax 2016-08-25 07:08:36 -07:00
3d5125cd87 Update paper 2016-08-25 06:50:09 -07:00
d775a20fff Update paper 2016-08-24 07:41:07 -07:00
4dc9fd995f Update paper 2016-08-24 06:55:18 -07:00
a79c7b4e8f Fix handling of unsigned char strings (#373) 2016-08-23 08:42:25 -07:00
195d6a5663 Update paper 2016-08-23 08:10:35 -07:00
6c184efa75 Remove old compatibility headers 2016-08-22 08:39:26 -07:00
93975c70bb Update paper 2016-08-21 07:50:31 -07:00
53f9e7f65c Add 'a' and 'A' format specifiers 2016-08-20 09:55:07 -07:00
768739c310 Update paper 2016-08-20 07:39:10 -07:00
94d387cd45 Update paper 2016-08-19 21:41:19 -07:00
721c9100f2 Update paper 2016-08-19 16:43:10 -07:00
108498bdd0 Add initial draft of the paper 2016-08-19 09:33:59 -07:00
f19d8f9655 Improve error reporting (#357) 2016-08-16 07:08:53 -07:00
2bf59a97c6 Add CUAUV, Seastar and ScyllaDB to the list of projects that use fmt 2016-08-10 08:29:44 -07:00
12a6027b40 Don't use -std compiler flag if CMAKE_CXX_STANDARD is specified (#366) 2016-08-05 08:22:45 -07:00
a5d0adf395 Use a heuristic to detect empty strftime result (#367) 2016-08-05 07:27:56 -07:00
1a23f9c274 Correct buffer growth when formatting time (#367) 2016-08-04 08:47:59 -07:00
9dbb60c4c8 Move fmt::fprintf to printf.h 2016-08-03 08:52:05 -07:00
ed30108918 Add "n" to integer formatting types 2016-07-26 07:36:35 -07:00
d5893c9a13 Update homepage and break long lines 2016-07-21 09:00:23 -07:00
6ee9f2ed09 Make printf argument formatters public (#335, #360) 2016-07-21 06:59:28 -07:00
631ffef438 Merge pull request #361 from nmoehrle/fix-unreachable-code
Remove unreachable code below FMT_THROW
2016-07-21 06:35:38 -07:00
355861f1ff Document PrintfFormatter::format 2016-07-20 08:26:14 -07:00
ab054532ce Move writer to PrintfFormatter object for consistency with BasicFormatter 2016-07-20 08:21:13 -07:00
9823675832 Break long lines 2016-07-20 08:17:33 -07:00
d4ddaaf2b1 Make PrintfFormatter public (#335, #360) 2016-07-20 08:09:14 -07:00
fa0f870ac9 Merge pull request #362 from smellman/smellman-patch-1
Update usage.rst: change Homebrew package name
2016-07-19 16:03:51 -07:00
1dbc6bd1fc Update usage.rst: change Homebrew package name 2016-07-20 07:34:21 +09:00
c8c9973669 Remove unreachable code below FMT_THROW 2016-07-19 20:33:55 +02:00
65cd664195 Fix handling of wide strings in StringWriter 2016-07-18 08:47:11 -07:00
c110c6eca7 Update readme 2016-07-16 08:44:27 -07:00
1acfd07f1e Workaround broken MinGW installation on AppVeyor (#355) 2016-07-16 08:00:53 -07:00
d4885cea62 Document BasicStringWriter 2016-07-16 08:00:53 -07:00
903357c853 Break a long line 2016-07-16 08:00:53 -07:00
88b84d6429 Merge pull request #358 from arvedarved/fix-freebsd
Fix compile on FreeBSD
2016-07-15 15:23:51 -07:00
d7c4e1cb1f Fix compile on freebsd 2016-07-15 14:28:18 +02:00
0d5ef5c2a6 Fix inclusion of printf.h in the header-only config (#354) 2016-07-12 06:59:35 -07:00
8631694021 check -> const_check to avoid a conflict with AssertMacros.h (#350) 2016-07-12 06:40:23 -07:00
4133e501f3 Merge branch 'locale' 2016-07-11 06:48:38 -07:00
0e6df7e511 Fix handling of thousands separator (#353) 2016-07-11 06:31:23 -07:00
44c926d96f Merge pull request #348 from LogicalKnight/fix-clang-extern-header-only
Fix building with clang in header-only mode
2016-06-27 09:21:16 -07:00
75f862bf57 Fix building with clang in header-only mode
Building under a recent clang compiler causes the use of an extern
template for the BasicData struct. However, the only instantiation of
that structure in format.cc is only done if FMT_HEADER_ONLY is not
defined. This causes the build to fail in C++11 or C++14 mode.
Therefore, only declare the BasicData template as extern if
FMT_HEADER_ONLY is not defined.
2016-06-25 05:22:07 +00:00
116914a949 Remove unused scripts 2016-06-22 20:58:19 -07:00
689fd2ad49 Refactor release script 2016-06-22 07:58:35 -07:00
7a0806a366 Fix release script 2016-06-22 07:29:34 -07:00
a81bff9349 Update documentation build instructions 2016-06-22 07:18:37 -07:00
5c876088d3 Reduce noise 2016-06-22 06:33:56 -07:00
d688072990 Break a long line 2016-06-22 06:29:18 -07:00
aa1e6b0f75 Handle images in rst2md converter 2016-06-22 06:28:51 -07:00
26819461bd Fix links in older docs 2016-06-17 07:30:42 -07:00
2e69e454d0 Fix a link (#347) 2016-06-16 11:18:40 -07:00
a914384bc4 Merge update-website and release scripts 2016-06-16 07:53:07 -07:00
41356aa00a Format scripts 2016-06-15 16:15:34 -07:00
e9a0d3141b Merge branch 'locale' 2016-06-15 15:04:24 -07:00
c7d0241abb Fix docs 2016-06-15 07:16:04 -07:00
e0d6f630f8 Fix MSVC warnings 2016-06-15 06:29:47 -07:00
a201c61977 Suppress -Wpadded warnings 2016-06-14 22:17:34 -07:00
4569b4dbd6 Fix -Wpessimizing-move 2016-06-14 15:53:40 -07:00
c9bb5468b6 Fix clang warning 2016-06-14 15:42:32 -07:00
b26e76efe9 Fix a -Wweak-vtables warning in clang 2016-06-14 08:54:08 -07:00
c7739536e8 Don't use strtod_l on Android (#345) 2016-06-14 06:57:12 -07:00
dfa631e64a Bump version 2016-06-14 06:33:57 -07:00
18e3f16576 Suppress clang's documentation warnings 2016-06-12 09:09:36 -07:00
72d51e0b1e Implemented #335: custom printf support 2016-06-09 08:41:56 -07:00
6ccb5673c4 Update README.rst 2016-06-03 08:40:07 -07:00
a82026746d Fix download link 2016-06-03 08:19:24 -07:00
9d36a5a3b0 Remove .doctrees 2016-06-03 08:11:24 -07:00
81e2dac955 Improve documentation build 2016-06-03 07:19:05 -07:00
5940431e2d Don't exclude the current version from dropdown menu 2016-06-03 06:23:49 -07:00
15008bf05d Generate docs for all versions 2016-06-02 08:47:33 -07:00
06045cb4a5 Minor fixes to documentation build scripts 2016-06-02 06:52:07 -07:00
9492b9ff03 Pass versions from CMake to Sphinx 2016-06-02 06:41:25 -07:00
0d9870dd9e Implement website update script 2016-06-01 07:45:54 -07:00
5e70843a73 Don't fail if a package was not found 2016-05-31 08:49:34 -07:00
d25e07660e Specify minimum required Breathe version 2016-05-31 08:46:28 -07:00
579736f210 Don't install Sphinx if it the minimum version is satisfied 2016-05-31 08:40:29 -07:00
8650c57ccd Reuse virtualenv to speed up builds 2016-05-26 08:00:41 -07:00
9071daebe9 Make work dir configurable 2016-05-26 07:35:10 -07:00
fcaf8a0cdc Make virtualenv dir configurable and break long lines 2016-05-26 06:52:51 -07:00
231c16df25 Break long lines 2016-05-26 06:48:12 -07:00
971359997f Implement website update script 2016-05-26 06:44:20 -07:00
f7abf9fd0f Implement website update script 2016-05-25 08:22:40 -07:00
e68836c123 Don't use --upgrade because it causes pip install older version of sphinx 2016-05-25 07:46:47 -07:00
dc1e36fa0e Make documentation installation optional (#333) 2016-05-24 07:03:08 -07:00
7bb1b50835 Update sphinx 2016-05-24 06:55:57 -07:00
96c28f748d Detect if lconv contains thousands_sep 2016-05-23 07:41:22 -07:00
e160c2b79a Add fmt prefix to includes (#332)
Technically it is not necessary, but prevents errors when people add the
fmt directory itself rather than its parent to includes.
2016-05-22 16:42:44 -07:00
e2a332e5df Use a mock to test locale support 2016-05-19 17:38:44 -07:00
ebff26f8f1 Improve formatting consistency 2016-05-19 13:36:00 -07:00
559739ec1d Merge pull request #328 from dpantele/android-fix
Workaround for missing lconv support in android
2016-05-19 13:31:38 -07:00
45a1509455 Workaround for missing lconv support in android (#327) 2016-05-19 13:29:21 -04:00
5b106083e7 Check if setlocale succeeded 2016-05-19 06:48:35 -07:00
2d8a7ed086 Add include to example 2016-05-19 06:39:03 -07:00
b18f783fed Add string.h to the docs 2016-05-18 21:04:34 -07:00
b6c0cf9683 Add fmt::to_string (#326) 2016-05-18 19:54:52 -07:00
d00b43c592 Workaround an issue with "delete this" in GMock and gcc 6.1.1 2016-05-14 17:58:14 -07:00
cc9b051d12 Move format_system_error to the public API (#323) 2016-05-13 07:19:39 -06:00
d67eb8af2f Correct docs 2016-05-10 11:07:53 -07:00
3400e0d609 Fix clang -Weverything warnings (#250) 2016-05-10 08:50:42 -07:00
f76583276a Clarify performance tradeoffs (#320) 2016-05-10 07:29:31 -07:00
31a4f0ab05 Implement website update script 2016-05-09 08:36:16 -07:00
744c2824c5 Fix a clang warning about an undefined template (#318) 2016-05-08 09:45:32 -07:00
140f11190b Add a website update script 2016-05-08 08:03:01 -07:00
fb67a2f660 Fix building source package 2016-05-08 07:28:34 -07:00
d2b65f77e8 Fix formatting 2016-05-08 07:27:41 -07:00
03cdd4ca50 Ignore virtualenv in subdirectories 2016-05-08 07:27:20 -07:00
e5e4fb370c Update version 2016-05-07 09:50:47 -07:00
70d93078e3 Update links 2016-05-07 09:37:38 -07:00
b344bd9582 Fix release script and changelog format 2016-05-07 09:21:26 -07:00
add6bcca3e Document ostream support 2016-05-07 09:09:33 -07:00
836415b215 Update changelog 2016-05-07 08:59:19 -07:00
7ce503e6a0 Update changelog 2016-05-07 08:50:51 -07:00
a4538ac1e8 Update ChangeLog.rst 2016-05-07 08:44:51 -07:00
6dfdada493 Update ChangeLog.rst 2016-05-07 08:00:12 -07:00
b7fe7dfad1 Update changelog 2016-05-07 07:50:56 -07:00
9c865560fd Fix a warning 2016-05-07 07:10:40 -07:00
1788883262 Fix warnings 2016-05-07 07:03:21 -07:00
90730e706b Move ostream support to ostream.{h,cc} 2016-05-06 07:37:20 -07:00
041725e9d0 Update changelog 2016-05-05 08:20:12 -07:00
c5fe3eb87e Fix code bloat regression on gcc 5.3.1 with -std=c++11 (#315) 2016-05-05 07:48:06 -07:00
b2a8141373 Update changelog 2016-05-04 08:49:29 -07:00
408c84cd42 Update copyright (#314) 2016-05-04 06:27:03 -07:00
6825ac9ccf Merge pull request #313 from dean0x7d/test-fixes
Fix a few bugs related to compile tests
2016-05-04 06:22:17 -07:00
f3d6d3a8f2 Fix find-package-test failure with non-default compiler
The failure would happen when the main project was configured with
a compiler which is not the system default. The new configuration
was not forwarded to "ctest --build-and-test". This fixes it.
2016-05-04 00:51:28 +02:00
8eaad79de7 Fix FMT_STATIC_ASSERT compile test
FMT_STATIC_ASSERT is defined in posix.h so expect_compile_error was
actually failing on a missing macro instead of the static assert.
2016-05-04 00:36:48 +02:00
131d446183 Fix compile tests not clearing the cache after an error
The cache bug meant that a failed test would appear forever broken,
even if the proper fix was applied.
2016-05-04 00:36:11 +02:00
4042198262 Merge pull request #312 from dean0x7d/intel-udl-fix
Fix user-defined literal detection for Intel C++ compiler (#311)
2016-05-03 08:34:53 -07:00
729491eab7 Use FMT_ICC_VERSION everywhere internally 2016-05-03 14:26:01 +02:00
3a04ebf14f Fix user-defined literal detection for Intel C++ compiler 2016-05-03 13:43:51 +02:00
b64d13a357 Merge pull request #309 from jwilk/spelling
Fix typos
2016-05-01 06:48:34 -07:00
e0ac51cbd9 Fix typos 2016-05-01 12:29:21 +02:00
4e279e2a70 Correct formatting 2016-04-29 07:02:37 -07:00
a3929b719a Document date/time formatting and move example to the docs 2016-04-29 06:40:31 -07:00
63be22d5dc Update template 2016-04-28 07:09:05 -07:00
f171d1f3d6 Update a link 2016-04-28 07:05:28 -07:00
c33f3e281b cppformat -> fmt 2016-04-28 07:00:22 -07:00
59d0efd6d6 Update links 2016-04-28 06:54:37 -07:00
56b6c05bf9 Update links 2016-04-28 06:47:43 -07:00
8dd48923d8 Update links 2016-04-28 06:45:50 -07:00
7dac3c44f5 Update links 2016-04-27 18:26:23 -07:00
4a0d9dbc30 Update build script 2016-04-27 18:22:44 -07:00
c916ab3319 Update docs 2016-04-27 08:35:59 -07:00
f9ee5f4b43 Update README.rst 2016-04-27 08:28:24 -07:00
e817bb9cc4 Update links 2016-04-27 08:23:03 -07:00
5946029585 Update README.rst 2016-04-27 08:15:49 -07:00
f4b5aeaed2 Update README.rst 2016-04-27 08:13:56 -07:00
5a6a58576a Update README.rst 2016-04-27 08:05:40 -07:00
90accff030 Fix a warning 2016-04-26 07:23:03 -07:00
49f6771ca9 Grow buffer 2016-04-25 08:24:14 -07:00
fa5ebd27d4 Implement time formatting 2016-04-25 08:07:27 -07:00
4cddbfa7cf Update README.rst 2016-04-25 06:36:26 -07:00
f1cd5413e9 Update README.rst 2016-04-24 10:46:21 -07:00
7f0d8184c9 cppformat -> fmt 2016-04-24 10:39:33 -07:00
e569297d6a Update Android config 2016-04-24 10:23:02 -07:00
bea282dae9 cppformat -> fmt 2016-04-24 09:18:15 -07:00
17544b1824 Add compatibility headers 2016-04-24 09:10:58 -07:00
afd67497de cppformat -> fmt 2016-04-24 09:06:12 -07:00
5e1576f79f cppformat -> fmt 2016-04-24 08:17:47 -07:00
848ab63a2a CPPFORMAT_VERSION -> FMT_VERSION 2016-04-24 07:16:33 -07:00
c7d9d79ad2 Document visitors and formatters 2016-04-21 07:31:32 -07:00
9ae2ac2fb7 Document ArgVisitor 2016-04-21 06:59:48 -07:00
125bb0f19a Revise docs 2016-04-21 06:50:54 -07:00
bfdca8b576 Make ArgVisitor public and document it
Also remove unnecessary namespace qualification.
2016-04-20 09:11:33 -07:00
da3467b7f9 Document BasicArgFormatter 2016-04-20 07:45:11 -07:00
fb5350543c Correct case 2016-04-20 07:44:37 -07:00
ccbc891992 Document argument formatters 2016-04-20 07:16:52 -07:00
b69e6dcead Make BasicArgFormatter public and add ArgFormatter
This allows providing custom argument formatters without relying on
internal APIs (#235).
2016-04-19 08:56:31 -07:00
4d8cee2d48 Document the n format specifier 2016-04-17 20:46:08 -07:00
f68771abe4 Implement locale-specific integer formatting 2016-04-17 19:06:03 -07:00
581afee039 Enable wstring in gtest (#304) 2016-04-13 08:26:42 -04:00
e49a4e0aff Bump version and adjust comments 2016-04-12 07:42:47 -04:00
753e9a04c0 Update version 2016-04-11 20:02:42 -04:00
18c69c998d Update changelog and version 2016-04-11 19:59:22 -04:00
6cfe43539a Update version 2016-04-11 19:59:01 -04:00
c61043450b Update changelog 2016-04-11 19:57:39 -04:00
e4f7d0d311 Resolve overloads 2016-04-11 09:32:24 -04:00
d8e246daaf Merge pull request #299 from niosHD/minor-packaging-improvements
Minor packaging improvements
2016-04-07 07:35:09 -07:00
620f999e80 ignore the current build directory when packaging
This makes sure that the current build directory is not packaged
with the regular source tree when `make package_source` is used.
2016-04-06 09:49:34 +02:00
dcbb6b1e4d make the install location for the generated cmake files configurable
By default the cmake files are installed to "<prefix>/lib/cmake/cppformat".
This can now be customized using the `FMT_CMAKE_DIR` cache variable.
Valid paths are documented in the cmake manual [1].

e.g., install the cmake files to "<prefix>/share/cmake/cppformat"
```
cmake -DFMT_CMAKE_DIR=share/cmake/cppformat <path-to-source>
```

Tip: Try `make package` to examine the result.

[1] https://cmake.org/cmake/help/v3.0/command/find_package.html
2016-04-06 09:33:13 +02:00
a99891e7a5 Use a mock to test custom argument formatter 2016-03-19 07:36:28 -07:00
52f89065e1 Make argument formatter customizable 2016-03-19 07:20:31 -07:00
9ffe98c00e Fix/suppress MSVC warnings 2016-03-19 06:39:33 -07:00
63d7f3d116 Don't check for C++11 features if C++11 support is disabled 2016-03-18 08:10:06 -07:00
c052cf11b9 Improve coding style consistency 2016-03-18 07:59:04 -07:00
0c901efb16 Use less strict pedantic flags for tests because of GMock (#291) 2016-03-18 07:52:24 -07:00
030cccfeef Update changelog 2016-03-17 07:28:21 -07:00
062e3bd757 Add a branch option to the release script 2016-03-13 10:27:15 -07:00
5174b8ca28 Update README.rst 2016-03-10 10:52:21 -08:00
090de29ddf Update README.rst 2016-03-10 10:51:52 -08:00
59607f5e99 Fix warnings on GCC 4.6.3 2016-03-09 07:47:08 -08:00
763d1fe6a3 Suppress warnings in Google Mock 2016-03-08 07:36:22 -08:00
7d6622942c Break a long line 2016-03-08 06:55:41 -08:00
0867c1b447 Test writing to ostream 2016-03-08 06:47:53 -08:00
6883d6e724 Merge pull request #285 from mwinterb/winerror_winu
Changed format_windows_error to not need LocalFree
2016-03-06 06:55:37 -08:00
8f4b8edb8b Added test of error code that forces the insufficient buffer code path. 2016-03-04 17:47:37 -08:00
5324d385c0 Fix a MSVC warning 2016-03-04 06:44:12 -08:00
fc505b5447 Merge pull request #286 from Naios/master
Define FMT_NOEXCEPT empty when exceptions are disabled
2016-03-03 09:54:27 -08:00
3c3d6b3d2a Define FMT_NOEXCEPT empty when exceptions are disabled
* Fixes warnings about disabled exception support in MSVC
2016-03-03 17:23:25 +01:00
2a05a87fe7 Changed format_windows_error to not need LocalFree
This is for non-'Desktop' applications that have a
more limited collection of functions.
2016-03-02 17:35:34 -08:00
3ecad55910 Fix sign conversion warnings 2016-03-02 07:53:14 -08:00
d929fdeb9b Fix clang warnings 2016-03-02 07:02:57 -08:00
9d577cae6f Fix handling of negative error codes in format_error_code 2016-03-02 07:01:21 -08:00
6e820841d4 Add mongo_smasher to the list of projects 2016-03-01 14:28:52 -08:00
82d6813e7c Fix a bunch of Clang sign-conversion warnings 2016-03-01 08:12:29 -08:00
2f12a32c20 Merge pull request #277 from PSPDFKit-labs/fix-switch-fallthrough
Fix switch fall-through warning
2016-02-23 10:28:17 -08:00
6178bc6f8e Fix switch fall-through warning
Clang with `-Wimplicit-fallthrough` enabled shows a warning here without
the break.
2016-02-23 12:59:26 -05:00
209748f128 Workaround a bug in Apple LLVM version 4.2 of clang (#276) 2016-02-23 07:27:01 -08:00
f64ea6235f Include xlocale.h for LC_NUMERIC_MASK on OS X 2016-02-19 13:30:18 -08:00
80d288b146 Correct comment 2016-02-11 07:25:00 -08:00
6500f161f7 Fix a warning in freelocale mock (#274) 2016-02-11 06:55:53 -08:00
abd93d824a Move gmock into test/ 2016-02-10 07:16:49 -08:00
2b2aa8926f add_subdirectory-test -> add-subdirectory-test for consistency 2016-02-10 07:01:40 -08:00
21b8279cfe Remove biicode because it has been shut down 2016-02-09 21:25:52 -08:00
cd7f6c1fda Comment 2016-02-09 15:40:26 -08:00
70e44a8e7f Simplify locale mock 2016-02-09 11:31:04 -08:00
b8c6192a61 Simplify build config and enable C++11 by default 2016-02-09 08:43:39 -08:00
c7b7141b11 Merge pull request #273 from niosHD/extend-ci-tests
Extend CI tests with older c++ standards
2016-02-09 06:53:58 -08:00
c57f8f563b omit the c++ 2003 tests 2016-02-09 09:08:11 +01:00
016af73d19 fixed typo in script 2016-02-07 18:47:39 +01:00
f961683516 specify c++11 as c++0x for travis 2016-02-07 18:41:46 +01:00
27a1b787c8 test in c++ 98, 03 and 11 mode 2016-02-07 18:32:23 +01:00
6a79a3279b build and test in c++11 and in c++98 mode 2016-02-07 18:23:02 +01:00
734d3bf175 Remove link to API compatibility report which is no longer functional 2016-02-05 08:31:19 -08:00
8c8877df5a treat format.cc like a header
Given that it is required for header only builds it has to be
installed too.
2016-02-05 15:27:49 +01:00
754be04f11 state that sudo is required for CI
This informs travis that the container-based build environment can
not be used.
2016-02-05 14:33:56 +01:00
1adee75e1c Check if -fno-delete-null-pointer-checks flag is supported 2016-02-04 08:36:41 -08:00
a4b611a3d3 Workaround GTest bug 705 (#268) 2016-02-04 08:15:19 -08:00
220bb764e5 Use quotes for local includes 2016-02-04 08:08:33 -08:00
a750114a38 Merge pull request #271 from newnon/master
fix android build
2016-02-04 06:14:24 -08:00
4d56c5ce4c fix android build 2016-02-04 12:46:59 +03:00
c0ad9a888b Update the source location in the documentation build script 2016-02-03 09:28:44 -08:00
0598d84f61 Merge pull request #267 from niosHD/update-project-layout
Update project layout
2016-02-03 08:48:25 -08:00
b09c83504e test for gnu++98 instead for c++98 because of mingw ... 2016-02-03 14:11:30 +01:00
3133925ab2 Merge branch 'master' into update-project-layout
Conflicts:
	posix.h
2016-02-03 13:15:26 +01:00
03b9485cb3 perform the slower tests only in PEDANTIC mode 2016-02-03 13:05:18 +01:00
ded46cc1b6 build test-main library again to improve build time 2016-02-03 12:59:03 +01:00
c1a4cd0fa7 check if cppformat is the master project or just used as dependency
Based on that information less intrusive option defaults are choosen.
Additionally, packaging support is omitted.
2016-02-03 11:20:19 +01:00
797d72133e restored smoke test for syntax compatibility with the 98 c++ standard 2016-02-03 10:01:53 +01:00
56cfd9f4ce installed posix.h too when it is built into the library 2016-02-03 09:59:55 +01:00
e0e8f717a0 FMT_USE_FILE_DESCRIPTORS is apparently only needed for the tests 2016-02-03 09:14:32 +01:00
cfb25b0e80 Use typedefs instead of macros 2016-02-02 22:06:54 -08:00
e489f879c3 Add locale tests 2016-02-02 17:21:09 -08:00
c0e926109e use the same warning options like before the PR
Additional notes on how to improve the current state have been added.
2016-02-02 17:14:51 +01:00
b05a02b91c remove comment which is now superfluous since the code has been moved 2016-02-02 17:04:12 +01:00
d411aa165e use quotes for including cppformat headers from the tests 2016-02-02 16:58:41 +01:00
be961bae0f fixed typo in python script 2016-02-01 10:41:45 +01:00
0a4acc9656 use the cmake and ctest to drive the appveyor build 2016-02-01 10:22:47 +01:00
d3fe82c55b propagate the build type into the find test 2016-01-31 20:17:39 +01:00
a659d8079e Merge branch 'master' into update-project-layout 2016-01-31 17:00:05 +01:00
00fda9b25a add a test for the find script on the build directory 2016-01-31 00:02:49 +01:00
4aeeb49d23 updated compile-test and include a working test
This makes sure that the test does not break due to other reasons.
2016-01-30 23:49:39 +01:00
fee52f79b8 update the includes in the tests to get rid of the deprecated warnings 2016-01-30 22:33:37 +01:00
4952e79e45 Document that floating-point formatting is locale-dependent. 2016-01-30 09:20:43 -08:00
95c0fb5075 Add a "C" numeric locale 2016-01-29 16:29:46 -08:00
62ac1d98a4 export the header only library also during installation 2016-01-29 16:57:45 +01:00
5aa5116edc moved the library definition and the installation into a sub CMakeLists.txt 2016-01-29 16:39:03 +01:00
5e7ab2f4ea major cleanup of the test folders CMakeLists.txt
The new code does not rely on globally defined include directories
anymore. Additionally a lot of conditional code and has been removed
which improves readability a lot.
2016-01-29 16:21:17 +01:00
b52d0bd9d4 define cppformat cmake targets with proper interface definitions 2016-01-29 13:23:08 +01:00
0fb474be3a outlined the compiler feature tests to improve script readability 2016-01-29 13:03:47 +01:00
3019a8c1fd moved code into cppformat subdirectory
Proxy headers have been placed into the project root to emit
deprecation warnings.
2016-01-29 12:49:35 +01:00
7ee287d3d9 Sign extend arguments of smaller types passed to %ll? (#265) 2016-01-27 07:03:19 -08:00
ae6368c985 Add a comment to clarify why convert to unsigned 2016-01-26 07:13:34 -08:00
bb7a80b1ab Correct comment 2016-01-25 06:46:43 -08:00
8474a6232d Don't perform narrowing conversion for integers in printf (#255) 2016-01-24 00:43:42 +01:00
22f61140d1 Format CMake files, ignore generated files and don't copy header 2016-01-13 07:14:32 -08:00
52ee516cf3 Merge pull request #264 from niosHD/improve-find-and-package-support
Improve find and package support
2016-01-13 06:37:41 -08:00
ef7bbfff87 removed workaround for cmake versions prior to 2.8.10 2016-01-13 09:54:02 +01:00
891e9117f6 trying to update cmake to 2.8.12 in travis via a ppa repo
Whitelist of repos:
https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json

Whitelist of packages:
https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
2016-01-12 16:57:40 +01:00
3fc3ecd184 reverted removal of CPACK_SOURCE_PACKAGE_FILE_NAME
The use of `CPACK_PACKAGE_NAME` leads to
`cppformat-<version>-Source.zip` as the name for the source package
which is different from the expected `cppformat-<version>.zip`.
2016-01-12 16:48:53 +01:00
06f3abe26d Return early from ArgMap::find 2016-01-12 06:37:39 -08:00
50f14f225c Merge pull request #262 from mwinterb/argmap_vector
Changed ArgMap to be backed by a vector instead of a map.
2016-01-12 06:23:19 -08:00
b732455fd3 enable package support for out of source builds 2016-01-12 12:52:05 +01:00
daf74ae0b1 upgrades to cmake 2.8.12 and adds config and export support
This commit upgrades cmake to 2.8.12 to implement proper cmake
`find_package` support using config and export file generation.
Having this support enables users to use installed cppformat
with a simple `find_package` call. Directly using a version
from a build directory is also supported.

main.cpp:
```
 #include <cppformat/format.h>
int main(int argc, char** argv)
{
  for(int i = 0; i < argc; ++i)
    fmt::print("{}: {}\n",i,argv[i]);
  return 0;
}

```

CMakeLists.txt:
```
cmake_minimum_required(VERSION 2.8.12)

project(cppformat-test)

find_package(cppformat REQUIRED)

add_executable(cppformat-test "main.cpp")
target_link_libraries(cppformat-test cppformat)

```
Configuring when cppformat is installed under `CMAKE_INSTALL_PREFIX`: `cmake <PATH_TO_TEST_SRC>`

Configuring when cppformat is installed `ELSEWHERE`: `cmake -Dcppformat_DIR=<ELSEWHERE>/lib/cmake/cppformat <PATH_TO_TEST_SRC>`

Configuring when cppformat is only built: `cmake -Dcppformat_DIR=<cppformat_BUILD_DIR> <PATH_TO_TEST_SRC>`
2016-01-12 12:47:19 +01:00
4af764d040 Changed ArgMap to be backed by a vector instead of a map.
The main reason for this is to avoid a dynamic memory allocation in every format() call with Visual Studio if there are no named arguments.
2016-01-10 15:30:34 -08:00
97e9ed11bc Set interface include dir for gmock 2016-01-09 08:07:31 -08:00
f55bf55d43 Correct comment 2016-01-09 08:06:59 -08:00
e604d5347f Fix links in README (#260) 2016-01-08 13:03:34 -08:00
979e70f10d Merge pull request #259 from mwinterb/unknown_pragma_bsr
Fixed unknown pragma warnings for _BitScanReverse.
2016-01-08 07:25:48 -08:00
b203beb61d Don't define the MSVC clz functions unless __builtin_clzll is unavailable.
This is mainly just to avoid including intrin.h unnecessarily.
2016-01-07 15:38:08 -08:00
28a303ddd4 Fixed unknown pragma warnings for _BitScanReverse.
Changed to only define the MSVC implementations for clz and clzll if the builtins are not available to avoid warnings about an unknown #pragma for "intrinsic".
2016-01-06 14:42:27 -08:00
3943803412 Merge pull request #256 from mwinterb/clang_ms_clz
Fixed macro redefinition warnings when compiling with clang-cl.
2016-01-06 06:46:17 -08:00
7185e96da1 Fixed issues with MSVC emulations of clz and clzll.
Both clang-cl and Clang/C2 #define _MSC_VER but also have support for __builtin_clz and __builtin_clzll, leading to duplicate macro definition warnings. Emulation of clz using _BitScanReverse is suppressed if the builtins are already available.

Additionally, the value of the output parameter of _BitScanReverse is undefined if the input value is 0, which is avoided by construction, so the code analysis warning for using uninitialized data is now suppressed.
2016-01-05 16:03:06 -08:00
251a0869be Fixed macro redefinition warnings when compiling with clang-cl.
Both clang-cl and Clang/C2 #define _MSC_VER but also have support for __builtin_clz and __builtin_clzll, leading to duplicate macro definition warnings. This change suppresses emulation of clz using _BitScanReverse if the __clang__ macro is defined.
2016-01-01 18:07:06 -08:00
804ad8f4df Document std::ostream overload of fprintf 2015-12-24 07:00:22 -08:00
8b0504825d Merge branch 'master' of github.com:cppformat/cppformat 2015-12-24 06:58:05 -08:00
77d3761b50 Enable macro expansion in Doxygen (fixes #248) 2015-12-24 06:54:37 -08:00
0525a03a69 Merge pull request #251 from nickhutchinson/work/fprintf-streams
Add fprintf overload that writes to a std::ostream
2015-12-23 06:58:44 -08:00
1a5a1708b7 Add fprintf overload that writes to a std::ostream
For completeness, add an overload for printf that takes a std::ostream.
2015-12-23 15:59:13 +13:00
4797ca025e POD -> trivially copyable/constructible 2015-12-20 07:29:59 -08:00
3121ebd044 Merge pull request #249 from dean0x7d/variadic-v3
Reduce compile time of variadic functions (2)
2015-12-20 07:10:00 -08:00
b098306839 Replace template recursion with array initialization 2015-12-19 18:19:18 +01:00
a721319e3a Add MakeArg constructor for Arg 2015-12-19 18:19:18 +01:00
29726cefb8 Remove extra '*'s 2015-12-18 07:30:03 -08:00
811964502d Add BasicFormatter's members to the docs 2015-12-18 07:24:25 -08:00
0629d76bb0 Fallback to sized integer types on MSVC if stdint.h is not available 2015-12-18 07:20:05 -08:00
016714c57b Add BasicFormatter to the docs 2015-12-18 07:16:40 -08:00
c679352517 Define FMT_API to nothing for Doxygen 2015-12-18 07:13:43 -08:00
1042ddda0f Document BasicFormatter 2015-12-18 07:07:41 -08:00
d998b5d038 Add version 2.0.0 to the dropdown menu 2015-12-18 06:47:37 -08:00
bf6651d1ca Add github-btn style 2015-12-17 07:59:09 -08:00
1cba0aea27 Simplify CMake config and do minor adjustments
for consistency with used coding conventions.
2015-12-10 07:24:23 -08:00
a9d2e826fe Merge pull request #245 from macdems/master
Declarations for shared library in Windows.
2015-12-10 07:05:33 -08:00
c47318afa8 Declarations for shared library in Windows.
Added __declspec(dllimport) and __declspec(dllexport) declarations
when compiled in Windows.
2015-12-10 13:36:18 +01:00
ecd52bc610 Fix for a bogus MSVC warning (#244) 2015-12-09 08:57:29 -08:00
5c76d107cb Fix MSVC build 2015-12-06 14:17:34 -08:00
98c1f76f24 Replace uninitialized_copy with memmove (#242)
because the memory areas may overlap.
2015-12-06 07:44:07 -08:00
e7f4566dd4 Replace <algorithm> with <memory>
~20% faster compile time on bloat-test
2015-12-04 22:57:36 -08:00
e0179ee190 Fix a warning and remove extra newline 2015-12-04 17:52:36 -08:00
e567fe6960 Replace "!!" with "!= 0" for readability 2015-12-04 17:52:06 -08:00
7c60db1e24 Fix a warning 2015-12-04 14:12:42 -08:00
0ea73df717 Merge branch 'custom-formatter' 2015-12-04 07:24:09 -08:00
aa7bb101ed Undefine fileno if defined in posix-test 2015-12-03 20:17:04 -08:00
3bc97a5564 Merge pull request #241 from Gachapen/fix_fileno
Fix fileno causing compile error when #defined
2015-12-03 20:15:10 -08:00
c2ffa14684 Fix fileno causing compile error when #defined
Error:
expected unqualified-id before '(' token
 int fmt::BufferedFile::fileno() const {

This is an issue with Android NDK and mingw32.
2015-12-03 23:21:42 +01:00
535dbdd1c8 Move formatter methods to the header
and improve naming consistency
2015-12-03 09:38:06 -08:00
00d56e06ef Bump version 2015-12-03 08:15:03 -08:00
7e94fcb680 Reapply accidentally reverted changes by ReadmeCritic 2015-12-03 08:09:53 -08:00
6ced4230f4 Initial support for custom formatters 2015-12-02 08:41:05 -08:00
82 changed files with 6542 additions and 3336 deletions

3
.github/pull_request_template vendored Normal file
View File

@ -0,0 +1,3 @@
<!---
Please make sure you've followed the guidelines outlined in the CONTRIBUTING.rst file.
--->

5
.gitignore vendored
View File

@ -2,7 +2,7 @@ bin/
/_CPack_Packages
/doc/doxyxml
/doc/html
/doc/virtualenv
virtualenv
/Testing
/install_manifest.txt
*~
@ -10,7 +10,8 @@ bin/
*.so*
*.zip
cmake_install.cmake
CPack*Config.cmake
CPack*.cmake
fmt-*.cmake
CTestTestfile.cmake
CMakeCache.txt
CMakeFiles

View File

@ -1,4 +1,6 @@
language: cpp
dist: trusty
sudo: required # the doc target uses sudo to install dependencies
os:
- linux
@ -12,8 +14,9 @@ env:
6pxmyzLHSn1ZR7OX5rfPvwM3tOyZ3H0=
matrix:
- BUILD=Doc
- BUILD=Debug
- BUILD=Release
- BUILD=Debug STANDARD=0x
- BUILD=Release STANDARD=98
- BUILD=Release STANDARD=0x
matrix:
exclude:

View File

@ -1,10 +1,10 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := cppformat_static
LOCAL_MODULE_FILENAME := libcppformat
LOCAL_MODULE := fmt_static
LOCAL_MODULE_FILENAME := libfmt
LOCAL_SRC_FILES := format.cc
LOCAL_SRC_FILES := fmt/format.cc
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)

View File

@ -1,64 +1,71 @@
message(STATUS "CMake version: ${CMAKE_VERSION}")
cmake_minimum_required(VERSION 2.6)
cmake_minimum_required(VERSION 2.8.12)
# Determine if fmt is built as a subproject (using add_subdirectory)
# or if it is the master project.
set(MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(MASTER_PROJECT ON)
endif ()
# Joins arguments and places the results in ${result_var}.
function(join result_var)
set(result )
foreach (arg ${ARGN})
set(result "${result}${arg}")
endforeach ()
set(${result_var} "${result}" PARENT_SCOPE)
endfunction()
# Set the default CMAKE_BUILD_TYPE to Release.
# This should be done before the project command since the latter can set
# CMAKE_BUILD_TYPE itself (it does so for nmake).
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
join(doc "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc})
endif ()
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
# Options that control generation of various targets.
option(FMT_DOC "Generate the doc target." ON)
option(FMT_INSTALL "Generate the install target." ON)
option(FMT_TEST "Generate the test target." ON)
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
option(FMT_USE_CPP11 "Enable the addition of C++11 compiler flags." ON)
project(FORMAT)
project(FMT)
# Starting with cmake 3.0 VERSION is part of the project command.
file(READ fmt/format.h format_h)
if (NOT format_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
message(FATAL_ERROR "Cannot get FMT_VERSION from format.h.")
endif ()
# Use math to skip leading zeros if any.
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.
${CPACK_PACKAGE_VERSION_PATCH})
message(STATUS "Version: ${FMT_VERSION}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-std=c++11 HAVE_STD_CPP11_FLAG)
if (HAVE_STD_CPP11_FLAG)
# Check if including cmath works with -std=c++11 and -O3.
# It may not in MinGW due to bug http://ehc.ac/p/mingw/bugs/2250/.
set(CMAKE_REQUIRED_FLAGS "-std=c++11 -O3")
check_cxx_source_compiles("
#include <cmath>
int main() {}" FMT_CPP11_CMATH)
# Check if including <unistd.h> works with -std=c++11.
# It may not in MinGW due to bug http://sourceforge.net/p/mingw/bugs/2024/.
check_cxx_source_compiles("
#include <unistd.h>
int main() {}" FMT_CPP11_UNISTD_H)
if (FMT_CPP11_CMATH AND FMT_CPP11_UNISTD_H)
set(CPP11_FLAG -std=c++11)
else ()
check_cxx_compiler_flag(-std=gnu++11 HAVE_STD_GNUPP11_FLAG)
if (HAVE_STD_CPP11_FLAG)
set(CPP11_FLAG -std=gnu++11)
endif ()
endif ()
set(CMAKE_REQUIRED_FLAGS )
else ()
check_cxx_compiler_flag(-std=c++0x HAVE_STD_CPP0X_FLAG)
if (HAVE_STD_CPP0X_FLAG)
set(CPP11_FLAG -std=c++0x)
endif ()
endif ()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
if (CMAKE_GENERATOR MATCHES "Visual Studio")
include(cxx11)
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wshadow -pedantic)
endif ()
if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
# If Microsoft SDK is installed create script run-msbuild.bat that
# calls SetEnv.cmd to to set up build environment and runs msbuild.
# calls SetEnv.cmd to set up build environment and runs msbuild.
# It is useful when building Visual Studio projects with the SDK
# toolchain rather than Visual Studio.
include(FindSetEnv)
@ -72,58 +79,14 @@ if (CMAKE_GENERATOR MATCHES "Visual Studio")
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
endif ()
set(FMT_SOURCES format.cc format.h)
include(CheckSymbolExists)
if (WIN32)
check_symbol_exists(open io.h HAVE_OPEN)
else ()
check_symbol_exists(open fcntl.h HAVE_OPEN)
endif ()
if (HAVE_OPEN)
add_definitions(-DFMT_USE_FILE_DESCRIPTORS=1)
set(FMT_SOURCES ${FMT_SOURCES} posix.cc posix.h)
endif ()
if (CPP11_FLAG)
set(CMAKE_REQUIRED_FLAGS ${CPP11_FLAG})
endif ()
if (BIICODE)
include(support/cmake/biicode.cmake)
return()
endif ()
add_library(cppformat ${FMT_SOURCES})
if (BUILD_SHARED_LIBS AND UNIX AND NOT APPLE)
# Fix rpmlint warning:
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
target_link_libraries(cppformat -Wl,--as-needed)
endif ()
if (FMT_PEDANTIC AND
(CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")))
set(FMT_EXTRA_COMPILE_FLAGS "-Wall -Wextra -Wshadow -pedantic")
endif ()
# If FMT_PEDANTIC is TRUE, then test compilation with both -std=c++11
# and the default flags. Otherwise use only the default flags.
# The library is distributed in the source form and users have full control
# over compile options, so the options used here only matter for testing.
if (CPP11_FLAG AND FMT_PEDANTIC)
set(FMT_EXTRA_COMPILE_FLAGS "${FMT_EXTRA_COMPILE_FLAGS} ${CPP11_FLAG}")
set(FMT_TEST_DEFAULT_FLAGS TRUE)
endif ()
set_target_properties(cppformat
PROPERTIES COMPILE_FLAGS "${FMT_EXTRA_COMPILE_FLAGS}")
set(CPPFORMAT_VERSION 2.0.0)
if (NOT CPPFORMAT_VERSION MATCHES "^([0-9]+).([0-9]+).([0-9]+)$")
message(FATAL_ERROR "Invalid version format ${CPPFORMAT_VERSION}.")
endif ()
set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
set(CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
add_subdirectory(fmt)
if (FMT_DOC)
add_subdirectory(doc)
@ -134,12 +97,10 @@ if (FMT_TEST)
add_subdirectory(test)
endif ()
set_target_properties(cppformat PROPERTIES
VERSION ${CPPFORMAT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR})
if (EXISTS .gitignore)
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
if (MASTER_PROJECT AND EXISTS ${gitignore})
# Get the list of ignored files from .gitignore.
file (STRINGS ".gitignore" lines)
file (STRINGS ${gitignore} lines)
LIST(REMOVE_ITEM lines /doc/html)
foreach (line ${lines})
string(REPLACE "." "[.]" line "${line}")
@ -151,15 +112,8 @@ if (EXISTS .gitignore)
set(CPACK_SOURCE_GENERATOR ZIP)
set(CPACK_SOURCE_IGNORE_FILES ${ignored_files})
set(CPACK_SOURCE_PACKAGE_FILE_NAME cppformat-${CPPFORMAT_VERSION})
set(CPACK_RESOURCE_FILE_README ${FORMAT_SOURCE_DIR}/README.rst)
set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION})
set(CPACK_PACKAGE_NAME fmt)
set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.rst)
include(CPack)
endif ()
# Install targets.
if (FMT_INSTALL)
set(FMT_LIB_DIR lib CACHE STRING
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.")
install(TARGETS cppformat DESTINATION ${FMT_LIB_DIR})
install(FILES format.h DESTINATION include/cppformat)
endif ()

11
CONTRIBUTING.rst Normal file
View File

@ -0,0 +1,11 @@
Contributing to fmt
===================
All C++ code must adhere to `Google C++ Style Guide
<https://google.github.io/styleguide/cppguide.html>`_ with the following
exceptions:
* Exceptions are permitted
* snake_case should be used instead of UpperCamelCase for function names
Thanks for contributing!

View File

@ -1,3 +1,328 @@
4.0.0 - 2017-06-27
------------------
* Removed old compatibility headers ``cppformat/*.h`` and CMake options (`#527 <https://github.com/pull/527>`_). Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_.
* Added ``string.h`` containing ``fmt::to_string()`` as alternative to ``std::to_string()`` as well as other string writer functionality (`#326 <https://github.com/fmtlib/fmt/issues/326>`_ and `#441 <https://github.com/fmtlib/fmt/pull/441>`_):
.. code:: c++
#include "fmt/string.h"
std::string answer = fmt::to_string(42);
Thanks to `@glebov-andrey (Andrey Glebov) <https://github.com/glebov-andrey>`_.
* Moved ``fmt::printf()`` to new ``printf.h`` header and allowed ``%s`` as generic specifier (`#453 <https://github.com/fmtlib/fmt/pull/453>`_), made ``%.f`` more conformant to regular ``printf()`` (`#490 <https://github.com/fmtlib/fmt/pull/490>`_), added custom writer support (`#476 <https://github.com/fmtlib/fmt/issues/476>`_) and implemented missing custom argument formatting (`#339 <https://github.com/fmtlib/fmt/pull/339>`_ and `#340 <https://github.com/fmtlib/fmt/pull/340>`_):
.. code:: c++
#include "fmt/printf.h"
// %s format specifier can be used with any argument type.
fmt::printf("%s", 42);
Thanks `@mojoBrendan <https://github.com/mojoBrendan>`_, `@manylegged (Arthur Danskin) <https://github.com/manylegged>`_ and `@spacemoose (Glen Stark) <https://github.com/spacemoose>`_. See also `#360 <https://github.com/fmtlib/fmt/issues/360>`_, `#335 <https://github.com/fmtlib/fmt/issues/335>`_ and `#331 <https://github.com/fmtlib/fmt/issues/331>`_.
* Added ``container.h`` containing a ``BasicContainerWriter`` to write to containers like ``std::vector`` (`#450 <https://github.com/fmtlib/fmt/pull/450>`_). Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_.
* Added ``fmt::join()`` function that takes a range and formats its elements separated by a given string (`#466 <https://github.com/fmtlib/fmt/pull/466>`_):
.. code:: c++
#include "fmt/format.h"
std::vector<double> v = {1.2, 3.4, 5.6};
// Prints "(+01.20, +03.40, +05.60)".
fmt::print("({:+06.2f})", fmt::join(v.begin(), v.end(), ", "));
Thanks `@olivier80 <https://github.com/olivier80>`_.
* Added support for custom formatting specifications to simplify customization of built-in formatting (`#444 <https://github.com/fmtlib/fmt/pull/444>`_). Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_. See also `#439 <https://github.com/fmtlib/fmt/issues/439>`_.
* Added ``fmt::format_system_error()`` for error code formatting (`#323 <https://github.com/fmtlib/fmt/issues/323>`_ and `#526 <https://github.com/fmtlib/fmt/pull/526>`_). Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_.
* Added thread-safe ``fmt::localtime()`` and ``fmt::gmtime()`` as replacement for the standard version to ``time.h`` (`#396 <https://github.com/fmtlib/fmt/pull/396>`_). Thanks `@codicodi <https://github.com/codicodi>`_.
* Internal improvements to ``NamedArg`` and ``ArgLists`` (`#389 <https://github.com/fmtlib/fmt/pull/389>`_ and `#390 <https://github.com/fmtlib/fmt/pull/390>`_). Thanks `@chronoxor <https://github.com/chronoxor>`_.
* Fixed crash due to bug in ``FormatBuf`` (`#493 <https://github.com/fmtlib/fmt/pull/493>`_). Thanks `@effzeh <https://github.com/effzeh>`_. See also `#480 <https://github.com/fmtlib/fmt/issues/480>`_ and `#491 <https://github.com/fmtlib/fmt/issues/491>`_.
* Fixed handling of wide strings in ``fmt::StringWriter``.
* Improved compiler error messages (`#357 <https://github.com/fmtlib/fmt/issues/357>`_).
* Fixed various warnings and issues with various compilers (`#494 <https://github.com/fmtlib/fmt/pull/494>`_, `#499 <https://github.com/fmtlib/fmt/pull/499>`_, `#483 <https://github.com/fmtlib/fmt/pull/483>`_, `#519 <https://github.com/fmtlib/fmt/pull/519>`_, `#485 <https://github.com/fmtlib/fmt/pull/485>`_, `#482 <https://github.com/fmtlib/fmt/pull/482>`_, `#475 <https://github.com/fmtlib/fmt/pull/475>`_, `#473 <https://github.com/fmtlib/fmt/pull/473>`_ and `#414 <https://github.com/fmtlib/fmt/pull/414>`_). Thanks `@chronoxor <https://github.com/chronoxor>`_, `@zhaohuaxishi <https://github.com/zhaohuaxishi>`_, `@pkestene (Pierre Kestener) <https://github.com/pkestene>`_, `@dschmidt (Dominik Schmidt) <https://github.com/dschmidt>`_ and `@0x414c (Alexey Gorishny) <https://github.com/0x414c>`_ .
* Improved CMake: targets are now namespaced (`#511 <https://github.com/fmtlib/fmt/pull/511>`_ and `#513 <https://github.com/fmtlib/fmt/pull/513>`_), supported header-only ``printf.h`` (`#354 <https://github.com/fmtlib/fmt/pull/354>`_), fixed issue with minimal supported library subset (`#418 <https://github.com/fmtlib/fmt/issues/418>`_, `#419 <https://github.com/fmtlib/fmt/pull/419>`_ and `#420 <https://github.com/fmtlib/fmt/pull/420>`_). Thanks `@bjoernthiel (Bjoern Thiel) <https://github.com/bjoernthiel>`_,
`@niosHD (Mario Werner) <https://github.com/niosHD>`_, `@LogicalKnight (Sean LK) <https://github.com/LogicalKnight>`_ and `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
* Improved documentation. Thanks to `@pwm1234 (Phil) <https://github.com/pwm1234>`_ for `#393 <https://github.com/fmtlib/fmt/pull/393>`_.
3.0.2 - 2017-06-14
------------------
* Added ``FMT_VERSION`` macro (`#411 <https://github.com/fmtlib/fmt/issues/411>`_).
* Used ``FMT_NULL`` instead of literal ``0`` (`#409 <https://github.com/fmtlib/fmt/pull/409>`_). Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
* Added extern templates for ``format_float`` (`#413 <https://github.com/fmtlib/fmt/issues/413>`_).
* Fixed implicit conversion issue (`#507 <https://github.com/fmtlib/fmt/issues/507>`_).
* Fixed signbit detection (`#423 <https://github.com/fmtlib/fmt/issues/423>`_).
* Fixed naming collision (`#425 <https://github.com/fmtlib/fmt/issues/425>`_).
* Fixed missing intrinsic for C++/CLI (`#457 <https://github.com/fmtlib/fmt/pull/457>`_). Thanks `@calumr (Calum Robinson) <https://github.com/calumr>`_
* Fixed Android detection (`#458 <https://github.com/fmtlib/fmt/pull/458>`_). Thanks `@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_.
* Use lean ``windows.h`` if not in header-only mode (`#503 <https://github.com/fmtlib/fmt/pull/503>`_). Thanks `@Quentin01 (Quentin Buathier) <https://github.com/Quentin01>`_.
* Fixed issue with CMake exporting C++11 flag (`#445 <https://github.com/fmtlib/fmt/pull/455>`_). Thanks `@EricWF (Eric) <https://github.com/EricWF>`_.
* Fixed issue with nvcc and MSVC compiler bug and MinGW (`#505 <https://github.com/fmtlib/fmt/issues/505>`_).
* Fixed DLL issues (`#469 <https://github.com/fmtlib/fmt/pull/469>`_ and `#502 <https://github.com/fmtlib/fmt/pull/502>`_). Thanks `@richardeakin (Richard Eakin) <https://github.com/richardeakin>`_ and `@AndreasSchoenle (Andreas Schönle) <https://github.com/AndreasSchoenle>`_.
* Fixed test compilation under FreeBSD (`#433 <https://github.com/fmtlib/fmt/issues/433>`_).
* Fixed various warnings (`#403 <https://github.com/fmtlib/fmt/pull/403>`_, `#410 <https://github.com/fmtlib/fmt/pull/410>`_ and `#510 <https://github.com/fmtlib/fmt/pull/510>`_). Thanks `@Lecetem <https://github.com/Lectem>`_, `@chenhayat (Chen Hayat) <https://github.com/chenhayat>`_ and `@trozen <https://github.com/trozen>`_.
* Removed redundant include (`#479 <https://github.com/fmtlib/fmt/issues/479>`_).
* Fixed documentation issues.
3.0.1 - 2016-11-01
------------------
* Fixed handling of thousands seperator (`#353 <https://github.com/fmtlib/fmt/issues/353>`_)
* Fixed handling of ``unsigned char`` strings (`#373 <https://github.com/fmtlib/fmt/issues/373>`_)
* Corrected buffer growth when formatting time (`#367 <https://github.com/fmtlib/fmt/issues/367>`_)
* Removed warnings under MSVC and clang (`#318 <https://github.com/fmtlib/fmt/issues/318>`_, `#250 <https://github.com/fmtlib/fmt/issues/250>`_, also merged `#385 <https://github.com/fmtlib/fmt/pull/385>`_ and `#361 <https://github.com/fmtlib/fmt/pull/361>`_). Thanks `@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_ and `@nmoehrle (Nils Moehrle) <https://github.com/nmoehrle>`_.
* Fixed compilation issues under Android (`#327 <https://github.com/fmtlib/fmt/pull/327>`_, `#345 <https://github.com/fmtlib/fmt/issues/345>`_ and `#381 <https://github.com/fmtlib/fmt/pull/381>`_), FreeBSD (`#358 <https://github.com/fmtlib/fmt/pull/358>`_), Cygwin (`#388 <https://github.com/fmtlib/fmt/issues/388>`_), MinGW (`#355 <https://github.com/fmtlib/fmt/issues/355>`_) as well as other issues (`#350 <https://github.com/fmtlib/fmt/issues/350>`_, `#366 <https://github.com/fmtlib/fmt/issues/355>`_, `#348 <https://github.com/fmtlib/fmt/pull/348>`_, `#402 <https://github.com/fmtlib/fmt/pull/402>`_, `#405 <https://github.com/fmtlib/fmt/pull/405>`_). Thanks to `@dpantele (Dmitry) <https://github.com/dpantele>`_, `@hghwng (Hugh Wang) <https://github.com/hghwng>`_, `@arvedarved (Tilman Keskinöz) <https://github.com/arvedarved>`_, `@LogicalKnight (Sean) <https://github.com/LogicalKnight>`_ and `@JanHellwig (Jan Hellwig) <https://github.com/janhellwig>`_.
* Fixed some documentation issues and extended specification (`#320 <https://github.com/fmtlib/fmt/issues/320>`_, `#333 <https://github.com/fmtlib/fmt/pull/333>`_, `#347 <https://github.com/fmtlib/fmt/issues/347>`_, `#362 <https://github.com/fmtlib/fmt/pull/362>`_). Thanks to `@smellman (Taro Matsuzawa aka. btm) <https://github.com/smellman>`_.
3.0.0 - 2016-05-07
------------------
* The project has been renamed from C++ Format (cppformat) to fmt for
consistency with the used namespace and macro prefix
(`#307 <https://github.com/fmtlib/fmt/issues/307>`_).
Library headers are now located in the ``fmt`` directory:
.. code:: c++
#include "fmt/format.h"
Including ``format.h`` from the ``cppformat`` directory is deprecated
but works via a proxy header which will be removed in the next major version.
The documentation is now available at http://fmtlib.net.
* Added support for `strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like
`date and time formatting <http://fmtlib.net/3.0.0/api.html#date-and-time-formatting>`_
(`#283 <https://github.com/fmtlib/fmt/issues/283>`_):
.. code:: c++
#include "fmt/time.h"
std::time_t t = std::time(nullptr);
// Prints "The date is 2016-04-29." (with the current date)
fmt::print("The date is {:%Y-%m-%d}.", *std::localtime(&t));
* ``std::ostream`` support including formatting of user-defined types that provide
overloaded ``operator<<`` has been moved to ``fmt/ostream.h``:
.. code:: c++
#include "fmt/ostream.h"
class Date {
int year_, month_, day_;
public:
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
friend std::ostream &operator<<(std::ostream &os, const Date &d) {
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};
std::string s = fmt::format("The date is {}", Date(2012, 12, 9));
// s == "The date is 2012-12-9"
* Added support for `custom argument formatters
<http://fmtlib.net/3.0.0/api.html#argument-formatters>`_
(`#235 <https://github.com/fmtlib/fmt/issues/235>`_).
* Added support for locale-specific integer formatting with the ``n`` specifier
(`#305 <https://github.com/fmtlib/fmt/issues/305>`_):
.. code:: c++
std::setlocale(LC_ALL, "en_US.utf8");
fmt::print("cppformat: {:n}\n", 1234567); // prints 1,234,567
* Sign is now preserved when formatting an integer with an incorrect ``printf``
format specifier (`#265 <https://github.com/fmtlib/fmt/issues/265>`_):
.. code:: c++
fmt::printf("%lld", -42); // prints -42
Note that it would be an undefined behavior in ``std::printf``.
* Length modifiers such as ``ll`` are now optional in printf formatting
functions and the correct type is determined automatically
(`#255 <https://github.com/fmtlib/fmt/issues/255>`_):
.. code:: c++
fmt::printf("%d", std::numeric_limits<long long>::max());
Note that it would be an undefined behavior in ``std::printf``.
* Added initial support for custom formatters
(`#231 <https://github.com/fmtlib/fmt/issues/231>`_).
* Fixed detection of user-defined literal support on Intel C++ compiler
(`#311 <https://github.com/fmtlib/fmt/issues/311>`_,
`#312 <https://github.com/fmtlib/fmt/pull/312>`_).
Thanks to `@dean0x7d (Dean Moldovan) <https://github.com/dean0x7d>`_ and
`@speth (Ray Speth) <https://github.com/speth>`_.
* Reduced compile time
(`#243 <https://github.com/fmtlib/fmt/pull/243>`_,
`#249 <https://github.com/fmtlib/fmt/pull/249>`_,
`#317 <https://github.com/fmtlib/fmt/issues/317>`_):
.. image:: https://cloud.githubusercontent.com/assets/4831417/11614060/
b9e826d2-9c36-11e5-8666-d4131bf503ef.png
.. image:: https://cloud.githubusercontent.com/assets/4831417/11614080/
6ac903cc-9c37-11e5-8165-26df6efae364.png
Thanks to `@dean0x7d (Dean Moldovan) <https://github.com/dean0x7d>`_.
* Compile test fixes (`#313 <https://github.com/fmtlib/fmt/pull/313>`_).
Thanks to `@dean0x7d (Dean Moldovan) <https://github.com/dean0x7d>`_.
* Documentation fixes (`#239 <https://github.com/fmtlib/fmt/pull/239>`_,
`#248 <https://github.com/fmtlib/fmt/issues/248>`_,
`#252 <https://github.com/fmtlib/fmt/issues/252>`_,
`#258 <https://github.com/fmtlib/fmt/pull/258>`_,
`#260 <https://github.com/fmtlib/fmt/issues/260>`_,
`#301 <https://github.com/fmtlib/fmt/issues/301>`_,
`#309 <https://github.com/fmtlib/fmt/pull/309>`_).
Thanks to `@ReadmeCritic <https://github.com/ReadmeCritic>`_
`@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_ and
`@jwilk (Jakub Wilk) <https://github.com/jwilk>`_.
* Fixed compiler and sanitizer warnings (
`#244 <https://github.com/fmtlib/fmt/issues/244>`_,
`#256 <https://github.com/fmtlib/fmt/pull/256>`_,
`#259 <https://github.com/fmtlib/fmt/pull/259>`_,
`#263 <https://github.com/fmtlib/fmt/issues/263>`_,
`#274 <https://github.com/fmtlib/fmt/issues/274>`_,
`#277 <https://github.com/fmtlib/fmt/pull/277>`_,
`#286 <https://github.com/fmtlib/fmt/pull/286>`_,
`#291 <https://github.com/fmtlib/fmt/issues/291>`_,
`#296 <https://github.com/fmtlib/fmt/issues/296>`_,
`#308 <https://github.com/fmtlib/fmt/issues/308>`_)
Thanks to `@mwinterb <https://github.com/mwinterb>`_,
`@pweiskircher (Patrik Weiskircher) <https://github.com/pweiskircher>`_,
`@Naios <https://github.com/Naios>`_.
* Improved compatibility with Windows Store apps
(`#280 <https://github.com/fmtlib/fmt/issues/280>`_,
`#285 <https://github.com/fmtlib/fmt/pull/285>`_)
Thanks to `@mwinterb <https://github.com/mwinterb>`_.
* Added tests of compatibility with older C++ standards
(`#273 <https://github.com/fmtlib/fmt/pull/273>`_).
Thanks to `@niosHD <https://github.com/niosHD>`_.
* Fixed Android build (`#271 <https://github.com/fmtlib/fmt/pull/271>`_).
Thanks to `@newnon <https://github.com/newnon>`_.
* Changed ``ArgMap`` to be backed by a vector instead of a map.
(`#261 <https://github.com/fmtlib/fmt/issues/261>`_,
`#262 <https://github.com/fmtlib/fmt/pull/262>`_).
Thanks to `@mwinterb <https://github.com/mwinterb>`_.
* Added ``fprintf`` overload that writes to a ``std::ostream``
(`#251 <https://github.com/fmtlib/fmt/pull/251>`_).
Thanks to `nickhutchinson (Nicholas Hutchinson) <https://github.com/nickhutchinson>`_.
* Export symbols when building a Windows DLL
(`#245 <https://github.com/fmtlib/fmt/pull/245>`_).
Thanks to `macdems (Maciek Dems) <https://github.com/macdems>`_.
* Fixed compilation on Cygwin (`#304 <https://github.com/fmtlib/fmt/issues/304>`_).
* Implemented a workaround for a bug in Apple LLVM version 4.2 of clang
(`#276 <https://github.com/fmtlib/fmt/issues/276>`_).
* Implemented a workaround for Google Test bug
`#705 <https://github.com/google/googletest/issues/705>`_ on gcc 6
(`#268 <https://github.com/fmtlib/fmt/issues/268>`_).
Thanks to `octoploid <https://github.com/octoploid>`_.
* Removed Biicode support because the latter has been discontinued.
2.1.1 - 2016-04-11
------------------
* The install location for generated CMake files is now configurable via
the ``FMT_CMAKE_DIR`` CMake variable
(`#299 <https://github.com/fmtlib/fmt/pull/299>`_).
Thanks to `@niosHD <https://github.com/niosHD>`_.
* Documentation fixes (`#252 <https://github.com/fmtlib/fmt/issues/252>`_).
2.1.0 - 2016-03-21
------------------
* Project layout and build system improvements
(`#267 <https://github.com/fmtlib/fmt/pull/267>`_):
* The code have been moved to the ``cppformat`` directory.
Including ``format.h`` from the top-level directory is deprecated
but works via a proxy header which will be removed in the next
major version.
* C++ Format CMake targets now have proper interface definitions.
* Installed version of the library now supports the header-only
configuration.
* Targets ``doc``, ``install``, and ``test`` are now disabled if C++ Format
is included as a CMake subproject. They can be enabled by setting
``FMT_DOC``, ``FMT_INSTALL``, and ``FMT_TEST`` in the parent project.
Thanks to `@niosHD <https://github.com/niosHD>`_.
2.0.1 - 2016-03-13
------------------
* Improved CMake find and package support
(`#264 <https://github.com/fmtlib/fmt/issues/264>`_).
Thanks to `@niosHD <https://github.com/niosHD>`_.
* Fix compile error with Android NDK and mingw32
(`#241 <https://github.com/fmtlib/fmt/issues/241>`_).
Thanks to `@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_.
* Documentation fixes
(`#248 <https://github.com/fmtlib/fmt/issues/248>`_,
`#260 <https://github.com/fmtlib/fmt/issues/260>`_).
2.0.0 - 2015-12-01
------------------
@ -5,9 +330,9 @@ General
~~~~~~~
* [Breaking] Named arguments
(`#169 <https://github.com/cppformat/cppformat/pull/169>`_,
`#173 <https://github.com/cppformat/cppformat/pull/173>`_,
`#174 <https://github.com/cppformat/cppformat/pull/174>`_):
(`#169 <https://github.com/fmtlib/fmt/pull/169>`_,
`#173 <https://github.com/fmtlib/fmt/pull/173>`_,
`#174 <https://github.com/fmtlib/fmt/pull/174>`_):
.. code:: c++
@ -16,9 +341,9 @@ General
Thanks to `@jamboree <https://github.com/jamboree>`_.
* [Experimental] User-defined literals for format and named arguments
(`#204 <https://github.com/cppformat/cppformat/pull/204>`_,
`#206 <https://github.com/cppformat/cppformat/pull/206>`_,
`#207 <https://github.com/cppformat/cppformat/pull/207>`_):
(`#204 <https://github.com/fmtlib/fmt/pull/204>`_,
`#206 <https://github.com/fmtlib/fmt/pull/206>`_,
`#207 <https://github.com/fmtlib/fmt/pull/207>`_):
.. code:: c++
@ -29,11 +354,11 @@ General
* [Breaking] Formatting of more than 16 arguments is now supported when using
variadic templates
(`#141 <https://github.com/cppformat/cppformat/issues/141>`_).
(`#141 <https://github.com/fmtlib/fmt/issues/141>`_).
Thanks to `@Shauren <https://github.com/Shauren>`_.
* Runtime width specification
(`#168 <https://github.com/cppformat/cppformat/pull/168>`_):
(`#168 <https://github.com/fmtlib/fmt/pull/168>`_):
.. code:: c++
@ -43,10 +368,10 @@ General
* [Breaking] Enums are now formatted with an overloaded ``std::ostream`` insertion
operator (``operator<<``) if available
(`#232 <https://github.com/cppformat/cppformat/issues/232>`_).
(`#232 <https://github.com/fmtlib/fmt/issues/232>`_).
* [Breaking] Changed default ``bool`` format to textual, "true" or "false"
(`#170 <https://github.com/cppformat/cppformat/issues/170>`_):
(`#170 <https://github.com/fmtlib/fmt/issues/170>`_):
.. code:: c++
@ -60,7 +385,7 @@ General
* ``fmt::printf`` and ``fmt::sprintf`` now support formatting of ``bool`` with the
``%s`` specifier giving textual output, "true" or "false"
(`#223 <https://github.com/cppformat/cppformat/pull/223>`_):
(`#223 <https://github.com/fmtlib/fmt/pull/223>`_):
.. code:: c++
@ -69,10 +394,10 @@ General
Thanks to `@LarsGullik <https://github.com/LarsGullik>`_.
* [Breaking] ``signed char`` and ``unsigned char`` are now formatted as integers by default
(`#217 <https://github.com/cppformat/cppformat/pull/217>`_).
(`#217 <https://github.com/fmtlib/fmt/pull/217>`_).
* [Breaking] Pointers to C strings can now be formatted with the ``p`` specifier
(`#223 <https://github.com/cppformat/cppformat/pull/223>`_):
(`#223 <https://github.com/fmtlib/fmt/pull/223>`_):
.. code:: c++
@ -82,12 +407,12 @@ General
* [Breaking] ``fmt::printf`` and ``fmt::sprintf`` now print null pointers as ``(nil)``
and null strings as ``(null)`` for consistency with glibc
(`#226 <https://github.com/cppformat/cppformat/pull/226>`_).
(`#226 <https://github.com/fmtlib/fmt/pull/226>`_).
Thanks to `@LarsGullik <https://github.com/LarsGullik>`_.
* [Breaking] ``fmt::(s)printf`` now supports formatting of objects of user-defined types
that provide an overloaded ``std::ostream`` insertion operator (``operator<<``)
(`#201 <https://github.com/cppformat/cppformat/issues/201>`_):
(`#201 <https://github.com/fmtlib/fmt/issues/201>`_):
.. code:: c++
@ -95,15 +420,15 @@ General
* [Breaking] The ``Buffer`` template is now part of the public API and can be used
to implement custom memory buffers
(`#140 <https://github.com/cppformat/cppformat/issues/140>`_).
(`#140 <https://github.com/fmtlib/fmt/issues/140>`_).
Thanks to `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_.
* [Breaking] Improved compatibility between ``BasicStringRef`` and
`std::experimental::basic_string_view
<http://en.cppreference.com/w/cpp/experimental/basic_string_view>`_
(`#100 <https://github.com/cppformat/cppformat/issues/100>`_,
`#159 <https://github.com/cppformat/cppformat/issues/159>`_,
`#183 <https://github.com/cppformat/cppformat/issues/183>`_):
(`#100 <https://github.com/fmtlib/fmt/issues/100>`_,
`#159 <https://github.com/fmtlib/fmt/issues/159>`_,
`#183 <https://github.com/fmtlib/fmt/issues/183>`_):
- Comparison operators now compare string content, not pointers
- ``BasicStringRef::c_str`` replaced by ``BasicStringRef::data``
@ -113,40 +438,40 @@ General
``BasicCStringRef``.
* Dependency on pthreads introduced by Google Test is now optional
(`#185 <https://github.com/cppformat/cppformat/issues/185>`_).
(`#185 <https://github.com/fmtlib/fmt/issues/185>`_).
* New CMake options ``FMT_DOC``, ``FMT_INSTALL`` and ``FMT_TEST`` to control
generation of ``doc``, ``install`` and ``test`` targets respectively, on by default
(`#197 <https://github.com/cppformat/cppformat/issues/197>`_,
`#198 <https://github.com/cppformat/cppformat/issues/198>`_,
`#200 <https://github.com/cppformat/cppformat/issues/200>`_).
(`#197 <https://github.com/fmtlib/fmt/issues/197>`_,
`#198 <https://github.com/fmtlib/fmt/issues/198>`_,
`#200 <https://github.com/fmtlib/fmt/issues/200>`_).
Thanks to `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_.
* ``noexcept`` is now used when compiling with MSVC2015
(`#215 <https://github.com/cppformat/cppformat/pull/215>`_).
(`#215 <https://github.com/fmtlib/fmt/pull/215>`_).
Thanks to `@dmkrepo (Dmitriy) <https://github.com/dmkrepo>`_.
* Added an option to disable use of ``windows.h`` when ``FMT_USE_WINDOWS_H``
is defined as 0 before including ``format.h``
(`#171 <https://github.com/cppformat/cppformat/issues/171>`_).
(`#171 <https://github.com/fmtlib/fmt/issues/171>`_).
Thanks to `@alfps (Alf P. Steinbach) <https://github.com/alfps>`_.
* [Breaking] ``windows.h`` is now included with ``NOMINMAX`` unless
``FMT_WIN_MINMAX`` is defined. This is done to prevent breaking code using
``std::min`` and ``std::max`` and only affects the header-only configuration
(`#152 <https://github.com/cppformat/cppformat/issues/152>`_,
`#153 <https://github.com/cppformat/cppformat/pull/153>`_,
`#154 <https://github.com/cppformat/cppformat/pull/154>`_).
(`#152 <https://github.com/fmtlib/fmt/issues/152>`_,
`#153 <https://github.com/fmtlib/fmt/pull/153>`_,
`#154 <https://github.com/fmtlib/fmt/pull/154>`_).
Thanks to `@DevO2012 <https://github.com/DevO2012>`_.
* Improved support for custom character types
(`#171 <https://github.com/cppformat/cppformat/issues/171>`_).
(`#171 <https://github.com/fmtlib/fmt/issues/171>`_).
Thanks to `@alfps (Alf P. Steinbach) <https://github.com/alfps>`_.
* Added an option to disable use of IOStreams when ``FMT_USE_IOSTREAMS``
is defined as 0 before including ``format.h``
(`#205 <https://github.com/cppformat/cppformat/issues/205>`_,
`#208 <https://github.com/cppformat/cppformat/pull/208>`_).
(`#205 <https://github.com/fmtlib/fmt/issues/205>`_,
`#208 <https://github.com/fmtlib/fmt/pull/208>`_).
Thanks to `@JodiTheTigger <https://github.com/JodiTheTigger>`_.
* Improved detection of ``isnan``, ``isinf`` and ``signbit``.
@ -155,31 +480,31 @@ Optimization
~~~~~~~~~~~~
* Made formatting of user-defined types more efficient with a custom stream buffer
(`#92 <https://github.com/cppformat/cppformat/issues/92>`_,
`#230 <https://github.com/cppformat/cppformat/pull/230>`_).
(`#92 <https://github.com/fmtlib/fmt/issues/92>`_,
`#230 <https://github.com/fmtlib/fmt/pull/230>`_).
Thanks to `@NotImplemented <https://github.com/NotImplemented>`_.
* Further improved performance of ``fmt::Writer`` on integer formatting
and fixed a minor regression. Now it is ~7% faster than ``karma::generate``
on Karma's benchmark
(`#186 <https://github.com/cppformat/cppformat/issues/186>`_).
(`#186 <https://github.com/fmtlib/fmt/issues/186>`_).
* [Breaking] Reduced `compiled code size
<https://github.com/cppformat/cppformat#compile-time-and-code-bloat>`_
(`#143 <https://github.com/cppformat/cppformat/issues/143>`_,
`#149 <https://github.com/cppformat/cppformat/pull/149>`_).
<https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_
(`#143 <https://github.com/fmtlib/fmt/issues/143>`_,
`#149 <https://github.com/fmtlib/fmt/pull/149>`_).
Distribution
~~~~~~~~~~~~
* [Breaking] Headers are now installed in
``${CMAKE_INSTALL_PREFIX}/include/cppformat``
(`#178 <https://github.com/cppformat/cppformat/issues/178>`_).
(`#178 <https://github.com/fmtlib/fmt/issues/178>`_).
Thanks to `@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_.
* [Breaking] Changed the library name from ``format`` to ``cppformat``
for consistency with the project name and to avoid potential conflicts
(`#178 <https://github.com/cppformat/cppformat/issues/178>`_).
(`#178 <https://github.com/fmtlib/fmt/issues/178>`_).
Thanks to `@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_.
* C++ Format is now available in `Debian <https://www.debian.org/>`_ GNU/Linux
@ -187,7 +512,7 @@ Distribution
`sid <https://packages.debian.org/source/sid/cppformat>`_) and
derived distributions such as
`Ubuntu <https://launchpad.net/ubuntu/+source/cppformat>`_ 15.10 and later
(`#155 <https://github.com/cppformat/cppformat/issues/155>`_)::
(`#155 <https://github.com/fmtlib/fmt/issues/155>`_)::
$ sudo apt-get install libcppformat1-dev
@ -197,7 +522,7 @@ Distribution
are now available. Thanks to Dave Johansen.
* C++ Format can now be installed via `Homebrew <http://brew.sh/>`_ on OS X
(`#157 <https://github.com/cppformat/cppformat/issues/157>`_)::
(`#157 <https://github.com/fmtlib/fmt/issues/157>`_)::
$ brew install cppformat
@ -208,47 +533,47 @@ Documentation
* Migrated from ReadTheDocs to GitHub Pages for better responsiveness
and reliability
(`#128 <https://github.com/cppformat/cppformat/issues/128>`_).
(`#128 <https://github.com/fmtlib/fmt/issues/128>`_).
New documentation address is http://cppformat.github.io/.
* Added `Building the documentation
<http://cppformat.github.io/dev/usage.html#building-the-documentation>`_
<http://fmtlib.net/2.0.0/usage.html#building-the-documentation>`_
section to the documentation.
* Documentation build script is now compatible with Python 3 and newer pip versions.
(`#189 <https://github.com/cppformat/cppformat/pull/189>`_,
`#209 <https://github.com/cppformat/cppformat/issues/209>`_).
(`#189 <https://github.com/fmtlib/fmt/pull/189>`_,
`#209 <https://github.com/fmtlib/fmt/issues/209>`_).
Thanks to `@JodiTheTigger <https://github.com/JodiTheTigger>`_ and
`@xentec <https://github.com/xentec>`_.
* Documentation fixes and improvements
(`#36 <https://github.com/cppformat/cppformat/issues/36>`_,
`#75 <https://github.com/cppformat/cppformat/issues/75>`_,
`#125 <https://github.com/cppformat/cppformat/issues/125>`_,
`#160 <https://github.com/cppformat/cppformat/pull/160>`_,
`#161 <https://github.com/cppformat/cppformat/pull/161>`_,
`#162 <https://github.com/cppformat/cppformat/issues/162>`_,
`#165 <https://github.com/cppformat/cppformat/issues/165>`_,
`#210 <https://github.com/cppformat/cppformat/issues/210>`_).
(`#36 <https://github.com/fmtlib/fmt/issues/36>`_,
`#75 <https://github.com/fmtlib/fmt/issues/75>`_,
`#125 <https://github.com/fmtlib/fmt/issues/125>`_,
`#160 <https://github.com/fmtlib/fmt/pull/160>`_,
`#161 <https://github.com/fmtlib/fmt/pull/161>`_,
`#162 <https://github.com/fmtlib/fmt/issues/162>`_,
`#165 <https://github.com/fmtlib/fmt/issues/165>`_,
`#210 <https://github.com/fmtlib/fmt/issues/210>`_).
Thanks to `@syohex (Syohei YOSHIDA) <https://github.com/syohex>`_ and
bug reporters.
* Fixed out-of-tree documentation build
(`#177 <https://github.com/cppformat/cppformat/issues/177>`_).
(`#177 <https://github.com/fmtlib/fmt/issues/177>`_).
Thanks to `@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_.
Fixes
~~~~~
* Fixed ``initializer_list`` detection
(`#136 <https://github.com/cppformat/cppformat/issues/136>`_).
(`#136 <https://github.com/fmtlib/fmt/issues/136>`_).
Thanks to `@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_.
* [Breaking] Fixed formatting of enums with numeric format specifiers in
``fmt::(s)printf``
(`#131 <https://github.com/cppformat/cppformat/issues/131>`_,
`#139 <https://github.com/cppformat/cppformat/issues/139>`_):
(`#131 <https://github.com/fmtlib/fmt/issues/131>`_,
`#139 <https://github.com/fmtlib/fmt/issues/139>`_):
.. code:: c++
@ -258,51 +583,53 @@ Fixes
Thanks to `@Naios <https://github.com/Naios>`_.
* Improved compatibility with old versions of MinGW
(`#129 <https://github.com/cppformat/cppformat/issues/129>`_,
`#130 <https://github.com/cppformat/cppformat/pull/130>`_,
`#132 <https://github.com/cppformat/cppformat/issues/132>`_).
(`#129 <https://github.com/fmtlib/fmt/issues/129>`_,
`#130 <https://github.com/fmtlib/fmt/pull/130>`_,
`#132 <https://github.com/fmtlib/fmt/issues/132>`_).
Thanks to `@cstamford (Christopher Stamford) <https://github.com/cstamford>`_.
* Fixed a compile error on MSVC with disabled exceptions
(`#144 <https://github.com/cppformat/cppformat/issues/144>`_).
(`#144 <https://github.com/fmtlib/fmt/issues/144>`_).
* Added a workaround for broken implementation of variadic templates in MSVC2012
(`#148 <https://github.com/cppformat/cppformat/issues/148>`_).
(`#148 <https://github.com/fmtlib/fmt/issues/148>`_).
* Placed the anonymous namespace within ``fmt`` namespace for the header-only
configuration
(`#171 <https://github.com/cppformat/cppformat/issues/171>`_).
(`#171 <https://github.com/fmtlib/fmt/issues/171>`_).
Thanks to `@alfps (Alf P. Steinbach) <https://github.com/alfps>`_.
* Fixed issues reported by Coverity Scan
(`#187 <https://github.com/cppformat/cppformat/issues/187>`_,
`#192 <https://github.com/cppformat/cppformat/issues/192>`_).
(`#187 <https://github.com/fmtlib/fmt/issues/187>`_,
`#192 <https://github.com/fmtlib/fmt/issues/192>`_).
* Implemented a workaround for a name lookup bug in MSVC2010
(`#188 <https://github.com/cppformat/cppformat/issues/188>`_).
(`#188 <https://github.com/fmtlib/fmt/issues/188>`_).
* Fixed compiler warnings
(`#95 <https://github.com/cppformat/cppformat/issues/95>`_,
`#96 <https://github.com/cppformat/cppformat/issues/96>`_,
`#114 <https://github.com/cppformat/cppformat/pull/114>`_,
`#135 <https://github.com/cppformat/cppformat/issues/135>`_,
`#142 <https://github.com/cppformat/cppformat/issues/142>`_,
`#145 <https://github.com/cppformat/cppformat/issues/145>`_,
`#146 <https://github.com/cppformat/cppformat/issues/146>`_,
`#158 <https://github.com/cppformat/cppformat/issues/158>`_,
`#163 <https://github.com/cppformat/cppformat/issues/163>`_,
`#175 <https://github.com/cppformat/cppformat/issues/175>`_,
`#190 <https://github.com/cppformat/cppformat/issues/190>`_,
`#191 <https://github.com/cppformat/cppformat/pull/191>`_,
`#194 <https://github.com/cppformat/cppformat/issues/194>`_,
`#196 <https://github.com/cppformat/cppformat/pull/196>`_,
`#216 <https://github.com/cppformat/cppformat/issues/216>`_,
`#218 <https://github.com/cppformat/cppformat/pull/218>`_,
`#220 <https://github.com/cppformat/cppformat/pull/220>`_,
`#229 <https://github.com/cppformat/cppformat/pull/229>`_,
`#233 <https://github.com/cppformat/cppformat/issues/233>`_,
`#234 <https://github.com/cppformat/cppformat/issues/234>`_,
`#236 <https://github.com/cppformat/cppformat/pull/236>`_).
(`#95 <https://github.com/fmtlib/fmt/issues/95>`_,
`#96 <https://github.com/fmtlib/fmt/issues/96>`_,
`#114 <https://github.com/fmtlib/fmt/pull/114>`_,
`#135 <https://github.com/fmtlib/fmt/issues/135>`_,
`#142 <https://github.com/fmtlib/fmt/issues/142>`_,
`#145 <https://github.com/fmtlib/fmt/issues/145>`_,
`#146 <https://github.com/fmtlib/fmt/issues/146>`_,
`#158 <https://github.com/fmtlib/fmt/issues/158>`_,
`#163 <https://github.com/fmtlib/fmt/issues/163>`_,
`#175 <https://github.com/fmtlib/fmt/issues/175>`_,
`#190 <https://github.com/fmtlib/fmt/issues/190>`_,
`#191 <https://github.com/fmtlib/fmt/pull/191>`_,
`#194 <https://github.com/fmtlib/fmt/issues/194>`_,
`#196 <https://github.com/fmtlib/fmt/pull/196>`_,
`#216 <https://github.com/fmtlib/fmt/issues/216>`_,
`#218 <https://github.com/fmtlib/fmt/pull/218>`_,
`#220 <https://github.com/fmtlib/fmt/pull/220>`_,
`#229 <https://github.com/fmtlib/fmt/pull/229>`_,
`#233 <https://github.com/fmtlib/fmt/issues/233>`_,
`#234 <https://github.com/fmtlib/fmt/issues/234>`_,
`#236 <https://github.com/fmtlib/fmt/pull/236>`_,
`#281 <https://github.com/fmtlib/fmt/issues/281>`_,
`#289 <https://github.com/fmtlib/fmt/issues/289>`_).
Thanks to `@seanmiddleditch (Sean Middleditch) <https://github.com/seanmiddleditch>`_,
`@dixlorenz (Dix Lorenz) <https://github.com/dixlorenz>`_,
`@CarterLi (李通洲) <https://github.com/CarterLi>`_,
@ -319,36 +646,36 @@ Fixes
* Fixed portability issues (mostly causing test failures) on ARM, ppc64, ppc64le,
s390x and SunOS 5.11 i386 (
`#138 <https://github.com/cppformat/cppformat/issues/138>`_,
`#179 <https://github.com/cppformat/cppformat/issues/179>`_,
`#180 <https://github.com/cppformat/cppformat/issues/180>`_,
`#202 <https://github.com/cppformat/cppformat/issues/202>`_,
`#225 <https://github.com/cppformat/cppformat/issues/225>`_,
`#138 <https://github.com/fmtlib/fmt/issues/138>`_,
`#179 <https://github.com/fmtlib/fmt/issues/179>`_,
`#180 <https://github.com/fmtlib/fmt/issues/180>`_,
`#202 <https://github.com/fmtlib/fmt/issues/202>`_,
`#225 <https://github.com/fmtlib/fmt/issues/225>`_,
`Red Hat Bugzilla Bug 1260297 <https://bugzilla.redhat.com/show_bug.cgi?id=1260297>`_).
Thanks to `@Naios <https://github.com/Naios>`_,
`@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_ and Dave Johansen.
* Fixed a name conflict with macro ``free`` defined in
``crtdbg.h`` when ``_CRTDBG_MAP_ALLOC`` is set
(`#211 <https://github.com/cppformat/cppformat/issues/211>`_).
(`#211 <https://github.com/fmtlib/fmt/issues/211>`_).
* Fixed shared library build on OS X
(`#212 <https://github.com/cppformat/cppformat/pull/212>`_).
(`#212 <https://github.com/fmtlib/fmt/pull/212>`_).
Thanks to `@dean0x7d (Dean Moldovan) <https://github.com/dean0x7d>`_.
* Fixed an overload conflict on MSVC when ``/Zc:wchar_t-`` option is specified
(`#214 <https://github.com/cppformat/cppformat/pull/214>`_).
(`#214 <https://github.com/fmtlib/fmt/pull/214>`_).
Thanks to `@slavanap (Vyacheslav Napadovsky) <https://github.com/slavanap>`_.
* Improved compatibility with MSVC 2008
(`#236 <https://github.com/cppformat/cppformat/pull/236>`_).
(`#236 <https://github.com/fmtlib/fmt/pull/236>`_).
Thanks to `@Jopie64 (Johan) <https://github.com/Jopie64>`_.
* Improved compatibility with bcc32
(`#227 <https://github.com/cppformat/cppformat/issues/227>`_).
(`#227 <https://github.com/fmtlib/fmt/issues/227>`_).
* Fixed ``static_assert`` detection on Clang
(`#228 <https://github.com/cppformat/cppformat/pull/228>`_).
(`#228 <https://github.com/fmtlib/fmt/pull/228>`_).
Thanks to `@dean0x7d (Dean Moldovan) <https://github.com/dean0x7d>`_.
1.1.0 - 2015-03-06
@ -356,8 +683,8 @@ Fixes
* Added ``BasicArrayWriter``, a class template that provides operations for
formatting and writing data into a fixed-size array
(`#105 <https://github.com/cppformat/cppformat/issues/105>`_ and
`#122 <https://github.com/cppformat/cppformat/issues/122>`_):
(`#105 <https://github.com/fmtlib/fmt/issues/105>`_ and
`#122 <https://github.com/fmtlib/fmt/issues/122>`_):
.. code:: c++
@ -369,58 +696,58 @@ Fixes
<http://www.polserver.com/>`_ to the list of notable projects using C++ Format.
* C++ Format now uses MSVC intrinsics for better formatting performance
(`#115 <https://github.com/cppformat/cppformat/pull/115>`_,
`#116 <https://github.com/cppformat/cppformat/pull/116>`_,
`#118 <https://github.com/cppformat/cppformat/pull/118>`_ and
`#121 <https://github.com/cppformat/cppformat/pull/121>`_).
(`#115 <https://github.com/fmtlib/fmt/pull/115>`_,
`#116 <https://github.com/fmtlib/fmt/pull/116>`_,
`#118 <https://github.com/fmtlib/fmt/pull/118>`_ and
`#121 <https://github.com/fmtlib/fmt/pull/121>`_).
Previously these optimizations where only used on GCC and Clang.
Thanks to `@CarterLi <https://github.com/CarterLi>`_ and
`@objectx <https://github.com/objectx>`_.
* CMake install target (`#119 <https://github.com/cppformat/cppformat/pull/119>`_).
* CMake install target (`#119 <https://github.com/fmtlib/fmt/pull/119>`_).
Thanks to `@TrentHouliston <https://github.com/TrentHouliston>`_.
You can now install C++ Format with ``make install`` command.
* Improved `Biicode <http://www.biicode.com/>`_ support
(`#98 <https://github.com/cppformat/cppformat/pull/98>`_ and
`#104 <https://github.com/cppformat/cppformat/pull/104>`_). Thanks to
(`#98 <https://github.com/fmtlib/fmt/pull/98>`_ and
`#104 <https://github.com/fmtlib/fmt/pull/104>`_). Thanks to
`@MariadeAnton <https://github.com/MariadeAnton>`_ and
`@franramirez688 <https://github.com/franramirez688>`_.
* Improved support for bulding with `Android NDK
* Improved support for building with `Android NDK
<https://developer.android.com/tools/sdk/ndk/index.html>`_
(`#107 <https://github.com/cppformat/cppformat/pull/107>`_).
(`#107 <https://github.com/fmtlib/fmt/pull/107>`_).
Thanks to `@newnon <https://github.com/newnon>`_.
The `android-ndk-example <https://github.com/cppformat/android-ndk-example>`_
The `android-ndk-example <https://github.com/fmtlib/android-ndk-example>`_
repository provides and example of using C++ Format with Android NDK:
.. image:: https://raw.githubusercontent.com/cppformat/android-ndk-example/
.. image:: https://raw.githubusercontent.com/fmtlib/android-ndk-example/
master/screenshot.png
* Improved documentation of ``SystemError`` and ``WindowsError``
(`#54 <https://github.com/cppformat/cppformat/issues/54>`_).
(`#54 <https://github.com/fmtlib/fmt/issues/54>`_).
* Various code improvements
(`#110 <https://github.com/cppformat/cppformat/pull/110>`_,
`#111 <https://github.com/cppformat/cppformat/pull/111>`_
`#112 <https://github.com/cppformat/cppformat/pull/112>`_).
(`#110 <https://github.com/fmtlib/fmt/pull/110>`_,
`#111 <https://github.com/fmtlib/fmt/pull/111>`_
`#112 <https://github.com/fmtlib/fmt/pull/112>`_).
Thanks to `@CarterLi <https://github.com/CarterLi>`_.
* Improved compile-time errors when formatting wide into narrow strings
(`#117 <https://github.com/cppformat/cppformat/issues/117>`_).
(`#117 <https://github.com/fmtlib/fmt/issues/117>`_).
* Fixed ``BasicWriter::write`` without formatting arguments when C++11 support
is disabled (`#109 <https://github.com/cppformat/cppformat/issues/109>`_).
is disabled (`#109 <https://github.com/fmtlib/fmt/issues/109>`_).
* Fixed header-only build on OS X with GCC 4.9
(`#124 <https://github.com/cppformat/cppformat/issues/124>`_).
(`#124 <https://github.com/fmtlib/fmt/issues/124>`_).
* Fixed packaging issues (`#94 <https://github.com/cppformat/cppformat/issues/94>`_).
* Fixed packaging issues (`#94 <https://github.com/fmtlib/fmt/issues/94>`_).
* Added `changelog <https://github.com/cppformat/cppformat/blob/master/ChangeLog.rst>`_
(`#103 <https://github.com/cppformat/cppformat/issues/103>`_).
* Added `changelog <https://github.com/fmtlib/fmt/blob/master/ChangeLog.rst>`_
(`#103 <https://github.com/fmtlib/fmt/issues/103>`_).
1.0.0 - 2015-02-05
------------------
@ -435,29 +762,29 @@ Fixes
* Compute string length in the constructor of ``BasicStringRef``
instead of the ``size`` method
(`#79 <https://github.com/cppformat/cppformat/issues/79>`_).
(`#79 <https://github.com/fmtlib/fmt/issues/79>`_).
This eliminates size computation for string literals on reasonable optimizing
compilers.
* Fix formatting of types with overloaded ``operator <<`` for ``std::wostream``
(`#86 <https://github.com/cppformat/cppformat/issues/86>`_):
(`#86 <https://github.com/fmtlib/fmt/issues/86>`_):
.. code:: c++
fmt::format(L"The date is {0}", Date(2012, 12, 9));
* Fix linkage of tests on Arch Linux
(`#89 <https://github.com/cppformat/cppformat/issues/89>`_).
(`#89 <https://github.com/fmtlib/fmt/issues/89>`_).
* Allow precision specifier for non-float arguments
(`#90 <https://github.com/cppformat/cppformat/issues/90>`_):
(`#90 <https://github.com/fmtlib/fmt/issues/90>`_):
.. code:: c++
fmt::print("{:.3}\n", "Carpet"); // prints "Car"
* Fix build on Android NDK
(`#93 <https://github.com/cppformat/cppformat/issues/93>`_)
(`#93 <https://github.com/fmtlib/fmt/issues/93>`_)
* Improvements to documentation build procedure.
@ -498,17 +825,17 @@ Fixes
This doesn't affect the formatting API.
* Support for custom memory allocators
(`#69 <https://github.com/cppformat/cppformat/issues/69>`_)
(`#69 <https://github.com/fmtlib/fmt/issues/69>`_)
* Formatting functions now accept `signed char` and `unsigned char` strings as
arguments (`#73 <https://github.com/cppformat/cppformat/issues/73>`_):
arguments (`#73 <https://github.com/fmtlib/fmt/issues/73>`_):
.. code:: c++
auto s = format("GLSL version: {}", glGetString(GL_VERSION));
* Reduced code bloat. According to the new `benchmark results
<https://github.com/cppformat/cppformat#compile-time-and-code-bloat>`_,
<https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_,
cppformat is close to ``printf`` and by the order of magnitude better than
Boost Format in terms of compiled code size.
@ -538,7 +865,7 @@ Fixes
fmt::printf("%1$s, %3$d %2$s", weekday, month, day);
* Arguments of ``char`` type can now be formatted as integers
(Issue `#55 <https://github.com/cppformat/cppformat/issues/55>`_):
(Issue `#55 <https://github.com/fmtlib/fmt/issues/55>`_):
.. code:: c++
@ -574,7 +901,7 @@ Fixes
Apart from a more natural syntax, this also improves performance as there
is no need to construct temporary formatter objects and control arguments'
lifetimes. Because the wrapper functions are very ligthweight, this doesn't
lifetimes. Because the wrapper functions are very lightweight, this doesn't
cause code bloat even in pre-C++11 mode.
* Simplified common case of formatting an ``std::string``. Now it requires a
@ -607,7 +934,7 @@ Fixes
Now all public functions are lowercase following the standard library
conventions. Previously it was a combination of lowercase and
CapitalizedWords.
Issue `#50 <https://github.com/cppformat/cppformat/issues/50>`_.
Issue `#50 <https://github.com/fmtlib/fmt/issues/50>`_.
* Old functions are marked as deprecated and will be removed in the next
release.

View File

@ -1,4 +1,4 @@
Copyright (c) 2012 - 2015, Victor Zverovich
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.

View File

@ -1,62 +1,64 @@
C++ Format
==========
{fmt}
=====
.. image:: https://travis-ci.org/cppformat/cppformat.png?branch=master
:target: https://travis-ci.org/cppformat/cppformat
.. image:: https://travis-ci.org/fmtlib/fmt.png?branch=master
:target: https://travis-ci.org/fmtlib/fmt
.. image:: https://ci.appveyor.com/api/projects/status/qk0bhyhqp1ekpat8
:target: https://ci.appveyor.com/project/vitaut/cppformat
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
:target: https://ci.appveyor.com/project/vitaut/fmt
.. image:: https://badges.gitter.im/Join%20Chat.svg
:alt: Join the chat at https://gitter.im/cppformat/cppformat
:target: https://gitter.im/cppformat/cppformat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
:alt: Join the chat at https://gitter.im/fmtlib/fmt
:target: https://gitter.im/fmtlib/fmt
C++ Format is an open-source formatting library for C++.
**fmt** is an open-source formatting library for C++.
It can be used as a safe alternative to printf or as a fast
alternative to IOStreams.
`Documentation <http://cppformat.github.io/latest/>`_
`Documentation <http://fmtlib.net/latest/>`_
Features
--------
* Two APIs: faster concatenation-based write API and slower (but still
very fast) replacement-based format API with positional arguments for
localization.
* Two APIs: faster concatenation-based `write API
<http://fmtlib.net/latest/api.html#write-api>`_ and slower,
but still very fast, replacement-based `format API
<http://fmtlib.net/latest/api.html#format-api>`_ with positional arguments
for localization.
* Write API similar to the one used by IOStreams but stateless allowing
faster implementation.
* Format API with `format string syntax
<http://cppformat.github.io/latest/syntax.html>`_
<http://fmtlib.net/latest/syntax.html>`_
similar to the one used by `str.format
<http://docs.python.org/2/library/stdtypes.html#str.format>`_ in Python.
<https://docs.python.org/2/library/stdtypes.html#str.format>`_ in Python.
* Safe `printf implementation
<http://cppformat.github.io/latest/reference.html#printf-formatting-functions>`_
<http://fmtlib.net/latest/api.html#printf-formatting-functions>`_
including the POSIX extension for positional arguments.
* Support for user-defined types.
* High speed: performance of the format API is close to that of
glibc's `printf <http://en.cppreference.com/w/cpp/io/c/fprintf>`_
and better than performance of IOStreams. See `Speed tests`_ and
and better than the performance of IOStreams. See `Speed tests`_ and
`Fast integer to string conversion in C++
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
* Small code size both in terms of source code (format consists of a single
* Small code size both in terms of source code (the core library consists of a single
header file and a single source file) and compiled code.
See `Compile time and code bloat`_.
* Reliability: the library has an extensive set of `unit tests
<https://github.com/cppformat/cppformat/tree/master/test>`_.
<https://github.com/fmtlib/fmt/tree/master/test>`_.
* Safety: the library is fully type safe, errors in format strings are
reported using exceptions, automatic memory management prevents buffer
overflow errors.
* Ease of use: small self-contained code base, no external dependencies,
permissive BSD `license
<https://github.com/cppformat/cppformat/blob/master/LICENSE.rst>`_
* `Portability <http://cppformat.github.io#portability>`_ with consistent output
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
* `Portability <http://fmtlib.net/latest/index.html#portability>`_ with consistent output
across platforms and support for older compilers.
* Clean warning-free codebase even on high warning levels
(-Wall -Wextra -pedantic).
* Support for wide strings.
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro.
See the `documentation <http://cppformat.github.io/latest/>`_ for more details.
See the `documentation <http://fmtlib.net/latest/>`_ for more details.
Examples
--------
@ -75,7 +77,7 @@ Arguments can be accessed by position and arguments' indices can be repeated:
std::string s = fmt::format("{0}{1}{0}", "abra", "cad");
// s == "abracadabra"
C++ Format can be used as a safe portable replacement for ``itoa``:
fmt can be used as a safe portable replacement for ``itoa``:
.. code:: c++
@ -89,6 +91,8 @@ An object of any user-defined type for which there is an overloaded
.. code:: c++
#include "fmt/ostream.h"
class Date {
int year_, month_, day_;
public:
@ -103,10 +107,10 @@ An object of any user-defined type for which there is an overloaded
// s == "The date is 2012-12-9"
You can use the `FMT_VARIADIC
<http://cppformat.github.io/latest/reference.html#utilities>`_
<http://fmtlib.net/latest/api.html#utilities>`_
macro to create your own functions similar to `format
<http://cppformat.github.io/latest/reference.html#format>`_ and
`print <http://cppformat.github.io/latest/reference.html#print>`_
<http://fmtlib.net/latest/api.html#format>`_ and
`print <http://fmtlib.net/latest/api.html#print>`_
which take arbitrary arguments:
.. code:: c++
@ -132,13 +136,25 @@ Projects using this library
* `AMPL/MP <https://github.com/ampl/mp>`_:
An open-source library for mathematical programming
* `HarpyWar/pvpgn <https://github.com/HarpyWar/pvpgn>`_:
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater vehicle
* `Drake <http://drake.mit.edu/>`_: A planning, control, and analysis toolbox for nonlinear dynamical systems (MIT)
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus (Lyft)
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
Player vs Player Gaming Network with tweaks
* `KBEngine <http://www.kbengine.org/>`_: An open-source MMOG server engine
* `KBEngine <http://kbengine.org/>`_: An open-source MMOG server engine
* `Keypirinha <http://keypirinha.com/>`_: A semantic launcher for Windows
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to generate randomized datasets
* `PenUltima Online (POL) <http://www.polserver.com/>`_:
An MMO server, compatible with most Ultima Online clients
@ -148,12 +164,18 @@ Projects using this library
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster proxy
* `Saddy <https://code.google.com/p/saddy/>`_:
* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_:
Small crossplatform 2D graphic engine
* `Salesforce Analytics Cloud <http://www.salesforce.com/analytics-cloud/overview/>`_:
Business intelligence software
* `Scylla <http://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store that can handle
1 million transactions per second on a single server
* `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++ framework for
high-performance server applications on modern hardware
* `spdlog <https://github.com/gabime/spdlog>`_: Super fast C++ logging library
* `Stellar <https://www.stellar.org/>`_: Financial platform
@ -166,7 +188,7 @@ Projects using this library
If you are aware of other projects using this library, please let me know
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
`issue <https://github.com/cppformat/cppformat/issues>`_.
`issue <https://github.com/fmtlib/fmt/issues>`_.
Motivation
----------
@ -188,7 +210,7 @@ doesn't support user-defined types. Printf also has safety issues although
they are mostly solved with `__attribute__ ((format (printf, ...))
<http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
There is a POSIX extension that adds positional arguments required for
`i18n <http://en.wikipedia.org/wiki/Internationalization_and_localization>`_
`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_
to printf but it is not a part of C99 and may not be available on some
platforms.
@ -277,14 +299,14 @@ The following speed tests results were generated by building
runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` or
equivalent is filled 2000000 times with output sent to ``/dev/null``; for
further details see the `source
<https://github.com/cppformat/format-benchmark/blob/master/tinyformat_test.cpp>`_.
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
EGLIBC 2.19 printf 1.30
libstdc++ 4.8.2 std::ostream 1.85
C++ Format 1.0 fmt::print 1.42
fmt 1.0 fmt::print 1.42
tinyformat 2.0.1 tfm::printf 2.25
Boost Format 1.54 boost::format 9.94
================= ============= ===========
@ -293,7 +315,7 @@ As you can see ``boost::format`` is much slower than the alternative methods; th
is confirmed by `other tests <http://accu.org/index.php/journals/1539>`_.
Tinyformat is quite good coming close to IOStreams. Unfortunately tinyformat
cannot be faster than the IOStreams because it uses them internally.
Performance of cppformat is close to that of printf, being `faster than printf on integer
Performance of fmt is close to that of printf, being `faster than printf on integer
formatting <http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_,
but slower on floating-point formatting which dominates this benchmark.
@ -301,8 +323,8 @@ Compile time and code bloat
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The script `bloat-test.py
<https://github.com/cppformat/format-benchmark/blob/master/bloat-test.py>`_
from `format-benchmark <https://github.com/cppformat/format-benchmark>`_
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
tests compile time and code bloat for nontrivial projects.
It generates 100 translation units and uses ``printf()`` or its alternative
five times in each to simulate a medium sized project. The resulting
@ -316,12 +338,12 @@ Method Compile Time, s Executable size, KiB Stripped size, KiB
============ =============== ==================== ==================
printf 2.6 41 30
IOStreams 19.4 92 70
C++ Format 46.8 46 34
fmt 46.8 46 34
tinyformat 64.6 418 386
Boost Format 222.8 990 923
============ =============== ==================== ==================
As you can see, C++ Format has two times less overhead in terms of resulting
As you can see, fmt has two times less overhead in terms of resulting
code size compared to IOStreams and comes pretty close to ``printf``.
Boost Format has by far the largest overheads.
@ -332,12 +354,12 @@ Method Compile Time, s Executable size, KiB Stripped size, KiB
============ =============== ==================== ==================
printf 2.1 41 30
IOStreams 19.7 86 62
C++ Format 47.9 108 86
fmt 47.9 108 86
tinyformat 27.7 234 190
Boost Format 122.6 884 763
============ =============== ==================== ==================
``libc``, ``libstdc++`` and ``libformat`` are all linked as shared
``libc``, ``libstdc++`` and ``libfmt`` are all linked as shared
libraries to compare formatting function overhead only. Boost Format
and tinyformat are header-only libraries so they don't provide any
linkage options.
@ -348,14 +370,14 @@ Running the tests
Please refer to `Building the library`__ for the instructions on how to build
the library and run the unit tests.
__ http://cppformat.github.io/latest/usage.html#building-the-library
__ http://fmtlib.net/latest/usage.html#building-the-library
Benchmarks reside in a separate repository,
`format-benchmarks <https://github.com/cppformat/format-benchmark>`_,
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
so to run the benchmarks you first need to clone this repository and
generate Makefiles with CMake::
$ git clone --recursive https://github.com/cppformat/format-benchmark.git
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git
$ cd format-benchmark
$ cmake .
@ -370,27 +392,27 @@ or the bloat test::
License
-------
C++ Format is distributed under the BSD `license
<https://github.com/cppformat/cppformat/blob/master/LICENSE.rst>`_.
fmt is distributed under the BSD `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
The `Format String Syntax
<http://cppformat.github.io/latest/syntax.html>`_
<http://fmtlib.net/latest/syntax.html>`_
section in the documentation is based on the one from Python `string module
documentation <http://docs.python.org/3/library/string.html#module-string>`_
documentation <https://docs.python.org/3/library/string.html#module-string>`_
adapted for the current library. For this reason the documentation is
distributed under the Python Software Foundation license available in
`doc/python-license.txt
<https://raw.github.com/cppformat/cppformat/master/doc/python-license.txt>`_.
It only applies if you distribute the documentation of C++ Format.
Links
-----
`API changes/compatibility report <http://upstream-tracker.org/versions/cppformat.html>`_
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
It only applies if you distribute the documentation of fmt.
Acknowledgments
---------------
The fmt library is maintained by Victor Zverovich (`vitaut <https://github.com/vitaut>`_)
and Jonathan Müller (`foonathan <https://github.com/foonathan>`_) with contributions from many
other people. See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and `Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names. Let us know if your contribution
is not listed or mentioned incorrectly and we'll make it right.
The benchmark section of this readme file and the performance tests are taken
from the excellent `tinyformat <https://github.com/c42f/tinyformat>`_ library
written by Chris Foster. Boost Format library is acknowledged transitively

View File

@ -5,7 +5,8 @@ if (NOT DOXYGEN)
endif ()
add_custom_target(doc
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.py ${CPPFORMAT_VERSION})
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.py ${FMT_VERSION}
SOURCES build.py conf.py _templates/layout.html)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
DESTINATION share/doc/cppformat)
DESTINATION share/doc/fmt OPTIONAL)

View File

@ -1,25 +1,28 @@
{% extends "!layout.html" %}
{% block extrahead %}
<meta name="description" content="Small, safe and fast formatting library for C++">
<meta name="description" content="Small, safe and fast formatting library">
<meta name="keywords" content="C++, formatting, printf, string, library">
<meta name="author" content="Victor Zverovich">
<link rel="stylesheet" href="_static/cppformat.css">
<link rel="stylesheet" href="_static/fmt.css">
{# Google Analytics #}
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();
a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;
a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-20116650-4', 'cppformat.github.io');
ga('create', 'UA-20116650-4', 'fmtlib.net');
ga('send', 'pageview');
</script>
{% endblock %}
{%- macro searchform(classes, button) %}
<form class="{{classes}}" role="search" action="{{ pathto('search') }}" method="get">
<form class="{{classes}}" role="search" action="{{ pathto('search') }}"
method="get">
<div class="form-group">
<input type="text" name="q" class="form-control" {{ 'placeholder="Search"' if not button }} >
<input type="text" name="q" class="form-control"
{{ 'placeholder="Search"' if not button }} >
</div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
@ -36,30 +39,33 @@
<div class="navbar-content">
{# Brand and toggle get grouped for better mobile display #}
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">C++ Format</a>
<a class="navbar-brand" href="index.html">{fmt}</a>
</div>
{# Collect the nav links, forms, and other content for toggling #}
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
{# TODO: update versions automatically #}
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-expanded="false">{{ version }} <span class="caret"></span></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
role="button" aria-expanded="false">{{ version }}
<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="http://cppformat.github.io/1.1.0/">1.1.0</a></li>
<li><a href="http://cppformat.github.io/1.0.0/">1.0.0</a></li>
{% for v in versions.split(',') %}
<li><a href="http://fmtlib.net/{{v}}">{{v}}</a></li>
{% endfor %}
</ul>
</li>
{% for name in ['Contents', 'Usage', 'API', 'Syntax'] %}
{% if pagename == name.lower() %}
<li class="active"><a href="{{name.lower()}}.html">{{name}} <span class="sr-only">(current)</span></a></li>
<li class="active"><a href="{{name.lower()}}.html">{{name}}
<span class="sr-only">(current)</span></a></li>
{%else%}
<li><a href="{{name.lower()}}.html">{{name}}</a></li>
{%endif%}
@ -74,20 +80,25 @@
</div> {# /.tb-container #}
</nav>
{% if pagename == "index" %}
{% set download_url = 'https://github.com/fmtlib/fmt/releases/download' %}
<div class="jumbotron">
<div class="tb-container">
<h1>C++ Format</h1>
<p class="lead">Small, safe and fast formatting library for C++</p>
<h1>{fmt}</h1>
<p class="lead">Small, safe and fast formatting library</p>
<div class="btn-group" role="group">
{% set name = 'fmt' if version.split('.')[0]|int >= 3 else 'cppformat' %}
<a class="btn btn-success"
href="https://github.com/cppformat/cppformat/releases/download/2.0.0/cppformat-2.0.0.zip">
<span class="glyphicon glyphicon-download"></span> Download
href="{{download_url}}/{{version}}/{{name}}-{{version}}.zip">
<span class="glyphicon glyphicon-download"></span> Download
</a>
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>
<button type="button" class="btn btn-success dropdown-toggle"
data-toggle="dropdown"><span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a href="https://github.com/cppformat/cppformat/releases/download/2.0.0/cppformat-2.0.0.zip">Version 2.0.0</a></li>
<li><a href="https://github.com/cppformat/cppformat/releases/download/1.1.0/cppformat-1.1.0.zip">Version 1.1.0</a></li>
<li><a href="https://github.com/cppformat/cppformat/releases/download/1.0.0/cppformat-1.0.0.zip">Version 1.0.0</a></li>
{% for v in versions.split(',') %}
{% set name = 'fmt' if v.split('.')[0]|int >= 3 else 'cppformat' %}
<li><a href="{{download_url}}/{{v}}/{{name}}-{{v}}.zip">Version {{v}}
</a></li>
{% endfor %}
</ul>
</div>
</div>
@ -104,14 +115,15 @@
{% block content %}
<div class="tb-container">
<div class="row">
{# TODO: integrate sidebar
{# Sidebar is currently disabled.
<div class="bs-sidebar">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
{%- block sidebarlogo %}
{%- if logo %}
<p class="logo"><a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
<img class="logo" src="{{ pathto('_static/' + logo, 1) }}"
alt="Logo"/>
</a></p>
{%- endif %}
{%- endblock %}

View File

@ -4,15 +4,15 @@
API Reference
*************
All functions and classes provided by the C++ Format library reside
All functions and classes provided by the fmt library reside
in namespace ``fmt`` and macros have prefix ``FMT_``. For brevity the
namespace is usually omitted in examples.
Formatting functions
====================
Format API
==========
The following functions use :ref:`format string syntax <syntax>` similar
to the one used by Python's `str.format
The following functions defined in ``fmt/format.h`` use :ref:`format string
syntax <syntax>` similar to the one used by Python's `str.format
<http://docs.python.org/3/library/stdtypes.html#str.format>`_ function.
They take *format_str* and *args* as arguments.
@ -22,6 +22,11 @@ arguments in the resulting string.
*args* is an argument list representing arbitrary arguments.
The `performance of the format API
<https://github.com/fmtlib/fmt/blob/master/README.rst#speed-tests>`_ is close
to that of glibc's ``printf`` and better than the performance of IOStreams.
For even better speed use the `write API`_.
.. _format:
.. doxygenfunction:: format(CStringRef, ArgList)
@ -34,24 +39,158 @@ arguments in the resulting string.
.. doxygenfunction:: print(std::FILE *, CStringRef, ArgList)
.. doxygenclass:: fmt::BasicFormatter
:members:
Date and time formatting
------------------------
The library supports `strftime
<http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like date and time
formatting::
#include "fmt/time.h"
std::time_t t = std::time(nullptr);
// Prints "The date is 2016-04-29." (with the current date)
fmt::print("The date is {:%Y-%m-%d}.", *std::localtime(&t));
The format string syntax is described in the documentation of
`strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_.
Formatting user-defined types
-----------------------------
A custom ``format_arg`` function may be implemented and used to format any
user-defined type. That is how date and time formatting described in the
previous section is implemented in :file:`fmt/time.h`. The following example
shows how to implement custom formatting for a user-defined structure.
::
struct MyStruct { double a, b; };
void format_arg(fmt::BasicFormatter<char> &f,
const char *&format_str, const MyStruct &s) {
f.writer().write("[MyStruct: a={:.1f}, b={:.2f}]", s.a, s.b);
}
MyStruct m = { 1, 2 };
std::string s = fmt::format("m={}", n);
// s == "m=[MyStruct: a=1.0, b=2.00]"
Note in the example above the ``format_arg`` function ignores the contents of
``format_str`` so the type will always be formatted as specified. See
``format_arg`` in :file:`fmt/time.h` for an advanced example of how to use
the ``format_str`` argument to customize the formatted output.
This section shows how to define a custom format function for a user-defined
type. The next section describes how to get ``fmt`` to use a conventional stream
output ``operator<<`` when one is defined for a user-defined type.
``std::ostream`` support
------------------------
The header ``fmt/ostream.h`` provides ``std::ostream`` support including
formatting of user-defined types that have overloaded ``operator<<``::
#include "fmt/ostream.h"
class Date {
int year_, month_, day_;
public:
Date(int year, int month, int day): year_(year), month_(month), day_(day) {}
friend std::ostream &operator<<(std::ostream &os, const Date &d) {
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};
std::string s = fmt::format("The date is {}", Date(2012, 12, 9));
// s == "The date is 2012-12-9"
.. doxygenfunction:: print(std::ostream&, CStringRef, ArgList)
Printf formatting functions
===========================
Argument formatters
-------------------
It is possible to change the way arguments are formatted by providing a
custom argument formatter class::
// A custom argument formatter that formats negative integers as unsigned
// with the ``x`` format specifier.
class CustomArgFormatter :
public fmt::BasicArgFormatter<CustomArgFormatter, char> {
public:
CustomArgFormatter(fmt::BasicFormatter<char, CustomArgFormatter> &f,
fmt::FormatSpec &s, const char *fmt)
: fmt::BasicArgFormatter<CustomArgFormatter, char>(f, s, fmt) {}
void visit_int(int value) {
if (spec().type() == 'x')
visit_uint(value); // convert to unsigned and format
else
fmt::BasicArgFormatter<CustomArgFormatter, char>::visit_int(value);
}
};
std::string custom_format(const char *format_str, fmt::ArgList args) {
fmt::MemoryWriter writer;
// Pass custom argument formatter as a template arg to BasicFormatter.
fmt::BasicFormatter<char, CustomArgFormatter> formatter(args, writer);
formatter.format(format_str);
return writer.str();
}
FMT_VARIADIC(std::string, custom_format, const char *)
std::string s = custom_format("{:x}", -42); // s == "ffffffd6"
.. doxygenclass:: fmt::ArgVisitor
:members:
.. doxygenclass:: fmt::BasicArgFormatter
:members:
.. doxygenclass:: fmt::ArgFormatter
:members:
Printf formatting
-----------------
The header ``fmt/printf.h`` provides ``printf``-like formatting functionality.
The following functions use `printf format string syntax
<http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html>`_ with
a POSIX extension for positional arguments.
the POSIX extension for positional arguments. Unlike their standard
counterparts, the ``fmt`` functions are type-safe and throw an exception if an
argument type doesn't match its format specification.
.. doxygenfunction:: printf(CStringRef, ArgList)
.. doxygenfunction:: fprintf(std::FILE*, CStringRef, ArgList)
.. doxygenfunction:: fprintf(std::FILE *, CStringRef, ArgList)
.. doxygenfunction:: fprintf(std::ostream&, CStringRef, ArgList)
.. doxygenfunction:: sprintf(CStringRef, ArgList)
.. doxygenclass:: fmt::PrintfFormatter
:members:
.. doxygenclass:: fmt::BasicPrintfArgFormatter
:members:
.. doxygenclass:: fmt::PrintfArgFormatter
:members:
Write API
=========
The write API provides classes for writing formatted data into character
streams. It is usually faster than the `format API`_ but, as IOStreams,
may result in larger compiled code size. The main writer class is
`~fmt::BasicMemoryWriter` which stores its output in a memory buffer and
provides direct access to it. It is possible to create custom writers that
store output elsewhere by subclassing `~fmt::BasicWriter`.
.. doxygenclass:: fmt::BasicWriter
:members:
@ -61,13 +200,19 @@ Write API
.. doxygenclass:: fmt::BasicArrayWriter
:members:
.. doxygenfunction:: bin
.. doxygenclass:: fmt::BasicStringWriter
:members:
.. doxygenfunction:: oct
.. doxygenclass:: fmt::BasicContainerWriter
:members:
.. doxygenfunction:: hex
.. doxygenfunction:: bin(int)
.. doxygenfunction:: hexu
.. doxygenfunction:: oct(int)
.. doxygenfunction:: hex(int)
.. doxygenfunction:: hexu(int)
.. doxygenfunction:: pad(int, unsigned, Char)
@ -85,6 +230,8 @@ Utilities
.. doxygenclass:: fmt::ArgList
:members:
.. doxygenfunction:: fmt::to_string(const T&)
.. doxygenclass:: fmt::BasicStringRef
:members:
@ -95,12 +242,14 @@ Utilities
:protected-members:
:members:
System Errors
System errors
=============
.. doxygenclass:: fmt::SystemError
:members:
.. doxygenfunction:: fmt::format_system_error
.. doxygenclass:: fmt::WindowsError
:members:
@ -109,7 +258,7 @@ System Errors
Custom allocators
=================
The C++ Format library supports custom dynamic memory allocators.
The fmt library supports custom dynamic memory allocators.
A custom allocator class can be specified as a template argument to
:class:`fmt::BasicMemoryWriter`::
@ -118,7 +267,8 @@ A custom allocator class can be specified as a template argument to
It is also possible to write a formatting function that uses a custom
allocator::
typedef std::basic_string<char, std::char_traits<char>, CustomAllocator> CustomString;
typedef std::basic_string<char, std::char_traits<char>, CustomAllocator>
CustomString;
CustomString format(CustomAllocator alloc, fmt::CStringRef format_str,
fmt::ArgList args) {

View File

@ -90,7 +90,8 @@
VERSION: '{{ release|e }}',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
HAS_SOURCE: {{ has_source|lower }}
HAS_SOURCE: {{ has_source|lower }},
SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}'
};
</script>
{%- for scriptfile in script_files %}

View File

@ -8,30 +8,35 @@ from distutils.version import LooseVersion
def pip_install(package, commit=None, **kwargs):
"Install package using pip."
min_version = kwargs.get('min_version')
if min_version:
from pkg_resources import get_distribution, DistributionNotFound
try:
installed_version = get_distribution(os.path.basename(package)).version
if LooseVersion(installed_version) >= min_version:
print('{} {} already installed'.format(package, min_version))
return
except DistributionNotFound:
pass
if commit:
check_version = kwargs.get('check_version', '')
#output = check_output(['pip', 'show', package.split('/')[1]])
#if check_version in output:
# print('{} already installed'.format(package))
# return
package = 'git+git://github.com/{0}.git@{1}'.format(package, commit)
print('Installing {}'.format(package))
check_call(['pip', 'install', '--upgrade', package])
package = 'git+https://github.com/{0}.git@{1}'.format(package, commit)
print('Installing {0}'.format(package))
check_call(['pip', 'install', package])
def build_docs(version='dev'):
def create_build_env(dirname='virtualenv'):
# Create virtualenv.
doc_dir = os.path.dirname(os.path.realpath(__file__))
virtualenv_dir = 'virtualenv'
check_call(['virtualenv', virtualenv_dir])
if not os.path.exists(dirname):
check_call(['virtualenv', dirname])
import sysconfig
scripts_dir = os.path.basename(sysconfig.get_path('scripts'))
activate_this_file = os.path.join(virtualenv_dir, scripts_dir,
'activate_this.py')
activate_this_file = os.path.join(dirname, scripts_dir, 'activate_this.py')
with open(activate_this_file) as f:
exec(f.read(), dict(__file__=activate_this_file))
# Import get_distribution after activating virtualenv to get info about
# the correct packages.
from pkg_resources import get_distribution, DistributionNotFound
# Upgrade pip because installation of sphinx with pip 1.1 available on Travis
# is broken (see #207) and it doesn't support the show command.
from pkg_resources import get_distribution, DistributionNotFound
pip_version = get_distribution('pip').version
if LooseVersion(pip_version) < LooseVersion('1.5.4'):
print("Updating pip")
@ -46,52 +51,67 @@ def build_docs(version='dev'):
except DistributionNotFound:
pass
# Install Sphinx and Breathe.
pip_install('cppformat/sphinx',
'12dde8afdb0a7bb5576e2656692c3478c69d8cc3',
check_version='1.4a0.dev-20151013')
pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee',
min_version='1.4.1.dev20160531')
pip_install('michaeljones/breathe',
'1c9d7f80378a92cffa755084823a78bb38ee4acc')
'6b1c5bb7a1866f15fc328b8716258354b10c1daa',
min_version='4.2.0')
def build_docs(version='dev', **kwargs):
doc_dir = kwargs.get('doc_dir', os.path.dirname(os.path.realpath(__file__)))
work_dir = kwargs.get('work_dir', '.')
include_dir = kwargs.get('include_dir',
os.path.join(os.path.dirname(doc_dir), 'fmt'))
# Build docs.
cmd = ['doxygen', '-']
p = Popen(cmd, stdin=PIPE)
doxyxml_dir = os.path.join(work_dir, 'doxyxml')
p.communicate(input=r'''
PROJECT_NAME = C++ Format
PROJECT_NAME = fmt
GENERATE_LATEX = NO
GENERATE_MAN = NO
GENERATE_RTF = NO
CASE_SENSE_NAMES = NO
INPUT = {0}/format.h
INPUT = {0}/container.h {0}/format.h {0}/ostream.h \
{0}/printf.h {0}/string.h
QUIET = YES
JAVADOC_AUTOBRIEF = YES
AUTOLINK_SUPPORT = NO
GENERATE_HTML = NO
GENERATE_XML = YES
XML_OUTPUT = doxyxml
XML_OUTPUT = {1}
ALIASES = "rst=\verbatim embed:rst"
ALIASES += "endrst=\endverbatim"
MACRO_EXPANSION = YES
PREDEFINED = _WIN32=1 \
FMT_USE_VARIADIC_TEMPLATES=1 \
FMT_USE_RVALUE_REFERENCES=1 \
FMT_USE_USER_DEFINED_LITERALS=1
FMT_USE_USER_DEFINED_LITERALS=1 \
FMT_API=
EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str
'''.format(os.path.dirname(doc_dir)).encode('UTF-8'))
'''.format(include_dir, doxyxml_dir).encode('UTF-8'))
if p.returncode != 0:
raise CalledProcessError(p.returncode, cmd)
html_dir = os.path.join(work_dir, 'html')
versions = ['3.0.0', '2.0.0', '1.1.0']
check_call(['sphinx-build',
'-Dbreathe_projects.format=' + os.path.join(os.getcwd(), 'doxyxml'),
'-Dversion=' + version, '-Drelease=' + version, '-Aversion=' + version,
'-b', 'html', doc_dir, 'html'])
'-Dbreathe_projects.format=' + os.path.abspath(doxyxml_dir),
'-Dversion=' + version, '-Drelease=' + version,
'-Aversion=' + version, '-Aversions=' + ','.join(versions),
'-b', 'html', doc_dir, html_dir])
try:
check_call(['lessc', '--clean-css',
'--include-path=' + os.path.join(doc_dir, 'bootstrap'),
os.path.join(doc_dir, 'cppformat.less'),
'html/_static/cppformat.css'])
os.path.join(doc_dir, 'fmt.less'),
os.path.join(html_dir, '_static', 'fmt.css')])
except OSError as e:
if e.errno != errno.ENOENT:
raise
print('lessc not found; make sure that Less (http://lesscss.org/) is installed')
print('lessc not found; make sure that Less (http://lesscss.org/) ' +
'is installed')
sys.exit(1)
return 'html'
return html_dir
if __name__ == '__main__':
create_build_env()
build_docs(sys.argv[1])

View File

@ -46,7 +46,7 @@ source_suffix = '.rst'
#master_doc = 'contents'
# General information about the project.
project = u'C++ Format'
project = u'fmt'
copyright = u'2012-2015, Victor Zverovich'
# The version info for the project you're documenting, acts as replacement for
@ -76,7 +76,7 @@ copyright = u'2012-2015, Victor Zverovich'
exclude_patterns = ['virtualenv']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
default_role = 'cpp:any'
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
@ -198,7 +198,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'format.tex', u'C++ Format Documentation',
('index', 'format.tex', u'fmt documentation',
u'Victor Zverovich', 'manual'),
]
@ -228,8 +228,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'format', u'format Documentation',
[u'Victor Zverovich'], 1)
('index', 'fmt', u'fmt documentation', [u'Victor Zverovich'], 1)
]
# If true, show URL addresses after external links.
@ -242,8 +241,8 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'format', u'format Documentation',
u'Victor Zverovich', 'format', 'One line description of project.',
('index', 'fmt', u'fmt documentation',
u'Victor Zverovich', 'fmt', 'One line description of project.',
'Miscellaneous'),
]

View File

@ -59,3 +59,8 @@ div.sphinxsidebar {
p.rubric {
margin-top: 10px;
}
.github-btn {
border: 0;
overflow: hidden;
}

View File

@ -1,18 +1,18 @@
Overview
========
C++ Format (cppformat) is an open-source formatting library for C++.
**fmt** (formerly cppformat) is an open-source formatting library.
It can be used as a safe alternative to printf or as a fast
alternative to IOStreams.
alternative to C++ IOStreams.
.. raw:: html
<div class="panel panel-default">
<div class="panel-heading">What users say:</div>
<div class="panel-body">
Thanks for creating this library. Its been a hole in C++ for a long time.
Ive used both boost::format and loki::SPrintf, and neither felt like the
right answer. This does.
Thanks for creating this library. Its been a hole in C++ for a long
time. Ive used both boost::format and loki::SPrintf, and neither felt
like the right answer. This does.
</div>
</div>
@ -24,8 +24,8 @@ Format API
The replacement-based Format API provides a safe alternative to ``printf``,
``sprintf`` and friends with comparable or `better performance
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
The `format string syntax <doc/latest/index.html#format-string-syntax>`_ is similar
to the one used by `str.format <http://docs.python.org/2/library/stdtypes.html#str.format>`_
The `format string syntax <syntax.html>`_ is similar to the one used by
`str.format <http://docs.python.org/2/library/stdtypes.html#str.format>`_
in Python:
.. code:: c++
@ -98,8 +98,8 @@ literal operators, they must be made visible with the directive
Write API
---------
The concatenation-based Write API (experimental) provides a
`fast <http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
The concatenation-based Write API (experimental) provides a `fast
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
stateless alternative to IOStreams:
.. code:: c++
@ -112,8 +112,9 @@ stateless alternative to IOStreams:
Safety
------
The library is fully type safe, automatic memory management prevents buffer overflow,
errors in format strings are reported using exceptions. For example, the code
The library is fully type safe, automatic memory management prevents buffer
overflow, errors in format strings are reported using exceptions. For example,
the code
.. code:: c++
@ -138,44 +139,46 @@ formatted into a narrow string. You can use a wide format string instead:
fmt::format(L"Cyrillic letter {}", L'\x42e');
For comparison, writing a wide character to ``std::ostream`` results in
its numeric value being written to the stream (i.e. 1070 instead of letter 'ю' which
is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is needed.
its numeric value being written to the stream (i.e. 1070 instead of letter 'ю'
which is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is
needed.
.. _portability:
Portability
-----------
C++ Format is highly portable. Here is an incomplete list of operating systems and
compilers where it has been tested and known to work:
The library is highly portable. Here is an incomplete list of operating systems
and compilers where it has been tested and known to work:
* 64-bit (amd64) GNU/Linux with GCC 4.4.3, `4.6.3 <https://travis-ci.org/cppformat/cppformat>`_,
4.7.2, 4.8.1 and Intel C++ Compiler (ICC) 14.0.2
* 64-bit (amd64) GNU/Linux with GCC 4.4.3,
`4.6.3 <https://travis-ci.org/fmtlib/fmt>`_, 4.7.2, 4.8.1, and Intel C++
Compiler (ICC) 14.0.2
* 32-bit (i386) GNU/Linux with GCC 4.4.3, 4.6.3
* Mac OS X with GCC 4.2.1 and Clang 4.2, 5.1.0
* 64-bit Windows with Visual C++ 2010, 2013 and
`2015 <https://ci.appveyor.com/project/vitaut/cppformat>`_
`2015 <https://ci.appveyor.com/project/vitaut/fmt>`_
* 32-bit Windows with Visual C++ 2010
Although the library uses C++11 features when available, it also works with older
compilers and standard library implementations. The only thing to keep in mind
for C++98 portability:
Although the library uses C++11 features when available, it also works with
older compilers and standard library implementations. The only thing to keep in
mind for C++98 portability:
* Variadic templates: minimum GCC 4.4, Clang 2.9 or VS2013. This feature allows
the Format API to accept an unlimited number of arguments. With older compilers
the maximum is 15.
the Format API to accept an unlimited number of arguments. With older
compilers the maximum is 15.
* User-defined literals: minimum GCC 4.7, Clang 3.1 or VS2015. The suffixes
``_format`` and ``_a`` are functionally equivalent to the functions
* User-defined literals: minimum GCC 4.7, Clang 3.1 or VS2015. The suffixes
``_format`` and ``_a`` are functionally equivalent to the functions
``fmt::format`` and ``fmt::arg``.
The output of all formatting functions is consistent across platforms. In particular,
formatting a floating-point infinity always gives ``inf`` while the output
of ``printf`` is platform-dependent in this case. For example,
The output of all formatting functions is consistent across platforms. In
particular, formatting a floating-point infinity always gives ``inf`` while the
output of ``printf`` is platform-dependent in this case. For example,
.. code::
@ -188,16 +191,16 @@ always prints ``inf``.
Ease of Use
-----------
C++ Format has small self-contained code base consisting of a single header file
and a single source file and no external dependencies. A permissive BSD `license
<https://github.com/cppformat/cppformat#license>`_ allows using the library both
in open-source and commercial projects.
fmt has a small self-contained code base with the core library consisting of
a single header file and a single source file and no external dependencies.
A permissive BSD `license <https://github.com/fmtlib/fmt#license>`_ allows
using the library both in open-source and commercial projects.
.. raw:: html
<a class="btn btn-success" href="https://github.com/cppformat/cppformat">GitHub Repository</a>
<a class="btn btn-success" href="https://github.com/fmtlib/fmt">GitHub Repository</a>
<div class="section footer">
<iframe src="http://ghbtns.com/github-btn.html?user=cppformat&amp;repo=cppformat&amp;type=watch&amp;count=true"
<iframe src="http://ghbtns.com/github-btn.html?user=fmtlib&amp;repo=fmt&amp;type=watch&amp;count=true"
class="github-btn" width="100" height="20"></iframe>
</div>

View File

@ -49,12 +49,10 @@ mini-language" or interpretation of the *format_spec*.
Most built-in types support a common formatting mini-language, which is
described in the next section.
A *format_spec* field can also include nested replacement fields within it.
These nested replacement fields can contain only an argument index;
format specifications are not allowed. Formatting is performed as if the
replacement fields within the format_spec are substituted before the
*format_spec* string is interpreted. This allows the formatting of a value
to be dynamically specified.
A *format_spec* field can also include nested replacement fields in certain
positions within it. These nested replacement fields can contain only an
argument id; format specifications are not allowed. This allows the
formatting of a value to be dynamically specified.
See the :ref:`formatexamples` section for some examples.
@ -80,8 +78,8 @@ The general form of a *standard format specifier* is:
sign: "+" | "-" | " "
width: `integer` | "{" `arg_id` "}"
precision: `integer` | "{" `arg_id` "}"
type: `int_type` | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
int_type: "b" | "B" | "d" | "o" | "x" | "X"
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"
The *fill* character can be any character other than '{' or '}'. The presence
of a fill character is signaled by the character following it, which must be
@ -216,6 +214,10 @@ The available integer presentation types are:
| | ``'#'`` option with this type adds the prefix ``"0X"`` |
| | to the output value. |
+---------+----------------------------------------------------------+
| ``'n'`` | Number. This is the same as ``'d'``, except that it uses |
| | the current locale setting to insert the appropriate |
| | number separator characters. |
+---------+----------------------------------------------------------+
| none | The same as ``'d'``. |
+---------+----------------------------------------------------------+
@ -230,7 +232,7 @@ The available presentation types for floating-point values are:
+=========+==========================================================+
| ``'a'`` | Hexadecimal floating point format. Prints the number in |
| | base 16 with prefix ``"0x"`` and lower-case letters for |
| | digits above 9. Uses 'p' to indicate the exponent. |
| | digits above 9. Uses ``'p'`` to indicate the exponent. |
+---------+----------------------------------------------------------+
| ``'A'`` | Same as ``'a'`` except it uses upper-case letters for |
| | the prefix, digits above 9 and to indicate the exponent. |
@ -262,6 +264,8 @@ The available presentation types for floating-point values are:
| none | The same as ``'g'``. |
+---------+----------------------------------------------------------+
Floating-point formatting is locale-dependent.
.. ifconfig:: False
+---------+----------------------------------------------------------+

View File

@ -2,9 +2,9 @@
Usage
*****
To use the C++ Format library, add :file:`format.h` and :file:`format.cc` from
a `release archive <https://github.com/cppformat/cppformat/releases/latest>`_
or the `Git repository <https://github.com/cppformat/cppformat>`_ to your project.
To use the fmt library, add :file:`format.h` and :file:`format.cc` from
a `release archive <https://github.com/fmtlib/fmt/releases/latest>`_
or the `Git repository <https://github.com/fmtlib/fmt>`_ to your project.
Alternatively, you can :ref:`build the library with CMake <building>`.
If you are using Visual C++ with precompiled headers, you might need to add
@ -19,24 +19,24 @@ before other includes in :file:`format.cc`.
Building the library
====================
The included `CMake build script`__ can be used to build the C++ Format
The included `CMake build script`__ can be used to build the fmt
library on a wide range of platforms. CMake is freely available for
download from http://www.cmake.org/download/.
__ https://github.com/cppformat/cppformat/blob/master/CMakeLists.txt
__ https://github.com/fmtlib/fmt/blob/master/CMakeLists.txt
CMake works by generating native makefiles or project files that can
be used in the compiler environment of your choice. The typical
workflow starts with::
mkdir build # Create a directory to hold the build output.
mkdir build # Create a directory to hold the build output.
cd build
cmake <path/to/cppformat> # Generate native build scripts.
cmake <path/to/fmt> # Generate native build scripts.
where :file:`{<path/to/cppformat>}` is a path to the ``cppformat`` repository.
where :file:`{<path/to/fmt>}` is a path to the ``fmt`` repository.
If you are on a \*nix system, you should now see a Makefile in the
current directory. Now you can build C++ Format by running :command:`make`.
current directory. Now you can build the library by running :command:`make`.
Once the library has been built you can invoke :command:`make test` to run
the tests.
@ -54,6 +54,23 @@ To build a `shared library`__ set the ``BUILD_SHARED_LIBS`` CMake variable to
__ http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries
Header-only usage with CMake
============================
In order to add ``fmtlib`` into an existing ``CMakeLists.txt`` file, you can add the ``fmt`` library directory into your main project, which will enable the ``fmt`` library::
add_subdirectory(fmt)
If you have a project called ``foo`` that you would like to link against the fmt library in a header-only fashion, you can enable with with::
target_link_libraries(foo PRIVATE fmt::fmt-header-only)
And then to ensure that the ``fmt`` library does not always get built, you can modify the call to ``add_subdirectory`` to read ::
add_subdirectory(fmt EXCLUDE_FROM_ALL)
This will ensure that the ``fmt`` library is exluded from calls to ``make``, ``make all``, or ``cmake --build .``.
Building the documentation
==========================
@ -62,29 +79,33 @@ system:
* `Python <https://www.python.org/>`_ with pip and virtualenv
* `Doxygen <http://www.stack.nl/~dimitri/doxygen/>`_
* `Less <http://lesscss.org/>`_ with less-plugin-clean-css
* `Less <http://lesscss.org/>`_ with ``less-plugin-clean-css``.
Ubuntu doesn't package the ``clean-css`` plugin so you should use ``npm``
instead of ``apt`` to install both ``less`` and the plugin::
sudo npm install -g less less-plugin-clean-css.
First generate makefiles or project files using CMake as described in
the previous section. Then compile the ``doc`` target/project, for example::
make doc
This will generate the HTML documenation in ``doc/html``.
This will generate the HTML documentation in ``doc/html``.
Android NDK
===========
C++ Format provides `Android.mk file`__ that can be used to build the library
fmt provides `Android.mk file`__ that can be used to build the library
with `Android NDK <https://developer.android.com/tools/sdk/ndk/index.html>`_.
For an example of using C++ Format with Android NDK, see the
`android-ndk-example <https://github.com/cppformat/android-ndk-example>`_
For an example of using fmt with Android NDK, see the
`android-ndk-example <https://github.com/fmtlib/android-ndk-example>`_
repository.
__ https://github.com/cppformat/cppformat/blob/master/Android.mk
__ https://github.com/fmtlib/fmt/blob/master/Android.mk
Homebrew
========
C++ Format can be installed on OS X using `Homebrew <http://brew.sh/>`_::
fmt can be installed on OS X using `Homebrew <http://brew.sh/>`_::
brew install cppformat
brew install fmt

90
fmt/CMakeLists.txt Normal file
View File

@ -0,0 +1,90 @@
# Define the fmt library, its includes and the needed defines.
# *.cc are added to FMT_HEADERS for the header-only configuration.
set(FMT_HEADERS container.h format.h format.cc ostream.h ostream.cc printf.h
printf.cc string.h time.h)
if (HAVE_OPEN)
set(FMT_HEADERS ${FMT_HEADERS} posix.h)
set(FMT_SOURCES ${FMT_SOURCES} posix.cc)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} ../README.rst ../ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt)
# Starting with cmake 3.1 the CXX_STANDARD property can be used instead.
# Note: Don't make -std=c++11 public or interface, since it breaks projects
# that use C++14.
target_compile_options(fmt PRIVATE ${CPP11_FLAG})
if (FMT_PEDANTIC)
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR})
if (BUILD_SHARED_LIBS)
if (UNIX AND NOT APPLE)
# Fix rpmlint warning:
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
target_link_libraries(fmt -Wl,--as-needed)
endif ()
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED)
endif ()
#------------------------------------------------------------------------------
# additionally define a header only library when cmake is new enough
if (CMAKE_VERSION VERSION_GREATER 3.1.0 OR CMAKE_VERSION VERSION_EQUAL 3.1.0)
add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_include_directories(fmt-header-only INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
endif ()
# Install targets.
if (FMT_INSTALL)
include(CMakePackageConfigHelpers)
set(FMT_CMAKE_DIR lib/cmake/fmt CACHE STRING
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(targets_export_name fmt-targets)
set (INSTALL_TARGETS fmt)
if (TARGET fmt-header-only)
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
endif ()
set(FMT_LIB_DIR lib CACHE STRING
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.")
# Generate the version, config and target files into the build directory.
write_basic_package_version_file(
${version_config}
VERSION ${FMT_VERSION}
COMPATIBILITY AnyNewerVersion)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
${project_config}
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
# Install version, config and target files.
install(
FILES ${project_config} ${version_config}
DESTINATION ${FMT_CMAKE_DIR})
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
NAMESPACE fmt::)
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
DESTINATION ${FMT_LIB_DIR})
install(FILES ${FMT_HEADERS} DESTINATION include/fmt)
endif ()

82
fmt/container.h Normal file
View File

@ -0,0 +1,82 @@
/*
Formatting library for C++ - standard container utilities
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_CONTAINER_H_
#define FMT_CONTAINER_H_
#include "format.h"
namespace fmt {
namespace internal {
/**
\rst
A "buffer" that appends data to a standard container (e.g. typically a
``std::vector`` or ``std::basic_string``).
\endrst
*/
template <typename Container>
class ContainerBuffer : public Buffer<typename Container::value_type> {
private:
Container& container_;
protected:
virtual void grow(std::size_t size) FMT_OVERRIDE {
container_.resize(size);
this->ptr_ = &container_[0];
this->capacity_ = size;
}
public:
explicit ContainerBuffer(Container& container) : container_(container) {
this->size_ = container_.size();
if (this->size_ > 0) {
this->ptr_ = &container_[0];
this->capacity_ = this->size_;
}
}
};
} // namespace internal
/**
\rst
This class template provides operations for formatting and appending data
to a standard *container* like ``std::vector`` or ``std::basic_string``.
**Example**::
void vecformat(std::vector<char>& dest, fmt::BasicCStringRef<char> format,
fmt::ArgList args) {
fmt::BasicContainerWriter<std::vector<char> > appender(dest);
appender.write(format, args);
}
FMT_VARIADIC(void, vecformat, std::vector<char>&,
fmt::BasicCStringRef<char>);
\endrst
*/
template <class Container>
class BasicContainerWriter
: public BasicWriter<typename Container::value_type> {
private:
internal::ContainerBuffer<Container> buffer_;
public:
/**
\rst
Constructs a :class:`fmt::BasicContainerWriter` object.
\endrst
*/
explicit BasicContainerWriter(Container& dest)
: BasicWriter<typename Container::value_type>(buffer_), buffer_(dest) {}
};
} // namespace fmt
#endif // FMT_CONTAINER_H_

535
fmt/format.cc Normal file
View File

@ -0,0 +1,535 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "format.h"
#include <string.h>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#if defined(_WIN32) && defined(__MINGW32__)
# include <cstring>
#endif
#if FMT_USE_WINDOWS_H
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
# define WIN32_LEAN_AND_MEAN
# endif
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h>
# else
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
# endif
#endif
#if FMT_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
#else
# define FMT_TRY if (true)
# define FMT_CATCH(x) if (false)
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable: 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
return fmt::internal::Null<>();
}
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
return fmt::internal::Null<>();
}
namespace fmt {
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
namespace {
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
}
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
# define FMT_SWPRINTF snwprintf
#else
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
const char RESET_COLOR[] = "\x1b[0m";
typedef void (*FormatFunc)(Writer &, int, StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
// Returns one of the following values:
// 0 - success
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
class StrError {
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {}
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result) {
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
// Handle the case when strerror_r is not available.
int handle(internal::Null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
ERANGE : result;
}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::Null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() {
// Suppress a warning about unused strerror_r.
strerror_r(0, FMT_NULL, "");
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
return StrError(error_code, buffer, buffer_size).run();
}
void format_error_code(Writer &out, int error_code,
StringRef message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc.
out.clear();
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
typedef internal::IntTraits<int>::MainType MainType;
MainType abs_value = static_cast<MainType>(error_code);
if (internal::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
}
error_code_size += internal::count_digits(abs_value);
if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP;
out << ERROR_STR << error_code;
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
}
void report_error(FormatFunc func, int error_code,
StringRef message) FMT_NOEXCEPT {
MemoryWriter full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
} // namespace
FMT_FUNC void SystemError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
template <typename T>
int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, value) :
FMT_SWPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, width, value) :
FMT_SWPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
const char internal::BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
#define FMT_POWERS_OF_10(factor) \
factor * 10, \
factor * 100, \
factor * 1000, \
factor * 10000, \
factor * 100000, \
factor * 1000000, \
factor * 10000000, \
factor * 100000000, \
factor * 1000000000
template <typename T>
const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
ULongLong(1000000000) * ULongLong(1000000000) * 10
};
FMT_FUNC void internal::report_unknown_type(char code, const char *type) {
(void)type;
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(FormatError(
format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(FormatError(
format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
}
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (s.size() > INT_MAX)
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
int s_size = static_cast<int>(s.size());
int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_[length] = 0;
}
FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
if (s.size() > INT_MAX)
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
int length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_[length] = 0;
return 0;
}
FMT_FUNC void WindowsError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_windows_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
FMT_FUNC void internal::format_windows_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
wchar_t *system_message = &buffer[0];
int result = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
if (result != 0) {
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
out << message << ": " << utf8_message;
return;
}
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
#endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
buffer.resize(internal::INLINE_BUFFER_SIZE);
for (;;) {
char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size());
if (result == 0) {
out << message << ": " << system_message;
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
template <typename Char>
void internal::ArgMap<Char>::init(const ArgList &args) {
if (!map_.empty())
return;
typedef internal::NamedArg<Char> NamedArg;
const NamedArg *named_arg = FMT_NULL;
bool use_values =
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
if (use_values) {
for (unsigned i = 0;/*nothing*/; ++i) {
internal::Arg::Type arg_type = args.type(i);
switch (arg_type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
return;
}
for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
internal::Arg::Type arg_type = args.type(i);
if (arg_type == internal::Arg::NAMED_ARG) {
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
}
}
for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
switch (args.args_[i].type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
}
template <typename Char>
void internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
internal::Arg arg = args_[arg_index];
switch (arg.type) {
case internal::Arg::NONE:
error = "argument index out of range";
break;
case internal::Arg::NAMED_ARG:
arg = *static_cast<const internal::Arg*>(arg.pointer);
break;
default:
/*nothing*/;
}
return arg;
}
FMT_FUNC void report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
print(format, args);
std::fputs(RESET_COLOR, stdout);
}
#ifndef FMT_HEADER_ONLY
template struct internal::BasicData<void>;
// Explicit instantiations for char.
template void internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const ArgList &args);
template FMT_API int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template FMT_API int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t.
template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void internal::ArgMap<wchar_t>::init(const ArgList &args);
template FMT_API int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template FMT_API int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
#endif // FMT_HEADER_ONLY
} // namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif

File diff suppressed because it is too large Load Diff

35
fmt/ostream.cc Normal file
View File

@ -0,0 +1,35 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "ostream.h"
namespace fmt {
namespace internal {
FMT_FUNC void write(std::ostream &os, Writer &w) {
const char *data = w.data();
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
UnsignedStreamSize size = w.size();
UnsignedStreamSize max_size =
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
do {
UnsignedStreamSize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n));
data += n;
size -= n;
} while (size != 0);
}
}
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
internal::write(os, w);
}
} // namespace fmt

105
fmt/ostream.h Normal file
View File

@ -0,0 +1,105 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include "format.h"
#include <ostream>
namespace fmt {
namespace internal {
template <class Char>
class FormatBuf : public std::basic_streambuf<Char> {
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
Yes &convert(std::ostream &);
struct DummyStream : std::ostream {
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
};
No &operator<<(std::ostream &, int);
template<typename T>
struct ConvertToIntImpl<T, true> {
// Convert to int only if T doesn't have an overloaded operator<<.
enum {
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
// Write the content of w to os.
FMT_API void write(std::ostream &os, Writer &w);
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
const Char *&format_str, const T &value) {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output << value;
BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str));
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "ostream.cc"
#endif
#endif // FMT_OSTREAM_H_

View File

@ -1,28 +1,10 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2014 - 2015, Victor Zverovich
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
For the license information refer to format.h.
*/
// Disable bogus MSVC warnings.
@ -39,6 +21,9 @@
#ifndef _WIN32
# include <unistd.h>
#else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# include <io.h>
@ -55,11 +40,14 @@
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# undef fileno
# endif
#endif // _WIN32
#ifdef fileno
# undef fileno
#endif
namespace {
#ifdef _WIN32
// Return type of read and write functions.
@ -87,16 +75,16 @@ fmt::BufferedFile::BufferedFile(
fmt::CStringRef filename, fmt::CStringRef mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
if (!file_)
throw SystemError(errno, "cannot open file {}", filename);
FMT_THROW(SystemError(errno, "cannot open file {}", filename));
}
void fmt::BufferedFile::close() {
if (!file_)
return;
int result = FMT_SYSTEM(fclose(file_));
file_ = 0;
file_ = FMT_NULL;
if (result != 0)
throw SystemError(errno, "cannot close file");
FMT_THROW(SystemError(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
@ -105,7 +93,7 @@ void fmt::BufferedFile::close() {
int fmt::BufferedFile::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1)
throw SystemError(errno, "cannot get file descriptor");
FMT_THROW(SystemError(errno, "cannot get file descriptor"));
return fd;
}
@ -118,7 +106,7 @@ fmt::File::File(fmt::CStringRef path, int oflag) {
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
#endif
if (fd_ == -1)
throw SystemError(errno, "cannot open file {}", path);
FMT_THROW(SystemError(errno, "cannot open file {}", path));
}
fmt::File::~File() FMT_NOEXCEPT {
@ -136,7 +124,7 @@ void fmt::File::close() {
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0)
throw SystemError(errno, "cannot close file");
FMT_THROW(SystemError(errno, "cannot close file"));
}
fmt::LongLong fmt::File::size() const {
@ -150,7 +138,7 @@ fmt::LongLong fmt::File::size() const {
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
throw WindowsError(GetLastError(), "cannot get file size");
FMT_THROW(WindowsError(GetLastError(), "cannot get file size"));
}
fmt::ULongLong long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
@ -158,7 +146,7 @@ fmt::LongLong fmt::File::size() const {
typedef struct stat Stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
throw SystemError(errno, "cannot get file attributes");
FMT_THROW(SystemError(errno, "cannot get file attributes"));
FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size),
"return type of File::size is not large enough");
return file_stat.st_size;
@ -169,16 +157,16 @@ std::size_t fmt::File::read(void *buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0)
throw SystemError(errno, "cannot read from file");
return result;
FMT_THROW(SystemError(errno, "cannot read from file"));
return internal::to_unsigned(result);
}
std::size_t fmt::File::write(const void *buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0)
throw SystemError(errno, "cannot write to file");
return result;
FMT_THROW(SystemError(errno, "cannot write to file"));
return internal::to_unsigned(result);
}
fmt::File fmt::File::dup(int fd) {
@ -186,7 +174,7 @@ fmt::File fmt::File::dup(int fd) {
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
throw SystemError(errno, "cannot duplicate file descriptor {}", fd);
FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd));
return File(new_fd);
}
@ -194,8 +182,8 @@ void fmt::File::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
throw SystemError(errno,
"cannot duplicate file descriptor {} to {}", fd_, fd);
FMT_THROW(SystemError(errno,
"cannot duplicate file descriptor {} to {}", fd_, fd));
}
}
@ -222,7 +210,7 @@ void fmt::File::pipe(File &read_end, File &write_end) {
int result = FMT_POSIX_CALL(pipe(fds));
#endif
if (result != 0)
throw SystemError(errno, "cannot create pipe");
FMT_THROW(SystemError(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = File(fds[0]);
@ -233,7 +221,7 @@ fmt::BufferedFile fmt::File::fdopen(const char *mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f)
throw SystemError(errno, "cannot associate stream with file descriptor");
FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor"));
BufferedFile file(f);
fd_ = -1;
return file;
@ -247,7 +235,7 @@ long fmt::getpagesize() {
#else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0)
throw SystemError(errno, "cannot get memory page size");
FMT_THROW(SystemError(errno, "cannot get memory page size"));
return size;
#endif
}

View File

@ -1,44 +1,32 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2014 - 2015, Victor Zverovich
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
For the license information refer to format.h.
*/
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#ifdef __MINGW32__
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_POSIX
@ -63,25 +51,6 @@
# endif
#endif
#if FMT_GCC_VERSION >= 407
# define FMT_UNUSED __attribute__((unused))
#else
# define FMT_UNUSED
#endif
#ifndef FMT_USE_STATIC_ASSERT
# define FMT_USE_STATIC_ASSERT 0
#endif
#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \
(FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600
# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message)
#else
# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b)
# define FMT_STATIC_ASSERT(cond, message) \
typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
@ -119,10 +88,10 @@ class BufferedFile {
public:
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT : file_(0) {}
BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {}
// Destroys the object closing the file it represents if any.
~BufferedFile() FMT_NOEXCEPT;
FMT_API ~BufferedFile() FMT_NOEXCEPT;
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
@ -139,9 +108,9 @@ public:
// A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
// A "move constructor" for for moving from an lvalue.
// A "move constructor" for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) {
f.file_ = 0;
f.file_ = FMT_NULL;
}
// A "move assignment operator" for moving from a temporary.
@ -155,7 +124,7 @@ public:
BufferedFile &operator=(BufferedFile &other) {
close();
file_ = other.file_;
other.file_ = 0;
other.file_ = FMT_NULL;
return *this;
}
@ -163,7 +132,7 @@ public:
// BufferedFile file = BufferedFile(...);
operator Proxy() FMT_NOEXCEPT {
Proxy p = {file_};
file_ = 0;
file_ = FMT_NULL;
return p;
}
@ -173,29 +142,29 @@ public:
public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = 0;
other.file_ = FMT_NULL;
}
BufferedFile& operator=(BufferedFile &&other) {
close();
file_ = other.file_;
other.file_ = 0;
other.file_ = FMT_NULL;
return *this;
}
#endif
// Opens a file.
BufferedFile(CStringRef filename, CStringRef mode);
FMT_API BufferedFile(CStringRef filename, CStringRef mode);
// Closes the file.
void close();
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
int (fileno)() const;
FMT_API int (fileno)() const;
void print(CStringRef format_str, const ArgList &args) {
fmt::print(file_, format_str, args);
@ -228,7 +197,7 @@ class File {
File() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a File object representing this file.
File(CStringRef path, int oflag);
FMT_API File(CStringRef path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
@ -245,7 +214,7 @@ class File {
// A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
// A "move constructor" for for moving from an lvalue.
// A "move constructor" for moving from an lvalue.
File(File &other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1;
}
@ -291,46 +260,100 @@ class File {
#endif
// Destroys the object closing the file it represents if any.
~File() FMT_NOEXCEPT;
FMT_API ~File() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
void close();
FMT_API void close();
// Returns the file size.
LongLong size() const;
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API LongLong size() const;
// Attempts to read count bytes from the file into the specified buffer.
std::size_t read(void *buffer, std::size_t count);
FMT_API std::size_t read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
std::size_t write(const void *buffer, std::size_t count);
FMT_API std::size_t write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static File dup(int fd);
FMT_API static File dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd);
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
static void pipe(File &read_end, File &write_end);
FMT_API static void pipe(File &read_end, File &write_end);
// Creates a BufferedFile object associated with this file and detaches
// this File object from the file.
BufferedFile fdopen(const char *mode);
FMT_API BufferedFile fdopen(const char *mode);
};
// Returns the memory page size.
long getpagesize();
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
!defined(__ANDROID__) && !defined(__CYGWIN__)
# define FMT_LOCALE
#endif
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
# ifdef _MSC_VER
typedef _locale_t locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char *locale, locale_t) {
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) {
_free_locale(locale);
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
public:
typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
if (!locale_)
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
Type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char *&str) const {
char *end = FMT_NULL;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
} // namespace fmt
#if !FMT_USE_RVALUE_REFERENCES

32
fmt/printf.cc Normal file
View File

@ -0,0 +1,32 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "format.h"
#include "printf.h"
namespace fmt {
template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args);
FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
}
#ifndef FMT_HEADER_ONLY
template void PrintfFormatter<char>::format(CStringRef format);
template void PrintfFormatter<wchar_t>::format(WCStringRef format);
#endif // FMT_HEADER_ONLY
} // namespace fmt

603
fmt/printf.h Normal file
View File

@ -0,0 +1,603 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "ostream.h"
namespace fmt {
namespace internal {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned>
struct IntChecker {
template <typename T>
static bool fits_in_int(T value) {
unsigned max = std::numeric_limits<int>::max();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
};
template <>
struct IntChecker<true> {
template <typename T>
static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max();
}
static bool fits_in_int(int) { return true; }
};
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> {
public:
void report_unhandled_arg() {
FMT_THROW(FormatError("precision is not integer"));
}
template <typename T>
int visit_any_int(T value) {
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(FormatError("number is too big"));
return static_cast<int>(value);
}
};
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public ArgVisitor<IsZeroInt, bool> {
public:
template <typename T>
bool visit_any_int(T value) { return value == 0; }
};
// returns the default type for format specific "%s"
class DefaultType : public ArgVisitor<DefaultType, char> {
public:
char visit_char(int) { return 'c'; }
char visit_bool(bool) { return 's'; }
char visit_pointer(const void *) { return 'p'; }
template <typename T>
char visit_any_int(T) { return 'd'; }
template <typename T>
char visit_any_double(T) { return 'g'; }
char visit_unhandled_arg() { return 's'; }
};
template <typename T, typename U>
struct is_same {
enum { value = 0 };
};
template <typename T>
struct is_same<T, T> {
enum { value = 1 };
};
// An argument visitor that converts an integer argument to T for printf,
// if T is an integral type. If T is void, the argument is converted to
// corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned)
template <typename T = void>
class ArgConverter : public ArgVisitor<ArgConverter<T>, void> {
private:
internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {}
void visit_bool(bool value) {
if (type_ != 's')
visit_any_int(value);
}
void visit_char(char value) {
if (type_ != 's')
visit_any_int(value);
}
template <typename U>
void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
if (type_ == 's') {
is_signed = std::numeric_limits<U>::is_signed;
}
using internal::Arg;
typedef typename internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType;
if (sizeof(TargetType) <= sizeof(int)) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
} else {
arg_.type = Arg::UINT;
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
}
} else {
if (is_signed) {
arg_.type = Arg::LONG_LONG;
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_.long_long_value = static_cast<LongLong>(value);
} else {
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value =
static_cast<typename internal::MakeUnsigned<U>::Type>(value);
}
}
}
};
// Converts an integer argument to char for printf.
class CharConverter : public ArgVisitor<CharConverter, void> {
private:
internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public:
explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
template <typename T>
void visit_any_int(T value) {
arg_.type = internal::Arg::CHAR;
arg_.int_value = static_cast<char>(value);
}
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class WidthHandler : public ArgVisitor<WidthHandler, unsigned> {
private:
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg() {
FMT_THROW(FormatError("width is not integer"));
}
template <typename T>
unsigned visit_any_int(T value) {
typedef typename internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = static_cast<UnsignedType>(value);
if (internal::is_negative(value)) {
spec_.align_ = ALIGN_LEFT;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max)
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
}
};
} // namespace internal
/**
\rst
A ``printf`` argument formatter based on the `curiously recurring template
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
or all of the visit methods with the same signatures as the methods in
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
Pass the subclass as the *Impl* template parameter. When a formatting
function processes an argument, it will dispatch to a visit method
specific to the argument type. For example, if the argument type is
``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
will be called. If the subclass doesn't contain a method with this signature,
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
superclass will be called.
\endrst
*/
template <typename Impl, typename Char, typename Spec>
class BasicPrintfArgFormatter :
public internal::ArgFormatterBase<Impl, Char, Spec> {
private:
void write_null_pointer() {
this->spec().type_ = 0;
this->write("(nil)");
}
typedef internal::ArgFormatterBase<Impl, Char, Spec> Base;
public:
/**
\rst
Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format
specifier information for standard argument types.
\endrst
*/
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {}
/** Formats an argument of type ``bool``. */
void visit_bool(bool value) {
Spec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return this->visit_any_int(value);
fmt_spec.type_ = 0;
this->write(value);
}
/** Formats a character. */
void visit_char(int value) {
const Spec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (fmt_spec.width_ > 1) {
Char fill = ' ';
out = w.grow_buffer(fmt_spec.width_);
if (fmt_spec.align_ != ALIGN_LEFT) {
std::fill_n(out, fmt_spec.width_ - 1, fill);
out += fmt_spec.width_ - 1;
} else {
std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
}
} else {
out = w.grow_buffer(1);
}
*out = static_cast<Char>(value);
}
/** Formats a null-terminated C string. */
void visit_cstring(const char *value) {
if (value)
Base::visit_cstring(value);
else if (this->spec().type_ == 'p')
write_null_pointer();
else
this->write("(null)");
}
/** Formats a pointer. */
void visit_pointer(const void *value) {
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c) {
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0};
const Char *format = format_str;
c.format(&formatter, c.value, &format);
}
};
/** The default printf argument formatter. */
template <typename Char>
class PrintfArgFormatter :
public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec> {
public:
/** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) {}
};
/** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter : private internal::FormatterBase {
private:
BasicWriter<Char> &writer_;
void parse_flags(FormatSpec &spec, const Char *&s);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
internal::Arg get_arg(
const Char *s,
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec);
public:
/**
\rst
Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have
appropriate lifetimes.
\endrst
*/
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(al), writer_(w) {}
/** Formats stored arguments and writes the output to the writer. */
void format(BasicCStringRef<Char> format_str);
};
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) {
for (;;) {
switch (*s++) {
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--s;
return;
}
}
}
template <typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index) {
(void)s;
const char *error = FMT_NULL;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
}
template <typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header(
const Char *&s, FormatSpec &spec) {
unsigned arg_index = std::numeric_limits<unsigned>::max();
Char c = *s;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned value = internal::parse_nonnegative_int(s);
if (*s == '$') { // value is an argument index
++s;
arg_index = value;
} else {
if (c == '0')
spec.fill_ = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
}
parse_flags(spec, s);
// Parse width.
if (*s >= '0' && *s <= '9') {
spec.width_ = internal::parse_nonnegative_int(s);
} else if (*s == '*') {
++s;
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) {
const Char *start = format_str.c_str();
const Char *s = start;
while (*s) {
Char c = *s++;
if (c != '%') continue;
if (*s == c) {
write(writer_, start, s);
start = ++s;
continue;
}
write(writer_, start, s - 1);
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.') {
++s;
if ('0' <= *s && *s <= '9') {
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
} else if (*s == '*') {
++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
} else {
spec.precision_ = 0;
}
}
using internal::Arg;
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0') {
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
using internal::ArgConverter;
switch (*s++) {
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<std::size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<void>(arg, *s).visit(arg);
}
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (spec.type_ == 's') {
// set the format type to the default if 's' is specified
spec.type_ = internal::DefaultType().visit(arg);
}
if (arg.type <= Arg::LAST_INTEGER_TYPE) {
// Normalize type.
switch (spec.type_) {
case 'i': case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t
internal::CharConverter(arg).visit(arg);
break;
}
}
start = s;
// Format argument.
AF(writer_, spec).visit(arg);
}
write(writer_, start, s);
}
inline void printf(Writer &w, CStringRef format, ArgList args) {
PrintfFormatter<char>(args, w).format(format);
}
FMT_VARIADIC(void, printf, Writer &, CStringRef)
inline void printf(WWriter &w, WCStringRef format, ArgList args) {
PrintfFormatter<wchar_t>(args, w).format(format);
}
FMT_VARIADIC(void, printf, WWriter &, WCStringRef)
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline std::string sprintf(CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC(std::string, sprintf, CStringRef)
inline std::wstring sprintf(WCStringRef format, ArgList args) {
WMemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline int printf(CStringRef format, ArgList args) {
return fprintf(stdout, format, args);
}
FMT_VARIADIC(int, printf, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
printf(w, format_str, args);
internal::write(os, w);
return static_cast<int>(w.size());
}
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "printf.cc"
#endif
#endif // FMT_PRINTF_H_

126
fmt/string.h Normal file
View File

@ -0,0 +1,126 @@
/*
Formatting library for C++ - string utilities
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_STRING_H_
#define FMT_STRING_H_
#include "format.h"
namespace fmt {
namespace internal {
// A buffer that stores data in ``std::basic_string``.
template <typename Char, typename Allocator = std::allocator<Char> >
class StringBuffer : public Buffer<Char> {
public:
typedef std::basic_string<Char, std::char_traits<Char>, Allocator> StringType;
private:
StringType data_;
protected:
virtual void grow(std::size_t size) FMT_OVERRIDE {
data_.resize(size);
this->ptr_ = &data_[0];
this->capacity_ = size;
}
public:
explicit StringBuffer(const Allocator &allocator = Allocator())
: data_(allocator) {}
// Moves the data to ``str`` clearing the buffer.
void move_to(StringType &str) {
data_.resize(this->size_);
str.swap(data_);
this->capacity_ = this->size_ = 0;
this->ptr_ = FMT_NULL;
}
};
} // namespace internal
/**
\rst
This class template provides operations for formatting and writing data
into a character stream. The output is stored in a ``std::basic_string``
that grows dynamically.
You can use one of the following typedefs for common character types
and the standard allocator:
+---------------+----------------------------+
| Type | Definition |
+===============+============================+
| StringWriter | BasicStringWriter<char> |
+---------------+----------------------------+
| WStringWriter | BasicStringWriter<wchar_t> |
+---------------+----------------------------+
**Example**::
StringWriter out;
out << "The answer is " << 42 << "\n";
This will write the following output to the ``out`` object:
.. code-block:: none
The answer is 42
The output can be moved to a ``std::basic_string`` with ``out.move_to()``.
\endrst
*/
template <typename Char, typename Allocator = std::allocator<Char> >
class BasicStringWriter : public BasicWriter<Char> {
private:
internal::StringBuffer<Char, Allocator> buffer_;
public:
/**
\rst
Constructs a :class:`fmt::BasicStringWriter` object.
\endrst
*/
explicit BasicStringWriter(const Allocator &allocator = Allocator())
: BasicWriter<Char>(buffer_), buffer_(allocator) {}
/**
\rst
Moves the buffer content to *str* clearing the buffer.
\endrst
*/
void move_to(std::basic_string<Char, std::char_traits<Char>, Allocator> &str) {
buffer_.move_to(str);
}
};
typedef BasicStringWriter<char> StringWriter;
typedef BasicStringWriter<wchar_t> WStringWriter;
/**
\rst
Converts *value* to ``std::string`` using the default format for type *T*.
**Example**::
#include "fmt/string.h"
std::string answer = fmt::to_string(42);
\endrst
*/
template <typename T>
std::string to_string(const T &value) {
fmt::MemoryWriter w;
w << value;
return w.str();
}
}
#endif // FMT_STRING_H_

143
fmt/time.h Normal file
View File

@ -0,0 +1,143 @@
/*
Formatting library for C++ - time formatting
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_TIME_H_
#define FMT_TIME_H_
#include "format.h"
#include <ctime>
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4702) // unreachable code
# pragma warning(disable: 4996) // "deprecated" functions
#endif
namespace fmt {
template <typename ArgFormatter>
void format_arg(BasicFormatter<char, ArgFormatter> &f,
const char *&format_str, const std::tm &tm) {
if (*format_str == ':')
++format_str;
const char *end = format_str;
while (*end && *end != '}')
++end;
if (*end != '}')
FMT_THROW(FormatError("missing '}' in format string"));
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
format.append(format_str, end + 1);
format[format.size() - 1] = '\0';
Buffer<char> &buffer = f.writer().buffer();
std::size_t start = buffer.size();
for (;;) {
std::size_t size = buffer.capacity() - start;
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
if (count != 0) {
buffer.resize(start + count);
break;
}
if (size >= format.size() * 256) {
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
format_str = end + 1;
}
namespace internal{
inline Null<> localtime_r(...) { return Null<>(); }
inline Null<> localtime_s(...) { return Null<>(); }
inline Null<> gmtime_r(...) { return Null<>(); }
inline Null<> gmtime_s(...) { return Null<>(); }
}
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
struct LocalTime {
std::time_t time_;
std::tm tm_;
LocalTime(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::Null<>) {
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
bool fallback(internal::Null<>) {
using namespace fmt::internal;
std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
};
LocalTime lt(time);
if (lt.run())
return lt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct GMTime {
std::time_t time_;
std::tm tm_;
GMTime(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::Null<>) {
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
bool fallback(internal::Null<>) {
std::tm *tm = std::gmtime(&time_);
if (tm != FMT_NULL) tm_ = *tm;
return tm != FMT_NULL;
}
};
GMTime gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
} //namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#endif // FMT_TIME_H_

1385
format.cc

File diff suppressed because it is too large Load Diff

View File

@ -10,22 +10,23 @@ platform = os.environ.get('PLATFORM')
path = os.environ['PATH']
cmake_command = ['cmake', '-DFMT_PEDANTIC=ON', '-DCMAKE_BUILD_TYPE=' + config]
if build == 'mingw':
cmake_command.append('-GMinGW Makefiles')
build_command = ['mingw32-make', '-j4']
test_command = ['mingw32-make', 'test']
# Remove the path to Git bin directory from $PATH because it breaks MinGW config.
path = path.replace(r'C:\Program Files (x86)\Git\bin', '')
os.environ['PATH'] = r'C:\MinGW\bin;' + path
cmake_command.append('-GMinGW Makefiles')
build_command = ['mingw32-make', '-j4']
test_command = ['mingw32-make', 'test']
# Remove the path to Git bin directory from $PATH because it breaks
# MinGW config.
path = path.replace(r'C:\Program Files (x86)\Git\bin', '')
os.environ['PATH'] = r'C:\MinGW\bin;' + path
else:
# Add MSBuild 14.0 to PATH as described in
# http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc.
os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\14.0\Bin;' + path
generator = 'Visual Studio 14 2015'
if platform == 'x64':
generator += ' Win64'
cmake_command.append('-G' + generator)
build_command = ['msbuild', '/m:4', '/p:Config=' + config, 'FORMAT.sln']
test_command = ['msbuild', 'RUN_TESTS.vcxproj']
# Add MSBuild 14.0 to PATH as described in
# http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc.
os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\14.0\Bin;' + path
generator = 'Visual Studio 14 2015'
if platform == 'x64':
generator += ' Win64'
cmake_command.append('-G' + generator)
build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4']
test_command = ['ctest', '-C', config]
check_call(cmake_command)
check_call(build_command)

View File

@ -20,3 +20,7 @@ build_script:
on_failure:
- appveyor PushArtifact Testing/Temporary/LastTest.log
- appveyor AddTest test
# Uncomment this to debug AppVeyor failures.
#on_finish:
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))

View File

@ -1,13 +0,0 @@
#!/usr/bin/env python
# Build the project with Biicode.
import glob, os, shutil
from subprocess import check_call
project_dir = 'biicode_project'
check_call(['bii', 'init', project_dir])
cppformat_dir = os.path.join(project_dir, 'blocks/vitaut/cppformat')
shutil.copytree('.', cppformat_dir, ignore=shutil.ignore_patterns(project_dir))
for f in glob.glob('support/biicode/*'):
shutil.copy(f, cppformat_dir)
check_call(['bii', 'cpp:build'], cwd=project_dir)

View File

@ -1,19 +0,0 @@
# Biicode configuration file
[paths]
# Local directories to look for headers (within block)
/
[dependencies]
# Manual adjust file implicit dependencies, add (+), remove (-), or overwrite (=)
CMakeLists.txt + cmake/FindSetEnv.cmake
format.h = format.cc
format.cc - test/* posix.cc
support/biicode/sample.cc - test/*
[mains]
# Manual adjust of files that define an executable
!test/test-main.cc
[parent]
vitaut/cppformat: 0

View File

@ -1,3 +0,0 @@
doc/*
breathe/*
gmock/*

View File

@ -1,17 +0,0 @@
#include "vitaut/cppformat/format.h"
class Date {
int year_, month_, day_;
public:
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
friend std::ostream &operator<<(std::ostream &os, const Date &d) {
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};
int main() {
std::string s = fmt::format("The date is {}", Date(2012, 12, 9));
fmt::print("Hello, {}!", "world"); // uses Python-like format string syntax
fmt::printf("\n%s", s); // uses printf format string syntax
}

View File

@ -1,18 +0,0 @@
# Initializes block variables
INIT_BIICODE_BLOCK()
# Actually create targets: EXEcutables and libraries.
ADD_BIICODE_TARGETS()
target_include_directories(${BII_BLOCK_TARGET} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
if (HAVE_OPEN)
target_compile_definitions(${BII_BLOCK_TARGET} INTERFACE -DFMT_USE_FILE_DESCRIPTORS=1)
endif ()
if (CMAKE_COMPILER_IS_GNUCXX)
target_compile_options(${BII_BLOCK_TARGET} INTERFACE -Wall -Wextra -Wshadow -pedantic)
endif ()
if (CPP11_FLAG AND FMT_PEDANTIC)
target_compile_options(${BII_BLOCK_TARGET} INTERFACE ${CPP11_FLAG})
endif ()

85
support/cmake/cxx11.cmake Normal file
View File

@ -0,0 +1,85 @@
# C++11 feature support detection
if (NOT FMT_USE_CPP11)
return()
endif ()
include(CheckCXXCompilerFlag)
if (FMT_USE_CPP11)
check_cxx_compiler_flag(-std=c++11 HAVE_STD_CPP11_FLAG)
if (HAVE_STD_CPP11_FLAG)
# Check if including cmath works with -std=c++11 and -O3.
# It may not in MinGW due to bug http://ehc.ac/p/mingw/bugs/2250/.
set(CMAKE_REQUIRED_FLAGS "-std=c++11 -O3")
check_cxx_source_compiles("
#include <cmath>
int main() {}" FMT_CPP11_CMATH)
# Check if including <unistd.h> works with -std=c++11.
# It may not in MinGW due to bug http://sourceforge.net/p/mingw/bugs/2024/.
check_cxx_source_compiles("
#include <unistd.h>
int main() {}" FMT_CPP11_UNISTD_H)
# Check if snprintf works with -std=c++11. It may not in MinGW.
check_cxx_source_compiles("
#include <stdio.h>
int main() {
char buffer[10];
snprintf(buffer, 10, \"foo\");
}" FMT_CPP11_SNPRINTF)
if (FMT_CPP11_CMATH AND FMT_CPP11_UNISTD_H AND FMT_CPP11_SNPRINTF)
set(CPP11_FLAG -std=c++11)
else ()
check_cxx_compiler_flag(-std=gnu++11 HAVE_STD_GNUPP11_FLAG)
if (HAVE_STD_CPP11_FLAG)
set(CPP11_FLAG -std=gnu++11)
endif ()
endif ()
set(CMAKE_REQUIRED_FLAGS )
else ()
check_cxx_compiler_flag(-std=c++0x HAVE_STD_CPP0X_FLAG)
if (HAVE_STD_CPP0X_FLAG)
set(CPP11_FLAG -std=c++0x)
endif ()
endif ()
endif ()
if (CMAKE_CXX_STANDARD)
# Don't use -std compiler flag if CMAKE_CXX_STANDARD is specified.
set(CPP11_FLAG )
endif ()
set(CMAKE_REQUIRED_FLAGS ${CPP11_FLAG})
# Check if variadic templates are working and not affected by GCC bug 39653:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
check_cxx_source_compiles("
template <class T, class ...Types>
struct S { typedef typename S<Types...>::type type; };
int main() {}" SUPPORTS_VARIADIC_TEMPLATES)
# Check if initializer lists are supported.
check_cxx_source_compiles("
#include <initializer_list>
int main() {}" SUPPORTS_INITIALIZER_LIST)
# Check if enum bases are available
check_cxx_source_compiles("
enum C : char {A};
int main() {}"
SUPPORTS_ENUM_BASE)
# Check if type traits are available
check_cxx_source_compiles("
#include <type_traits>
class C { void operator=(const C&); };
int main() { static_assert(!std::is_copy_assignable<C>::value, \"\"); }"
SUPPORTS_TYPE_TRAITS)
# Check if user-defined literals are available
check_cxx_source_compiles("
void operator\"\" _udl(long double);
int main() {}"
SUPPORTS_USER_DEFINED_LITERALS)
set(CMAKE_REQUIRED_FLAGS )

View File

@ -0,0 +1,4 @@
@PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake)
check_required_components(fmt)

581
support/docopt.py Normal file
View File

@ -0,0 +1,581 @@
"""Pythonic command-line interface parser that will make you smile.
* http://docopt.org
* Repository and issue-tracker: https://github.com/docopt/docopt
* Licensed under terms of MIT license (see LICENSE-MIT)
* Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com
"""
import sys
import re
__all__ = ['docopt']
__version__ = '0.6.1'
class DocoptLanguageError(Exception):
"""Error in construction of usage-message by developer."""
class DocoptExit(SystemExit):
"""Exit in case user invoked program with incorrect arguments."""
usage = ''
def __init__(self, message=''):
SystemExit.__init__(self, (message + '\n' + self.usage).strip())
class Pattern(object):
def __eq__(self, other):
return repr(self) == repr(other)
def __hash__(self):
return hash(repr(self))
def fix(self):
self.fix_identities()
self.fix_repeating_arguments()
return self
def fix_identities(self, uniq=None):
"""Make pattern-tree tips point to same object if they are equal."""
if not hasattr(self, 'children'):
return self
uniq = list(set(self.flat())) if uniq is None else uniq
for i, child in enumerate(self.children):
if not hasattr(child, 'children'):
assert child in uniq
self.children[i] = uniq[uniq.index(child)]
else:
child.fix_identities(uniq)
def fix_repeating_arguments(self):
"""Fix elements that should accumulate/increment values."""
either = [list(child.children) for child in transform(self).children]
for case in either:
for e in [child for child in case if case.count(child) > 1]:
if type(e) is Argument or type(e) is Option and e.argcount:
if e.value is None:
e.value = []
elif type(e.value) is not list:
e.value = e.value.split()
if type(e) is Command or type(e) is Option and e.argcount == 0:
e.value = 0
return self
def transform(pattern):
"""Expand pattern into an (almost) equivalent one, but with single Either.
Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d)
Quirks: [-a] => (-a), (-a...) => (-a -a)
"""
result = []
groups = [[pattern]]
while groups:
children = groups.pop(0)
parents = [Required, Optional, OptionsShortcut, Either, OneOrMore]
if any(t in map(type, children) for t in parents):
child = [c for c in children if type(c) in parents][0]
children.remove(child)
if type(child) is Either:
for c in child.children:
groups.append([c] + children)
elif type(child) is OneOrMore:
groups.append(child.children * 2 + children)
else:
groups.append(child.children + children)
else:
result.append(children)
return Either(*[Required(*e) for e in result])
class LeafPattern(Pattern):
"""Leaf/terminal node of a pattern tree."""
def __init__(self, name, value=None):
self.name, self.value = name, value
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value)
def flat(self, *types):
return [self] if not types or type(self) in types else []
def match(self, left, collected=None):
collected = [] if collected is None else collected
pos, match = self.single_match(left)
if match is None:
return False, left, collected
left_ = left[:pos] + left[pos + 1:]
same_name = [a for a in collected if a.name == self.name]
if type(self.value) in (int, list):
if type(self.value) is int:
increment = 1
else:
increment = ([match.value] if type(match.value) is str
else match.value)
if not same_name:
match.value = increment
return True, left_, collected + [match]
same_name[0].value += increment
return True, left_, collected
return True, left_, collected + [match]
class BranchPattern(Pattern):
"""Branch/inner node of a pattern tree."""
def __init__(self, *children):
self.children = list(children)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__,
', '.join(repr(a) for a in self.children))
def flat(self, *types):
if type(self) in types:
return [self]
return sum([child.flat(*types) for child in self.children], [])
class Argument(LeafPattern):
def single_match(self, left):
for n, pattern in enumerate(left):
if type(pattern) is Argument:
return n, Argument(self.name, pattern.value)
return None, None
@classmethod
def parse(class_, source):
name = re.findall('(<\S*?>)', source)[0]
value = re.findall('\[default: (.*)\]', source, flags=re.I)
return class_(name, value[0] if value else None)
class Command(Argument):
def __init__(self, name, value=False):
self.name, self.value = name, value
def single_match(self, left):
for n, pattern in enumerate(left):
if type(pattern) is Argument:
if pattern.value == self.name:
return n, Command(self.name, True)
else:
break
return None, None
class Option(LeafPattern):
def __init__(self, short=None, long=None, argcount=0, value=False):
assert argcount in (0, 1)
self.short, self.long, self.argcount = short, long, argcount
self.value = None if value is False and argcount else value
@classmethod
def parse(class_, option_description):
short, long, argcount, value = None, None, 0, False
options, _, description = option_description.strip().partition(' ')
options = options.replace(',', ' ').replace('=', ' ')
for s in options.split():
if s.startswith('--'):
long = s
elif s.startswith('-'):
short = s
else:
argcount = 1
if argcount:
matched = re.findall('\[default: (.*)\]', description, flags=re.I)
value = matched[0] if matched else None
return class_(short, long, argcount, value)
def single_match(self, left):
for n, pattern in enumerate(left):
if self.name == pattern.name:
return n, pattern
return None, None
@property
def name(self):
return self.long or self.short
def __repr__(self):
return 'Option(%r, %r, %r, %r)' % (self.short, self.long,
self.argcount, self.value)
class Required(BranchPattern):
def match(self, left, collected=None):
collected = [] if collected is None else collected
l = left
c = collected
for pattern in self.children:
matched, l, c = pattern.match(l, c)
if not matched:
return False, left, collected
return True, l, c
class Optional(BranchPattern):
def match(self, left, collected=None):
collected = [] if collected is None else collected
for pattern in self.children:
m, left, collected = pattern.match(left, collected)
return True, left, collected
class OptionsShortcut(Optional):
"""Marker/placeholder for [options] shortcut."""
class OneOrMore(BranchPattern):
def match(self, left, collected=None):
assert len(self.children) == 1
collected = [] if collected is None else collected
l = left
c = collected
l_ = None
matched = True
times = 0
while matched:
# could it be that something didn't match but changed l or c?
matched, l, c = self.children[0].match(l, c)
times += 1 if matched else 0
if l_ == l:
break
l_ = l
if times >= 1:
return True, l, c
return False, left, collected
class Either(BranchPattern):
def match(self, left, collected=None):
collected = [] if collected is None else collected
outcomes = []
for pattern in self.children:
matched, _, _ = outcome = pattern.match(left, collected)
if matched:
outcomes.append(outcome)
if outcomes:
return min(outcomes, key=lambda outcome: len(outcome[1]))
return False, left, collected
class Tokens(list):
def __init__(self, source, error=DocoptExit):
self += source.split() if hasattr(source, 'split') else source
self.error = error
@staticmethod
def from_pattern(source):
source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source)
source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s]
return Tokens(source, error=DocoptLanguageError)
def move(self):
return self.pop(0) if len(self) else None
def current(self):
return self[0] if len(self) else None
def parse_long(tokens, options):
"""long ::= '--' chars [ ( ' ' | '=' ) chars ] ;"""
long, eq, value = tokens.move().partition('=')
assert long.startswith('--')
value = None if eq == value == '' else value
similar = [o for o in options if o.long == long]
if tokens.error is DocoptExit and similar == []: # if no exact match
similar = [o for o in options if o.long and o.long.startswith(long)]
if len(similar) > 1: # might be simply specified ambiguously 2+ times?
raise tokens.error('%s is not a unique prefix: %s?' %
(long, ', '.join(o.long for o in similar)))
elif len(similar) < 1:
argcount = 1 if eq == '=' else 0
o = Option(None, long, argcount)
options.append(o)
if tokens.error is DocoptExit:
o = Option(None, long, argcount, value if argcount else True)
else:
o = Option(similar[0].short, similar[0].long,
similar[0].argcount, similar[0].value)
if o.argcount == 0:
if value is not None:
raise tokens.error('%s must not have an argument' % o.long)
else:
if value is None:
if tokens.current() in [None, '--']:
raise tokens.error('%s requires argument' % o.long)
value = tokens.move()
if tokens.error is DocoptExit:
o.value = value if value is not None else True
return [o]
def parse_shorts(tokens, options):
"""shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;"""
token = tokens.move()
assert token.startswith('-') and not token.startswith('--')
left = token.lstrip('-')
parsed = []
while left != '':
short, left = '-' + left[0], left[1:]
similar = [o for o in options if o.short == short]
if len(similar) > 1:
raise tokens.error('%s is specified ambiguously %d times' %
(short, len(similar)))
elif len(similar) < 1:
o = Option(short, None, 0)
options.append(o)
if tokens.error is DocoptExit:
o = Option(short, None, 0, True)
else: # why copying is necessary here?
o = Option(short, similar[0].long,
similar[0].argcount, similar[0].value)
value = None
if o.argcount != 0:
if left == '':
if tokens.current() in [None, '--']:
raise tokens.error('%s requires argument' % short)
value = tokens.move()
else:
value = left
left = ''
if tokens.error is DocoptExit:
o.value = value if value is not None else True
parsed.append(o)
return parsed
def parse_pattern(source, options):
tokens = Tokens.from_pattern(source)
result = parse_expr(tokens, options)
if tokens.current() is not None:
raise tokens.error('unexpected ending: %r' % ' '.join(tokens))
return Required(*result)
def parse_expr(tokens, options):
"""expr ::= seq ( '|' seq )* ;"""
seq = parse_seq(tokens, options)
if tokens.current() != '|':
return seq
result = [Required(*seq)] if len(seq) > 1 else seq
while tokens.current() == '|':
tokens.move()
seq = parse_seq(tokens, options)
result += [Required(*seq)] if len(seq) > 1 else seq
return [Either(*result)] if len(result) > 1 else result
def parse_seq(tokens, options):
"""seq ::= ( atom [ '...' ] )* ;"""
result = []
while tokens.current() not in [None, ']', ')', '|']:
atom = parse_atom(tokens, options)
if tokens.current() == '...':
atom = [OneOrMore(*atom)]
tokens.move()
result += atom
return result
def parse_atom(tokens, options):
"""atom ::= '(' expr ')' | '[' expr ']' | 'options'
| long | shorts | argument | command ;
"""
token = tokens.current()
result = []
if token in '([':
tokens.move()
matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token]
result = pattern(*parse_expr(tokens, options))
if tokens.move() != matching:
raise tokens.error("unmatched '%s'" % token)
return [result]
elif token == 'options':
tokens.move()
return [OptionsShortcut()]
elif token.startswith('--') and token != '--':
return parse_long(tokens, options)
elif token.startswith('-') and token not in ('-', '--'):
return parse_shorts(tokens, options)
elif token.startswith('<') and token.endswith('>') or token.isupper():
return [Argument(tokens.move())]
else:
return [Command(tokens.move())]
def parse_argv(tokens, options, options_first=False):
"""Parse command-line argument vector.
If options_first:
argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ;
else:
argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ;
"""
parsed = []
while tokens.current() is not None:
if tokens.current() == '--':
return parsed + [Argument(None, v) for v in tokens]
elif tokens.current().startswith('--'):
parsed += parse_long(tokens, options)
elif tokens.current().startswith('-') and tokens.current() != '-':
parsed += parse_shorts(tokens, options)
elif options_first:
return parsed + [Argument(None, v) for v in tokens]
else:
parsed.append(Argument(None, tokens.move()))
return parsed
def parse_defaults(doc):
defaults = []
for s in parse_section('options:', doc):
# FIXME corner case "bla: options: --foo"
_, _, s = s.partition(':') # get rid of "options:"
split = re.split('\n[ \t]*(-\S+?)', '\n' + s)[1:]
split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])]
options = [Option.parse(s) for s in split if s.startswith('-')]
defaults += options
return defaults
def parse_section(name, source):
pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)',
re.IGNORECASE | re.MULTILINE)
return [s.strip() for s in pattern.findall(source)]
def formal_usage(section):
_, _, section = section.partition(':') # drop "usage:"
pu = section.split()
return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )'
def extras(help, version, options, doc):
if help and any((o.name in ('-h', '--help')) and o.value for o in options):
print(doc.strip("\n"))
sys.exit()
if version and any(o.name == '--version' and o.value for o in options):
print(version)
sys.exit()
class Dict(dict):
def __repr__(self):
return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items()))
def docopt(doc, argv=None, help=True, version=None, options_first=False):
"""Parse `argv` based on command-line interface described in `doc`.
`docopt` creates your command-line interface based on its
description that you pass as `doc`. Such description can contain
--options, <positional-argument>, commands, which could be
[optional], (required), (mutually | exclusive) or repeated...
Parameters
----------
doc : str
Description of your command-line interface.
argv : list of str, optional
Argument vector to be parsed. sys.argv[1:] is used if not
provided.
help : bool (default: True)
Set to False to disable automatic help on -h or --help
options.
version : any object
If passed, the object will be printed if --version is in
`argv`.
options_first : bool (default: False)
Set to True to require options precede positional arguments,
i.e. to forbid options and positional arguments intermix.
Returns
-------
args : dict
A dictionary, where keys are names of command-line elements
such as e.g. "--verbose" and "<path>", and values are the
parsed values of those elements.
Example
-------
>>> from docopt import docopt
>>> doc = '''
... Usage:
... my_program tcp <host> <port> [--timeout=<seconds>]
... my_program serial <port> [--baud=<n>] [--timeout=<seconds>]
... my_program (-h | --help | --version)
...
... Options:
... -h, --help Show this screen and exit.
... --baud=<n> Baudrate [default: 9600]
... '''
>>> argv = ['tcp', '127.0.0.1', '80', '--timeout', '30']
>>> docopt(doc, argv)
{'--baud': '9600',
'--help': False,
'--timeout': '30',
'--version': False,
'<host>': '127.0.0.1',
'<port>': '80',
'serial': False,
'tcp': True}
See also
--------
* For video introduction see http://docopt.org
* Full documentation is available in README.rst as well as online
at https://github.com/docopt/docopt#readme
"""
argv = sys.argv[1:] if argv is None else argv
usage_sections = parse_section('usage:', doc)
if len(usage_sections) == 0:
raise DocoptLanguageError('"usage:" (case-insensitive) not found.')
if len(usage_sections) > 1:
raise DocoptLanguageError('More than one "usage:" (case-insensitive).')
DocoptExit.usage = usage_sections[0]
options = parse_defaults(doc)
pattern = parse_pattern(formal_usage(DocoptExit.usage), options)
# [default] syntax for argument is disabled
#for a in pattern.flat(Argument):
# same_name = [d for d in arguments if d.name == a.name]
# if same_name:
# a.value = same_name[0].value
argv = parse_argv(Tokens(argv), list(options), options_first)
pattern_options = set(pattern.flat(Option))
for options_shortcut in pattern.flat(OptionsShortcut):
doc_options = parse_defaults(doc)
options_shortcut.children = list(set(doc_options) - pattern_options)
#if any_options:
# options_shortcut.children += [Option(o.short, o.long, o.argcount)
# for o in argv if type(o) is Option]
extras(help, version, argv, doc)
matched, left, collected = pattern.fix().match(argv)
if matched and left == []: # better error message if left?
return Dict((a.name, a.value) for a in (pattern.flat() + collected))
raise DocoptExit()

View File

@ -1,46 +0,0 @@
# A file downloader.
import contextlib, os, tempfile, timer, urllib2, urlparse
class Downloader:
def __init__(self, dir=None):
self.dir = dir
# Downloads a file and removes it when exiting a block.
# Usage:
# d = Downloader()
# with d.download(url) as f:
# use_file(f)
def download(self, url, cookie=None):
suffix = os.path.splitext(urlparse.urlsplit(url)[2])[1]
fd, filename = tempfile.mkstemp(suffix=suffix, dir=self.dir)
os.close(fd)
with timer.print_time('Downloading', url, 'to', filename):
opener = urllib2.build_opener()
if cookie:
opener.addheaders.append(('Cookie', cookie))
num_tries = 2
for i in range(num_tries):
try:
f = opener.open(url)
except urllib2.URLError, e:
print('Failed to open url', url)
continue
length = f.headers.get('content-length')
if not length:
print('Failed to get content-length')
continue
length = int(length)
with open(filename, 'wb') as out:
count = 0
while count < length:
data = f.read(1024 * 1024)
count += len(data)
out.write(data)
@contextlib.contextmanager
def remove(filename):
try:
yield filename
finally:
os.remove(filename)
return remove(filename)

235
support/manage.py Executable file
View File

@ -0,0 +1,235 @@
#!/usr/bin/env python
"""Manage site and releases.
Usage:
manage.py release [<branch>]
manage.py site
"""
from __future__ import print_function
import datetime, docopt, fileinput, json, os
import re, requests, shutil, sys, tempfile
from contextlib import contextmanager
from distutils.version import LooseVersion
from subprocess import check_call
class Git:
def __init__(self, dir):
self.dir = dir
def call(self, method, args, **kwargs):
return check_call(['git', method] + list(args), **kwargs)
def add(self, *args):
return self.call('add', args, cwd=self.dir)
def checkout(self, *args):
return self.call('checkout', args, cwd=self.dir)
def clean(self, *args):
return self.call('clean', args, cwd=self.dir)
def clone(self, *args):
return self.call('clone', list(args) + [self.dir])
def commit(self, *args):
return self.call('commit', args, cwd=self.dir)
def pull(self, *args):
return self.call('pull', args, cwd=self.dir)
def push(self, *args):
return self.call('push', args, cwd=self.dir)
def reset(self, *args):
return self.call('reset', args, cwd=self.dir)
def update(self, *args):
clone = not os.path.exists(self.dir)
if clone:
self.clone(*args)
return clone
def clean_checkout(repo, branch):
repo.clean('-f', '-d')
repo.reset('--hard')
repo.checkout(branch)
class Runner:
def __init__(self, cwd):
self.cwd = cwd
def __call__(self, *args, **kwargs):
kwargs['cwd'] = kwargs.get('cwd', self.cwd)
check_call(args, **kwargs)
def create_build_env():
"""Create a build environment."""
class Env:
pass
env = Env()
# Import the documentation build module.
env.fmt_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(env.fmt_dir, 'doc'))
import build
env.build_dir = 'build'
# Virtualenv and repos are cached to speed up builds.
build.create_build_env(os.path.join(env.build_dir, 'virtualenv'))
env.fmt_repo = Git(os.path.join(env.build_dir, 'fmt'))
return env
@contextmanager
def rewrite(filename):
class Buffer:
pass
buffer = Buffer()
if not os.path.exists(filename):
buffer.data = ''
yield buffer
return
with open(filename) as f:
buffer.data = f.read()
yield buffer
with open(filename, 'w') as f:
f.write(buffer.data)
fmt_repo_url = 'git@github.com:fmtlib/fmt'
def update_site(env):
env.fmt_repo.update(fmt_repo_url)
doc_repo = Git(os.path.join(env.build_dir, 'fmtlib.github.io'))
doc_repo.update('git@github.com:fmtlib/fmtlib.github.io')
for version in ['1.0.0', '1.1.0', '2.0.0', '3.0.0']:
clean_checkout(env.fmt_repo, version)
target_doc_dir = os.path.join(env.fmt_repo.dir, 'doc')
# Remove the old theme.
for entry in os.listdir(target_doc_dir):
path = os.path.join(target_doc_dir, entry)
if os.path.isdir(path):
shutil.rmtree(path)
# Copy the new theme.
for entry in ['_static', '_templates', 'basic-bootstrap', 'bootstrap',
'conf.py', 'fmt.less']:
src = os.path.join(env.fmt_dir, 'doc', entry)
dst = os.path.join(target_doc_dir, entry)
copy = shutil.copytree if os.path.isdir(src) else shutil.copyfile
copy(src, dst)
# Rename index to contents.
contents = os.path.join(target_doc_dir, 'contents.rst')
if not os.path.exists(contents):
os.rename(os.path.join(target_doc_dir, 'index.rst'), contents)
# Fix issues in reference.rst/api.rst.
for filename in ['reference.rst', 'api.rst']:
pattern = re.compile('doxygenfunction.. (bin|oct|hexu|hex)$', re.M)
with rewrite(os.path.join(target_doc_dir, filename)) as b:
b.data = b.data.replace('std::ostream &', 'std::ostream&')
b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data)
b.data = b.data.replace('std::FILE*', 'std::FILE *')
b.data = b.data.replace('unsigned int', 'unsigned')
# Fix a broken link in index.rst.
index = os.path.join(target_doc_dir, 'index.rst')
with rewrite(index) as b:
b.data = b.data.replace(
'doc/latest/index.html#format-string-syntax', 'syntax.html')
# Build the docs.
html_dir = os.path.join(env.build_dir, 'html')
if os.path.exists(html_dir):
shutil.rmtree(html_dir)
include_dir = env.fmt_repo.dir
if LooseVersion(version) >= LooseVersion('3.0.0'):
include_dir = os.path.join(include_dir, 'fmt')
import build
build.build_docs(version, doc_dir=target_doc_dir,
include_dir=include_dir, work_dir=env.build_dir)
shutil.rmtree(os.path.join(html_dir, '.doctrees'))
# Create symlinks for older versions.
for link, target in {'index': 'contents', 'api': 'reference'}.items():
link = os.path.join(html_dir, link) + '.html'
target += '.html'
if os.path.exists(os.path.join(html_dir, target)) and \
not os.path.exists(link):
os.symlink(target, link)
# Copy docs to the website.
version_doc_dir = os.path.join(doc_repo.dir, version)
shutil.rmtree(version_doc_dir)
shutil.move(html_dir, version_doc_dir)
def release(args):
env = create_build_env()
fmt_repo = env.fmt_repo
branch = args.get('<branch>')
if branch is None:
branch = 'master'
if not fmt_repo.update('-b', branch, fmt_repo_url):
clean_checkout(fmt_repo, branch)
# Convert changelog from RST to GitHub-flavored Markdown and get the
# version.
changelog = 'ChangeLog.rst'
changelog_path = os.path.join(fmt_repo.dir, changelog)
import rst2md
changes, version = rst2md.convert(changelog_path)
cmakelists = 'CMakeLists.txt'
for line in fileinput.input(os.path.join(fmt_repo.dir, cmakelists),
inplace=True):
prefix = 'set(FMT_VERSION '
if line.startswith(prefix):
line = prefix + version + ')\n'
sys.stdout.write(line)
# Update the version in the changelog.
title_len = 0
for line in fileinput.input(changelog_path, inplace=True):
if line.decode('utf-8').startswith(version + ' - TBD'):
line = version + ' - ' + datetime.date.today().isoformat()
title_len = len(line)
line += '\n'
elif title_len:
line = '-' * title_len + '\n'
title_len = 0
sys.stdout.write(line)
# TODO: add new version to manage.py
fmt_repo.checkout('-B', 'release')
fmt_repo.add(changelog, cmakelists)
fmt_repo.commit('-m', 'Update version')
# Build the docs and package.
run = Runner(fmt_repo.dir)
run('cmake', '.')
run('make', 'doc', 'package_source')
update_site(env)
# Create a release on GitHub.
fmt_repo.push('origin', 'release')
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
params={'access_token': os.getenv('FMT_TOKEN')},
data=json.dumps({'tag_name': version,
'target_commitish': 'release',
'body': changes, 'draft': True}))
if r.status_code != 201:
raise Exception('Failed to create a release ' + str(r))
if __name__ == '__main__':
args = docopt.docopt(__doc__)
if args.get('release'):
release(args)
elif args.get('site'):
update_site(create_build_env())

View File

@ -1,183 +0,0 @@
#!/usr/bin/env python
# Release script
from __future__ import print_function
import datetime, fileinput, json, os, re, requests, shutil, sys, tempfile
from docutils import nodes, writers, core
from subprocess import check_call
class MDWriter(writers.Writer):
"""GitHub-flavored markdown writer"""
supported = ('md',)
"""Formats this writer supports."""
def translate(self):
translator = Translator(self.document)
self.document.walkabout(translator)
self.output = (translator.output, translator.version)
def is_github_ref(node):
return re.match('https://github.com/.*/(issues|pull)/.*', node['refuri'])
class Translator(nodes.NodeVisitor):
def __init__(self, document):
nodes.NodeVisitor.__init__(self, document)
self.output = ''
self.indent = 0
self.preserve_newlines = False
def write(self, text):
self.output += text.replace('\n', '\n' + ' ' * self.indent)
def visit_document(self, node):
pass
def depart_document(self, node):
pass
def visit_section(self, node):
pass
def depart_section(self, node):
# Skip all sections except the first one.
raise nodes.StopTraversal
def visit_title(self, node):
self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1)
raise nodes.SkipChildren
def depart_title(self, node):
pass
def visit_Text(self, node):
if not self.preserve_newlines:
node = node.replace('\n', ' ')
self.write(node)
def depart_Text(self, node):
pass
def visit_bullet_list(self, node):
pass
def depart_bullet_list(self, node):
pass
def visit_list_item(self, node):
self.write('* ')
self.indent += 2
def depart_list_item(self, node):
self.indent -= 2
self.write('\n\n')
def visit_paragraph(self, node):
pass
def depart_paragraph(self, node):
pass
def visit_reference(self, node):
if not is_github_ref(node):
self.write('[')
def depart_reference(self, node):
if not is_github_ref(node):
self.write('](' + node['refuri'] + ')')
def visit_target(self, node):
pass
def depart_target(self, node):
pass
def visit_literal(self, node):
self.write('`')
def depart_literal(self, node):
self.write('`')
def visit_literal_block(self, node):
self.write('\n\n```')
if 'c++' in node['classes']:
self.write('c++')
self.write('\n')
self.preserve_newlines = True
def depart_literal_block(self, node):
self.write('\n```\n')
self.preserve_newlines = False
def visit_inline(self, node):
pass
def depart_inline(self, node):
pass
class Runner:
def __init__(self):
self.cwd = '.'
def __call__(self, *args, **kwargs):
kwargs['cwd'] = kwargs.get('cwd', self.cwd)
check_call(args, **kwargs)
workdir = tempfile.mkdtemp()
try:
run = Runner()
cppformat_dir = os.path.join(workdir, 'cppformat')
run('git', 'clone', 'git@github.com:cppformat/cppformat.git', cppformat_dir)
# Convert changelog from RST to GitHub-flavored Markdown and get the version.
changelog = 'ChangeLog.rst'
changelog_path = os.path.join(cppformat_dir, changelog)
changes, version = core.publish_file(source_path=changelog_path, writer=MDWriter())
cmakelists = 'CMakeLists.txt'
for line in fileinput.input(os.path.join(cppformat_dir, cmakelists), inplace=True):
prefix = 'set(CPPFORMAT_VERSION '
if line.startswith(prefix):
line = prefix + version + ')\n'
sys.stdout.write(line)
# Update the version in the changelog.
title_len = 0
for line in fileinput.input(changelog_path, inplace=True):
if line.startswith(version + ' - TBD'):
line = version + ' - ' + datetime.date.today().isoformat()
title_len = len(line)
line += '\n'
elif title_len:
line = '-' * title_len + '\n'
title_len = 0
sys.stdout.write(line)
run.cwd = cppformat_dir
run('git', 'checkout', '-b', 'release')
run('git', 'add', changelog, cmakelists)
run('git', 'commit', '-m', 'Update version')
# Build the docs and package.
run('cmake', '.')
run('make', 'doc', 'package_source')
site_dir = os.path.join(workdir, 'cppformat.github.io')
run('git', 'clone', 'git@github.com:cppformat/cppformat.github.io.git', site_dir)
doc_dir = os.path.join(site_dir, version)
shutil.copytree(os.path.join(cppformat_dir, 'doc', 'html'), doc_dir,
ignore=shutil.ignore_patterns('.doctrees', '.buildinfo'))
run.cwd = site_dir
run('git', 'add', doc_dir)
run('git', 'commit', '-m', 'Update docs')
# Create a release on GitHub.
run('git', 'push', 'origin', 'release', cwd=cppformat_dir)
r = requests.post('https://api.github.com/repos/cppformat/cppformat/releases',
params={'access_token': os.getenv('CPPFORMAT_TOKEN')},
data=json.dumps({'tag_name1': version, 'target_commitish': 'release',
'body': changes, 'draft': True}))
if r.status_code != 201:
raise Exception('Failed to create a release ' + str(r))
finally:
shutil.rmtree(workdir)

127
support/rst2md.py Normal file
View File

@ -0,0 +1,127 @@
# reStructuredText (RST) to GitHub-flavored Markdown converter
import re
from docutils import core, nodes, writers
def is_github_ref(node):
return re.match('https://github.com/.*/(issues|pull)/.*', node['refuri'])
class Translator(nodes.NodeVisitor):
def __init__(self, document):
nodes.NodeVisitor.__init__(self, document)
self.output = ''
self.indent = 0
self.preserve_newlines = False
def write(self, text):
self.output += text.replace('\n', '\n' + ' ' * self.indent)
def visit_document(self, node):
pass
def depart_document(self, node):
pass
def visit_section(self, node):
pass
def depart_section(self, node):
# Skip all sections except the first one.
raise nodes.StopTraversal
def visit_title(self, node):
self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1)
raise nodes.SkipChildren
def depart_title(self, node):
pass
def visit_Text(self, node):
if not self.preserve_newlines:
node = node.replace('\n', ' ')
self.write(node)
def depart_Text(self, node):
pass
def visit_bullet_list(self, node):
pass
def depart_bullet_list(self, node):
pass
def visit_list_item(self, node):
self.write('* ')
self.indent += 2
def depart_list_item(self, node):
self.indent -= 2
self.write('\n\n')
def visit_paragraph(self, node):
pass
def depart_paragraph(self, node):
pass
def visit_reference(self, node):
if not is_github_ref(node):
self.write('[')
def depart_reference(self, node):
if not is_github_ref(node):
self.write('](' + node['refuri'] + ')')
def visit_target(self, node):
pass
def depart_target(self, node):
pass
def visit_literal(self, node):
self.write('`')
def depart_literal(self, node):
self.write('`')
def visit_literal_block(self, node):
self.write('\n\n```')
if 'c++' in node['classes']:
self.write('c++')
self.write('\n')
self.preserve_newlines = True
def depart_literal_block(self, node):
self.write('\n```\n')
self.preserve_newlines = False
def visit_inline(self, node):
pass
def depart_inline(self, node):
pass
def visit_image(self, node):
self.write('![](' + node['uri'] + ')')
def depart_image(self, node):
pass
class MDWriter(writers.Writer):
"""GitHub-flavored markdown writer"""
supported = ('md',)
"""Formats this writer supports."""
def translate(self):
translator = Translator(self.document)
self.document.walkabout(translator)
self.output = (translator.output, translator.version)
def convert(rst_path):
"""Converts RST file to Markdown."""
return core.publish_file(source_path=rst_path, writer=MDWriter())

View File

@ -1,2 +1,2 @@
If you are not redirected automatically, follow the
`link to the C++ Format documentation <http://cppformat.github.io/latest/>`_.
`link to the fmt documentation <http://fmtlib.net/latest/>`_.

View File

@ -2,15 +2,15 @@
{% block extrahead %}
<meta charset="UTF-8">
<meta http-equiv="refresh" content="1;url=http://cppformat.github.io/latest/">
<meta http-equiv="refresh" content="1;url=http://fmtlib.net/latest/">
<script type="text/javascript">
window.location.href = "http://cppformat.github.io/latest/"
window.location.href = "http://fmtlib.net/latest/"
</script>
<title>Page Redirection</title>
{% endblock %}
{% block document %}
If you are not redirected automatically, follow the <a href='http://cppformat.github.io/latest/'>link to the C++ Format documentation</a>.
If you are not redirected automatically, follow the <a href='http://fmtlib.net/latest/'>link to the fmt documentation</a>.
{% endblock %}
{% block footer %}

View File

@ -1,35 +0,0 @@
# A with statement based timer.
from __future__ import print_function
from contextlib import contextmanager
import timeit
class Timer:
"""
A with statement based timer.
Usage:
t = Timer()
with t:
do_something()
time = t.time
"""
def __enter__(self):
self.start = timeit.default_timer()
def __exit__(self, type, value, traceback):
finish = timeit.default_timer()
self.time = finish - self.start
@contextmanager
def print_time(*args):
"""
Measures and prints the time taken to execute nested code.
args: Additional arguments to print.
"""
t = Timer()
print(*args)
with t:
yield
print(*args, end=' ')
print('finished in {0:.2f} second(s)'.format(t.time))

View File

@ -6,25 +6,28 @@ import errno, os, re, shutil, sys, tempfile, urllib
from subprocess import call, check_call, check_output, Popen, PIPE, STDOUT
def rmtree_if_exists(dir):
try:
shutil.rmtree(dir)
except OSError as e:
if e.errno == errno.ENOENT:
pass
try:
shutil.rmtree(dir)
except OSError as e:
if e.errno == errno.ENOENT:
pass
build = os.environ['BUILD']
if build == 'Doc':
travis = 'TRAVIS' in os.environ
# Install dependencies.
if travis:
def makedirs_if_not_exist(dir):
try:
os.makedirs(dir)
except OSError as e:
if e.errno != errno.EEXIST:
raise
def install_dependencies():
branch = os.environ['TRAVIS_BRANCH']
if branch != 'master':
print('Branch: ' + branch)
exit(0) # Ignore non-master branches
check_call('curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | ' +
'sudo apt-key add -', shell=True)
check_call('echo "deb https://deb.nodesource.com/node_0.10 precise main" | ' +
'sudo tee /etc/apt/sources.list.d/nodesource.list', shell=True)
print('Branch: ' + branch)
exit(0) # Ignore non-master branches
check_call('curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key ' +
'| sudo apt-key add -', shell=True)
check_call('echo "deb https://deb.nodesource.com/node_0.10 precise main" ' +
'| sudo tee /etc/apt/sources.list.d/nodesource.list', shell=True)
check_call(['sudo', 'apt-get', 'update'])
check_call(['sudo', 'apt-get', 'install', 'python-virtualenv', 'nodejs'])
check_call(['npm', 'install', '-g', 'less', 'less-plugin-clean-css'])
@ -32,46 +35,84 @@ if build == 'Doc':
urllib.urlretrieve('http://mirrors.kernel.org/ubuntu/pool/main/d/doxygen/' +
deb_file, deb_file)
check_call(['sudo', 'dpkg', '-i', deb_file])
cppformat_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, os.path.join(cppformat_dir, 'doc'))
import build
html_dir = build.build_docs()
repo = 'cppformat.github.io'
if travis and 'KEY' not in os.environ:
# Don't update the repo if building on Travis from an account that doesn't
# have push access.
print('Skipping update of ' + repo)
exit(0)
# Clone the cppformat.github.io repo.
rmtree_if_exists(repo)
git_url = 'https://github.com/' if travis else 'git@github.com:'
check_call(['git', 'clone', git_url + 'cppformat/{}.git'.format(repo)])
# Copy docs to the repo.
target_dir = os.path.join(repo, 'dev')
rmtree_if_exists(target_dir)
shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*'))
if travis:
check_call(['git', 'config', '--global', 'user.name', 'amplbot'])
check_call(['git', 'config', '--global', 'user.email', 'viz@ampl.com'])
# Push docs to GitHub pages.
check_call(['git', 'add', '--all'], cwd=repo)
if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo):
check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo)
cmd = 'git push'
if travis:
cmd += ' https://$KEY@github.com/cppformat/cppformat.github.io.git master'
p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo)
# Print the output without the key.
print(p.communicate()[0].replace(os.environ['KEY'], '$KEY'))
if p.returncode != 0:
raise CalledProcessError(p.returncode, cmd)
exit(0)
check_call(['git', 'submodule', 'update', '--init'])
check_call(['cmake', '-DCMAKE_BUILD_TYPE=' + build, '-DFMT_PEDANTIC=ON', '.'])
check_call(['make', '-j4'])
fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
build = os.environ['BUILD']
if build == 'Doc':
travis = 'TRAVIS' in os.environ
if travis:
install_dependencies()
sys.path.insert(0, os.path.join(fmt_dir, 'doc'))
import build
build.create_build_env()
html_dir = build.build_docs()
repo = 'fmtlib.github.io'
if travis and 'KEY' not in os.environ:
# Don't update the repo if building on Travis from an account that
# doesn't have push access.
print('Skipping update of ' + repo)
exit(0)
# Clone the fmtlib.github.io repo.
rmtree_if_exists(repo)
git_url = 'https://github.com/' if travis else 'git@github.com:'
check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)])
# Copy docs to the repo.
target_dir = os.path.join(repo, 'dev')
rmtree_if_exists(target_dir)
shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*'))
if travis:
check_call(['git', 'config', '--global', 'user.name', 'amplbot'])
check_call(['git', 'config', '--global', 'user.email', 'viz@ampl.com'])
# Push docs to GitHub pages.
check_call(['git', 'add', '--all'], cwd=repo)
if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo):
check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo)
cmd = 'git push'
if travis:
cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master'
p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo)
# Print the output without the key.
print(p.communicate()[0].replace(os.environ['KEY'], '$KEY'))
if p.returncode != 0:
raise CalledProcessError(p.returncode, cmd)
exit(0)
standard = os.environ['STANDARD']
install_dir = os.path.join(fmt_dir, "_install")
build_dir = os.path.join(fmt_dir, "_build")
test_build_dir = os.path.join(fmt_dir, "_build_test")
# Configure library.
makedirs_if_not_exist(build_dir)
common_cmake_flags = [
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build
]
extra_cmake_flags = []
if standard != '0x':
extra_cmake_flags = [
'-DCMAKE_CXX_FLAGS=-std=c++' + standard, '-DFMT_USE_CPP11=OFF'
]
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', fmt_dir] +
common_cmake_flags + extra_cmake_flags, cwd=build_dir)
# Build library.
check_call(['make', '-j4'], cwd=build_dir)
# Test library.
env = os.environ.copy()
env['CTEST_OUTPUT_ON_FAILURE'] = '1'
if call(['make', 'test'], env=env):
with open('Testing/Temporary/LastTest.log', 'r') as f:
print(f.read())
if call(['make', 'test'], env=env, cwd=build_dir):
with open('Testing/Temporary/LastTest.log', 'r') as f:
print(f.read())
sys.exit(-1)
# Install library.
check_call(['make', 'install'], cwd=build_dir)
# Test installation.
makedirs_if_not_exist(test_build_dir)
check_call(['cmake', '-DCMAKE_CXX_FLAGS=-std=c++' + standard,
os.path.join(fmt_dir, "test", "find-package-test")] +
common_cmake_flags, cwd=test_build_dir)
check_call(['make', '-j4'], cwd=test_build_dir)

View File

@ -8,23 +8,23 @@ import shutil, tempfile
from subprocess import check_output, STDOUT
class Git:
def __init__(self, dir):
self.dir = dir
def __init__(self, dir):
self.dir = dir
def __call__(self, *args):
output = check_output(['git'] + list(args), cwd=self.dir, stderr=STDOUT)
print(output)
return output
def __call__(self, *args):
output = check_output(['git'] + list(args), cwd=self.dir, stderr=STDOUT)
print(output)
return output
dir = tempfile.mkdtemp()
try:
git = Git(dir)
git('clone', '-b', 'coverity', 'git@github.com:cppformat/cppformat.git', dir)
output = git('merge', '-X', 'theirs', '--no-commit', 'origin/master')
if 'Fast-forward' not in output:
git('reset', 'HEAD', '.travis.yml')
git('checkout', '--', '.travis.yml')
git('commit', '-m', 'Update coverity branch')
git('push')
git = Git(dir)
git('clone', '-b', 'coverity', 'git@github.com:fmtlib/fmt.git', dir)
output = git('merge', '-X', 'theirs', '--no-commit', 'origin/master')
if 'Fast-forward' not in output:
git('reset', 'HEAD', '.travis.yml')
git('checkout', '--', '.travis.yml')
git('commit', '-m', 'Update coverity branch')
git('push')
finally:
shutil.rmtree(dir)
shutil.rmtree(dir)

View File

@ -1,15 +1,16 @@
set(FMT_GMOCK_DIR ../gmock)
include_directories(.. ${FMT_GMOCK_DIR})
#------------------------------------------------------------------------------
# Build the google test library
# We compile Google Test ourselves instead of using pre-compiled libraries.
# See the Google Test FAQ "Why is it not recommended to install a
# pre-compiled copy of Google Test (for example, into /usr/local)?"
# at http://code.google.com/p/googletest/wiki/FAQ for more details.
add_library(gmock STATIC
${FMT_GMOCK_DIR}/gmock-gtest-all.cc ${FMT_GMOCK_DIR}/gmock/gmock.h
${FMT_GMOCK_DIR}/gtest/gtest.h ${FMT_GMOCK_DIR}/gtest/gtest-spi.h)
gmock-gtest-all.cc gmock/gmock.h gtest/gtest.h gtest/gtest-spi.h)
target_compile_options(gmock PUBLIC ${CPP11_FLAG})
target_compile_definitions(gmock PUBLIC GTEST_HAS_STD_WSTRING=1)
target_include_directories(gmock PUBLIC .)
find_package(Threads)
if (Threads_FOUND)
target_link_libraries(gmock ${CMAKE_THREAD_LIBS_INIT})
@ -17,20 +18,8 @@ else ()
target_compile_definitions(gmock PUBLIC GTEST_HAS_PTHREAD=0)
endif ()
# Check if variadic templates are working and not affected by GCC bug 39653:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
check_cxx_source_compiles("
template <class T, class ...Types>
struct S { typedef typename S<Types...>::type type; };
int main() {}" FMT_VARIADIC_TEMPLATES)
# Check if initializer lists are supported.
check_cxx_source_compiles("
#include <initializer_list>
int main() {}" FMT_INITIALIZER_LIST)
if (NOT FMT_VARIADIC_TEMPLATES OR NOT FMT_INITIALIZER_LIST)
add_definitions(-DGTEST_LANG_CXX11=0)
if (NOT SUPPORTS_VARIADIC_TEMPLATES OR NOT SUPPORTS_INITIALIZER_LIST)
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
endif ()
# Workaround a bug in implementation of variadic templates in MSVC11.
@ -43,113 +32,141 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_definitions(gmock PUBLIC GTEST_USE_OWN_TR1_TUPLE=1)
endif ()
#------------------------------------------------------------------------------
# Build the actual library tests
set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc)
add_library(test-main STATIC ${TEST_MAIN_SRC})
target_link_libraries(test-main cppformat gmock)
target_compile_definitions(test-main PUBLIC
FMT_USE_FILE_DESCRIPTORS=$<BOOL:${HAVE_OPEN}>)
target_link_libraries(test-main gmock fmt)
include(CheckCXXCompilerFlag)
# Workaround GTest bug https://github.com/google/googletest/issues/705.
check_cxx_compiler_flag(
-fno-delete-null-pointer-checks HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
if (HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
target_compile_options(test-main PUBLIC -fno-delete-null-pointer-checks)
endif ()
# Use less strict pedantic flags for the tests because GMock doesn't compile
# cleanly with -pedantic and -std=c++98.
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wno-long-long -Wno-variadic-macros)
endif ()
function(add_fmt_executable name)
add_executable(${name} ${ARGN})
if (MINGW)
target_link_libraries(${name} -static-libgcc -static-libstdc++)
endif ()
endfunction()
# Adds a test.
# Usage: add_fmt_test(name [CUSTOM_LINK] srcs...)
# Usage: add_fmt_test(name srcs...)
function(add_fmt_test name)
cmake_parse_arguments(add_fmt_test CUSTOM_LINK "" "" ${ARGN})
add_executable(${name} ${name}.cc ${add_fmt_test_UNPARSED_ARGUMENTS})
add_fmt_executable(${name} ${name}.cc ${ARGN})
target_link_libraries(${name} test-main)
if (NOT add_fmt_test_CUSTOM_LINK)
target_link_libraries(${name} cppformat)
# define if certain c++ features can be used
target_compile_definitions(${name} PRIVATE
FMT_USE_TYPE_TRAITS=$<BOOL:${SUPPORTS_TYPE_TRAITS}>
FMT_USE_ENUM_BASE=$<BOOL:${SUPPORTS_ENUM_BASE}>)
if (FMT_PEDANTIC)
target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
add_test(NAME ${name} COMMAND ${name})
endfunction()
add_fmt_test(assert-test)
add_fmt_test(container-test)
add_fmt_test(gtest-extra-test)
add_fmt_test(format-test)
if (FMT_PEDANTIC AND MSVC)
set_target_properties(format-test PROPERTIES COMPILE_FLAGS /W4)
endif ()
add_fmt_test(format-impl-test CUSTOM_LINK)
add_fmt_test(format-impl-test)
add_fmt_test(ostream-test)
add_fmt_test(printf-test)
foreach (target format-test printf-test)
if (FMT_PEDANTIC AND CMAKE_COMPILER_IS_GNUCXX)
set_target_properties(${target} PROPERTIES COMPILE_FLAGS
"-Wall -Wextra -pedantic -Wno-long-long -Wno-variadic-macros")
endif ()
if (CPP11_FLAG)
set_target_properties(${target} PROPERTIES COMPILE_FLAGS ${CPP11_FLAG})
endif ()
endforeach ()
add_fmt_test(string-test)
add_fmt_test(time-test)
add_fmt_test(util-test mock-allocator.h)
if (CPP11_FLAG)
set_target_properties(util-test PROPERTIES COMPILE_FLAGS ${CPP11_FLAG})
add_fmt_test(macro-test)
add_fmt_test(custom-formatter-test)
# Enable stricter options for one test to make sure that the header is free of
# warnings.
if (FMT_PEDANTIC AND MSVC)
target_compile_options(format-test PRIVATE /W4)
endif ()
check_cxx_source_compiles("
enum C : char {A};
int main() {}"
HAVE_ENUM_BASE)
if (HAVE_ENUM_BASE)
set_target_properties(util-test
PROPERTIES COMPILE_DEFINITIONS "FMT_USE_ENUM_BASE=1")
endif ()
foreach (src ${FMT_SOURCES})
set(FMT_TEST_SOURCES ${FMT_TEST_SOURCES} ../${src})
endforeach ()
check_cxx_source_compiles("
#include <type_traits>
class C { void operator=(const C&); };
int main() { static_assert(!std::is_copy_assignable<C>::value, \"\"); }"
HAVE_TYPE_TRAITS)
if (HAVE_TYPE_TRAITS)
foreach (target format-test util-test)
set_target_properties(${target}
PROPERTIES COMPILE_DEFINITIONS "FMT_USE_TYPE_TRAITS=1")
endforeach ()
endif ()
add_executable(macro-test macro-test.cc ${FMT_TEST_SOURCES} ${TEST_MAIN_SRC})
target_link_libraries(macro-test gmock)
if (HAVE_OPEN)
add_executable(posix-mock-test posix-mock-test.cc ../format.cc ${TEST_MAIN_SRC})
add_fmt_executable(posix-mock-test
posix-mock-test.cc ../fmt/format.cc ../fmt/printf.cc ${TEST_MAIN_SRC})
target_include_directories(posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR})
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
target_link_libraries(posix-mock-test gmock)
add_test(NAME posix-mock-test COMMAND posix-mock-test)
add_fmt_test(posix-test)
endif ()
add_executable(header-only-test
add_fmt_executable(header-only-test
header-only-test.cc header-only-test2.cc test-main.cc)
set_target_properties(header-only-test
PROPERTIES COMPILE_DEFINITIONS "FMT_HEADER_ONLY=1")
target_link_libraries(header-only-test gmock)
if (TARGET fmt-header-only)
target_link_libraries(header-only-test fmt-header-only)
else ()
target_include_directories(header-only-test PRIVATE ${PROJECT_SOURCE_DIR})
target_compile_definitions(header-only-test PRIVATE FMT_HEADER_ONLY=1)
endif ()
# Test that the library can be compiled with exceptions disabled.
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
if (HAVE_FNO_EXCEPTIONS_FLAG)
add_library(noexception-test STATIC ../format.cc)
set_target_properties(noexception-test
PROPERTIES COMPILE_FLAGS -fno-exceptions)
endif ()
# Test compilation with default flags.
if (FMT_TEST_DEFAULT_FLAGS)
file(GLOB src RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cc *.h)
foreach (s ${FMT_SOURCES})
set(src ${src} ../${s})
endforeach ()
add_library(testformat STATIC ${src})
add_library(noexception-test ../fmt/format.cc)
target_include_directories(noexception-test PRIVATE ${PROJECT_SOURCE_DIR})
target_compile_options(noexception-test PRIVATE -fno-exceptions)
endif ()
if (FMT_PEDANTIC)
# Test that the library compiles without windows.h.
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_library(no-windows-h-test ../fmt/format.cc)
target_include_directories(no-windows-h-test PRIVATE ${PROJECT_SOURCE_DIR})
target_compile_definitions(no-windows-h-test PRIVATE FMT_USE_WINDOWS_H=0)
endif ()
add_test(compile-test ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/compile-test"
"${CMAKE_CURRENT_BINARY_DIR}/compile-test"
--build-generator ${CMAKE_GENERATOR}
--build-makeprogram ${CMAKE_MAKE_PROGRAM})
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
--build-options
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCPP11_FLAG=${CPP11_FLAG}"
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
# Test that the library compiles without windows.h.
add_library(no-windows-h-test ../format.cc)
set_target_properties(no-windows-h-test
PROPERTIES COMPILE_DEFINITIONS "FMT_USE_WINDOWS_H=0")
# test if the targets are findable from the build directory
add_test(find-package-test ${CMAKE_CTEST_COMMAND}
-C ${CMAKE_BUILD_TYPE}
--build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/find-package-test"
"${CMAKE_CURRENT_BINARY_DIR}/find-package-test"
--build-generator ${CMAKE_GENERATOR}
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
--build-options
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DFMT_DIR=${PROJECT_BINARY_DIR}"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
# test if the targets are findable when add_subdirectory is used
add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND}
-C ${CMAKE_BUILD_TYPE}
--build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/add-subdirectory-test"
"${CMAKE_CURRENT_BINARY_DIR}/add-subdirectory-test"
--build-generator ${CMAKE_GENERATOR}
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
--build-options
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif ()

View File

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 2.8.12)
project(fmt-test)
add_subdirectory(../.. fmt)
add_executable(library-test "main.cc")
target_link_libraries(library-test fmt::fmt)
if (TARGET fmt::fmt-header-only)
add_executable(header-only-test "main.cc")
target_link_libraries(header-only-test fmt::fmt-header-only)
endif ()

View File

@ -0,0 +1,6 @@
#include "fmt/format.h"
int main(int argc, char** argv) {
for(int i = 0; i < argc; ++i)
fmt::print("{}: {}\n", i, argv[i]);
}

View File

@ -25,7 +25,7 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "format.h"
#include "fmt/format.h"
#include "gtest/gtest.h"
#if GTEST_HAS_DEATH_TEST

View File

@ -4,24 +4,49 @@ cmake_minimum_required(VERSION 2.8)
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../..)
set(CMAKE_REQUIRED_FLAGS ${CPP11_FLAG})
function (expect_compile_error code)
check_cxx_source_compiles("
#include \"format.cc\"
#include \"posix.h\"
function (generate_source result fragment)
set(${result} "
#define FMT_HEADER_ONLY 1
#include \"fmt/posix.h\"
int main() {
${code}
${fragment}
}
" compiles)
set (does_compile ${compiles})
" PARENT_SCOPE)
endfunction ()
function (expect_compile code)
generate_source(source "${code}")
check_cxx_source_compiles("${source}" compiles)
if (NOT compiles)
set(error_msg "Compile error for: ${code}")
endif ()
# Unset the CMake cache variable compiles. Otherwise the compile test will
# just use cached information next time it runs.
unset(compiles CACHE)
if (does_compile)
message(FATAL_ERROR "No compile error for: ${code}")
if (error_msg)
message(FATAL_ERROR ${error_msg})
endif ()
endfunction ()
function (expect_compile_error code)
generate_source(source "${code}")
check_cxx_source_compiles("${source}" compiles)
if (compiles)
set(error_msg "No compile error for: ${code}")
endif ()
# Unset the CMake cache variable compiles. Otherwise the compile test will
# just use cached information next time it runs.
unset(compiles CACHE)
if (error_msg)
message(FATAL_ERROR ${error_msg})
endif ()
endfunction ()
# check if the source file skeleton compiles
expect_compile("")
# MakeArg doesn't accept [const] volatile char *.
expect_compile_error("volatile char s[] = \"test\"; (fmt::internal::MakeArg<char>)(s);")
expect_compile_error("const volatile char s[] = \"test\"; (fmt::internal::MakeArg<char>)(s);")
@ -38,4 +63,16 @@ expect_compile_error("fmt::MemoryWriter() << fmt::pad(42, 5, L' ');")
# Formatting a wide character with a narrow format string is forbidden.
expect_compile_error("fmt::format(\"{}\", L'a';")
expect_compile("FMT_STATIC_ASSERT(true, \"this should never happen\");")
expect_compile_error("FMT_STATIC_ASSERT(0 > 1, \"oops\");")
# Make sure that compiler features detected in the header
# match the features detected in CMake.
if (SUPPORTS_USER_DEFINED_LITERALS)
set(supports_udl 1)
else ()
set(supports_udl 0)
endif ()
expect_compile("#if FMT_USE_USER_DEFINED_LITERALS != ${supports_udl}
# error
#endif")

94
test/container-test.cc Normal file
View File

@ -0,0 +1,94 @@
/*
Tests of container utilities
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "fmt/container.h"
#include "gtest/gtest.h"
using fmt::internal::ContainerBuffer;
TEST(ContainerBufferTest, Empty) {
std::string data;
ContainerBuffer<std::string> buffer(data);
EXPECT_EQ(0u, buffer.size());
EXPECT_EQ(0u, buffer.capacity());
}
TEST(ContainerBufferTest, Reserve) {
std::string data;
ContainerBuffer<std::string> buffer(data);
std::size_t capacity = std::string().capacity() + 10;
buffer.reserve(capacity);
EXPECT_EQ(0u, buffer.size());
EXPECT_EQ(capacity, buffer.capacity());
}
TEST(ContainerBufferTest, Resize) {
std::string data;
ContainerBuffer<std::string> buffer(data);
std::size_t size = std::string().capacity() + 10;
buffer.resize(size);
EXPECT_EQ(size, buffer.size());
EXPECT_EQ(size, buffer.capacity());
}
TEST(ContainerBufferTest, Append) {
std::string data("Why so");
const std::string serious(" serious");
ContainerBuffer<std::string> buffer(data);
buffer.append(serious.c_str(), serious.c_str() + serious.length());
EXPECT_EQ("Why so serious", data);
EXPECT_EQ(data.length(), buffer.size());
}
TEST(BasicContainerWriterTest, String) {
std::string data;
fmt::BasicContainerWriter<std::string> out(data);
out << "The answer is " << 42 << "\n";
EXPECT_EQ("The answer is 42\n", data);
EXPECT_EQ(17u, out.size());
}
TEST(BasicContainerWriterTest, WString) {
std::wstring data;
fmt::BasicContainerWriter<std::wstring> out(data);
out << "The answer is " << 42 << "\n";
EXPECT_EQ(L"The answer is 42\n", data);
EXPECT_EQ(17u, out.size());
}
TEST(BasicContainerWriterTest, Vector) {
std::vector<char> data;
fmt::BasicContainerWriter<std::vector<char> > out(data);
out << "The answer is " << 42 << "\n";
EXPECT_EQ(17u, data.size());
EXPECT_EQ(out.size(), data.size());
}
TEST(BasicContainerWriterTest, StringAppend) {
std::string data("The");
fmt::BasicContainerWriter<std::string> out(data);
EXPECT_EQ(3u, data.size());
EXPECT_EQ(3u, out.size());
out << " answer is " << 42 << "\n";
EXPECT_EQ("The answer is 42\n", data);
EXPECT_EQ(17u, out.size());
}
TEST(BasicContainerWriterTest, VectorAppend) {
std::vector<char> data;
data.push_back('T');
data.push_back('h');
data.push_back('e');
fmt::BasicContainerWriter<std::vector<char> > out(data);
EXPECT_EQ(3u, data.size());
EXPECT_EQ(3u, out.size());
out << " answer is " << 42 << "\n";
EXPECT_EQ(17u, data.size());
EXPECT_EQ(17u, out.size());
}

View File

@ -0,0 +1,68 @@
/*
Custom argument formatter tests
Copyright (c) 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "fmt/printf.h"
#include "gtest-extra.h"
using fmt::BasicPrintfArgFormatter;
// A custom argument formatter that doesn't print `-` for floating-point values
// rounded to 0.
class CustomArgFormatter
: public fmt::BasicArgFormatter<CustomArgFormatter, char> {
public:
CustomArgFormatter(fmt::BasicFormatter<char, CustomArgFormatter> &f,
fmt::FormatSpec &s, const char *fmt)
: fmt::BasicArgFormatter<CustomArgFormatter, char>(f, s, fmt) {}
void visit_double(double value) {
if (round(value * pow(10, spec().precision())) == 0)
value = 0;
fmt::BasicArgFormatter<CustomArgFormatter, char>::visit_double(value);
}
};
// A custom argument formatter that doesn't print `-` for floating-point values
// rounded to 0.
class CustomPrintfArgFormatter :
public BasicPrintfArgFormatter<CustomPrintfArgFormatter, char> {
public:
typedef BasicPrintfArgFormatter<CustomPrintfArgFormatter, char> Base;
CustomPrintfArgFormatter(fmt::BasicWriter<char> &w, fmt::FormatSpec &spec)
: Base(w, spec) {}
void visit_double(double value) {
if (round(value * pow(10, spec().precision())) == 0)
value = 0;
Base::visit_double(value);
}
};
std::string custom_format(const char *format_str, fmt::ArgList args) {
fmt::MemoryWriter writer;
// Pass custom argument formatter as a template arg to BasicFormatter.
fmt::BasicFormatter<char, CustomArgFormatter> formatter(args, writer);
formatter.format(format_str);
return writer.str();
}
FMT_VARIADIC(std::string, custom_format, const char *)
std::string custom_sprintf(const char* format_str, fmt::ArgList args){
fmt::MemoryWriter writer;
fmt::PrintfFormatter<char, CustomPrintfArgFormatter> formatter(args, writer);
formatter.format(format_str);
return writer.str();
}
FMT_VARIADIC(std::string, custom_sprintf, const char*);
TEST(CustomFormatterTest, Format) {
EXPECT_EQ("0.00", custom_format("{:.2f}", -.00001));
EXPECT_EQ("0.00", custom_sprintf("%.2f", -.00001));
}

View File

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 2.8.12)
project(fmt-test)
find_package(FMT REQUIRED)
add_executable(library-test main.cc)
target_link_libraries(library-test fmt::fmt)
if (TARGET fmt::fmt-header-only)
add_executable(header-only-test main.cc)
target_link_libraries(header-only-test fmt::fmt-header-only)
endif ()

View File

@ -0,0 +1,6 @@
#include "fmt/format.h"
int main(int argc, char** argv) {
for(int i = 0; i < argc; ++i)
fmt::print("{}: {}\n", i, argv[i]);
}

View File

@ -26,16 +26,21 @@
*/
#define FMT_NOEXCEPT
#undef FMT_SHARED
#include "test-assert.h"
// Include format.cc instead of format.h to test implementation-specific stuff.
#include "format.cc"
// Include *.cc instead of *.h to test implementation-specific stuff.
#include "fmt/format.cc"
#include "fmt/printf.cc"
#include <algorithm>
#include <cstring>
#include "gmock/gmock.h"
#include "gtest-extra.h"
#include "util.h"
#undef min
#undef max
TEST(FormatTest, ArgConverter) {
@ -43,7 +48,7 @@ TEST(FormatTest, ArgConverter) {
Arg arg = Arg();
arg.type = Arg::LONG_LONG;
arg.long_long_value = std::numeric_limits<fmt::LongLong>::max();
fmt::ArgConverter<fmt::LongLong>(arg, 'd').visit(arg);
fmt::internal::ArgConverter<fmt::LongLong>(arg, 'd').visit(arg);
EXPECT_EQ(Arg::LONG_LONG, arg.type);
}
@ -59,7 +64,8 @@ TEST(FormatTest, StrError) {
char *message = 0;
char buffer[BUFFER_SIZE];
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = 0, 0), "invalid buffer");
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = buffer, 0), "invalid buffer");
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = buffer, 0),
"invalid buffer");
buffer[0] = 'x';
#if defined(_GNU_SOURCE) && !defined(__COVERITY__)
// Use invalid error code to make sure that safe_strerror returns an error
@ -101,13 +107,21 @@ TEST(FormatTest, FormatErrorCode) {
fmt::format_error_code(w, 42, prefix);
EXPECT_EQ(msg, w.str());
}
{
int codes[] = {42, -1};
for (std::size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
// Test maximum buffer size.
msg = fmt::format("error {}", codes[i]);
fmt::MemoryWriter w;
std::string prefix(
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size(), 'x');
fmt::format_error_code(w, 42, prefix);
fmt::format_error_code(w, codes[i], prefix);
EXPECT_EQ(prefix + sep + msg, w.str());
std::size_t size = fmt::internal::INLINE_BUFFER_SIZE;
EXPECT_EQ(size, w.size());
w.clear();
// Test with a message that doesn't fit into the buffer.
prefix += 'x';
fmt::format_error_code(w, codes[i], prefix);
EXPECT_EQ(msg, w.str());
}
}

View File

@ -28,12 +28,10 @@
#include <cctype>
#include <cfloat>
#include <climits>
#include <clocale>
#include <cmath>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <memory>
#include <sstream>
#include <stdint.h>
#if FMT_USE_TYPE_TRAITS
@ -45,7 +43,24 @@
// Test that the library compiles if None is defined to 0 as done by xlib.h.
#define None 0
#include "format.h"
struct LocaleMock {
static LocaleMock *instance;
MOCK_METHOD0(localeconv, lconv *());
} *LocaleMock::instance;
namespace fmt {
namespace std {
using namespace ::std;
lconv *localeconv() {
return LocaleMock::instance ?
LocaleMock::instance->localeconv() : ::std::localeconv();
}
}
}
#include "fmt/format.h"
#include "util.h"
#include "mock-allocator.h"
#include "gtest-extra.h"
@ -235,7 +250,7 @@ TEST(WriterTest, Allocator) {
std::size_t size =
static_cast<std::size_t>(1.5 * fmt::internal::INLINE_BUFFER_SIZE);
std::vector<char> mem(size);
EXPECT_CALL(alloc, allocate(size)).WillOnce(testing::Return(&mem[0]));
EXPECT_CALL(alloc, allocate(size, 0)).WillOnce(testing::Return(&mem[0]));
for (int i = 0; i < fmt::internal::INLINE_BUFFER_SIZE + 1; ++i)
w << '*';
EXPECT_CALL(alloc, deallocate(&mem[0], size));
@ -383,30 +398,10 @@ TEST(WriterTest, hexu) {
EXPECT_EQ("DEADBEEF", (MemoryWriter() << hexu(0xdeadbeefull)).str());
}
class Date {
int year_, month_, day_;
public:
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
int year() const { return year_; }
int month() const { return month_; }
int day() const { return day_; }
friend std::ostream &operator<<(std::ostream &os, const Date &d) {
os << d.year_ << '-' << d.month_ << '-' << d.day_;
return os;
}
friend std::wostream &operator<<(std::wostream &os, const Date &d) {
os << d.year_ << L'-' << d.month_ << L'-' << d.day_;
return os;
}
template <typename Char>
friend BasicWriter<Char> &operator<<(BasicWriter<Char> &f, const Date &d) {
return f << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};
template <typename Char>
BasicWriter<Char> &operator<<(BasicWriter<Char> &f, const Date &d) {
return f << d.year() << '-' << d.month() << '-' << d.day();
}
class ISO8601DateFormatter {
const Date *date_;
@ -662,7 +657,6 @@ TEST(FormatterTest, LeftAlign) {
EXPECT_EQ("c ", format("{0:<5}", 'c'));
EXPECT_EQ("abc ", format("{0:<5}", "abc"));
EXPECT_EQ("0xface ", format("{0:<8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
}
TEST(FormatterTest, RightAlign) {
@ -680,7 +674,6 @@ TEST(FormatterTest, RightAlign) {
EXPECT_EQ(" c", format("{0:>5}", 'c'));
EXPECT_EQ(" abc", format("{0:>5}", "abc"));
EXPECT_EQ(" 0xface", format("{0:>8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
}
TEST(FormatterTest, NumericAlign) {
@ -706,8 +699,6 @@ TEST(FormatterTest, NumericAlign) {
FormatError, "format specifier '=' requires numeric argument");
EXPECT_THROW_MSG(format("{0:=8}", reinterpret_cast<void*>(0xface)),
FormatError, "format specifier '=' requires numeric argument");
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")),
FormatError, "format specifier '=' requires numeric argument");
}
TEST(FormatterTest, CenterAlign) {
@ -725,7 +716,6 @@ TEST(FormatterTest, CenterAlign) {
EXPECT_EQ(" c ", format("{0:^5}", 'c'));
EXPECT_EQ(" abc ", format("{0:^6}", "abc"));
EXPECT_EQ(" 0xface ", format("{0:^8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
}
TEST(FormatterTest, Fill) {
@ -745,7 +735,6 @@ TEST(FormatterTest, Fill) {
EXPECT_EQ("c****", format("{0:*<5}", 'c'));
EXPECT_EQ("abc**", format("{0:*<5}", "abc"));
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
}
TEST(FormatterTest, PlusSign) {
@ -770,8 +759,6 @@ TEST(FormatterTest, PlusSign) {
FormatError, "format specifier '+' requires numeric argument");
EXPECT_THROW_MSG(format("{0:+}", reinterpret_cast<void*>(0x42)),
FormatError, "format specifier '+' requires numeric argument");
EXPECT_THROW_MSG(format("{0:+}", TestString()),
FormatError, "format specifier '+' requires numeric argument");
}
TEST(FormatterTest, MinusSign) {
@ -796,8 +783,6 @@ TEST(FormatterTest, MinusSign) {
FormatError, "format specifier '-' requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", reinterpret_cast<void*>(0x42)),
FormatError, "format specifier '-' requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", TestString()),
FormatError, "format specifier '-' requires numeric argument");
}
TEST(FormatterTest, SpaceSign) {
@ -822,8 +807,6 @@ TEST(FormatterTest, SpaceSign) {
FormatError, "format specifier ' ' requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", reinterpret_cast<void*>(0x42)),
FormatError, "format specifier ' ' requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", TestString()),
FormatError, "format specifier ' ' requires numeric argument");
}
TEST(FormatterTest, HashFlag) {
@ -869,8 +852,6 @@ TEST(FormatterTest, HashFlag) {
FormatError, "format specifier '#' requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", reinterpret_cast<void*>(0x42)),
FormatError, "format specifier '#' requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", TestString()),
FormatError, "format specifier '#' requires numeric argument");
}
TEST(FormatterTest, ZeroFlag) {
@ -891,8 +872,6 @@ TEST(FormatterTest, ZeroFlag) {
FormatError, "format specifier '0' requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", reinterpret_cast<void*>(0x42)),
FormatError, "format specifier '0' requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", TestString()),
FormatError, "format specifier '0' requires numeric argument");
}
TEST(FormatterTest, Width) {
@ -920,7 +899,6 @@ TEST(FormatterTest, Width) {
EXPECT_EQ(" 0xcafe", format("{0:10}", reinterpret_cast<void*>(0xcafe)));
EXPECT_EQ("x ", format("{0:11}", 'x'));
EXPECT_EQ("str ", format("{0:12}", "str"));
EXPECT_EQ("test ", format("{0:13}", TestString("test")));
}
TEST(FormatterTest, RuntimeWidth) {
@ -954,7 +932,7 @@ TEST(FormatterTest, RuntimeWidth) {
FormatError, "number is too big");
EXPECT_THROW_MSG(format("{0:{1}}", 0, -1l),
FormatError, "negative width");
if (fmt::internal::check(sizeof(long) > sizeof(int))) {
if (fmt::internal::const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX;
EXPECT_THROW_MSG(format("{0:{1}}", 0, (value + 1)),
FormatError, "number is too big");
@ -979,7 +957,6 @@ TEST(FormatterTest, RuntimeWidth) {
format("{0:{1}}", reinterpret_cast<void*>(0xcafe), 10));
EXPECT_EQ("x ", format("{0:{1}}", 'x', 11));
EXPECT_EQ("str ", format("{0:{1}}", "str", 12));
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));
}
TEST(FormatterTest, Precision) {
@ -1039,7 +1016,6 @@ TEST(FormatterTest, Precision) {
FormatError, "precision not allowed in pointer format specifier");
EXPECT_EQ("st", format("{0:.2}", "str"));
EXPECT_EQ("te", format("{0:.2}", TestString("test")));
}
TEST(FormatterTest, RuntimePrecision) {
@ -1075,7 +1051,7 @@ TEST(FormatterTest, RuntimePrecision) {
FormatError, "number is too big");
EXPECT_THROW_MSG(format("{0:.{1}}", 0, -1l),
FormatError, "negative precision");
if (fmt::internal::check(sizeof(long) > sizeof(int))) {
if (fmt::internal::const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX;
EXPECT_THROW_MSG(format("{0:.{1}}", 0, (value + 1)),
FormatError, "number is too big");
@ -1123,7 +1099,6 @@ TEST(FormatterTest, RuntimePrecision) {
FormatError, "precision not allowed in pointer format specifier");
EXPECT_EQ("st", format("{0:.{1}}", "str", 2));
EXPECT_EQ("te", format("{0:.{1}}", TestString("test"), 2));
}
template <typename T>
@ -1164,7 +1139,7 @@ TEST(FormatterTest, FormatShort) {
TEST(FormatterTest, FormatInt) {
EXPECT_THROW_MSG(format("{0:v", 42),
FormatError, "missing '}' in format string");
check_unknown_types(42, "bBdoxX", "integer");
check_unknown_types(42, "bBdoxXn", "integer");
}
TEST(FormatterTest, FormatBin) {
@ -1248,6 +1223,27 @@ TEST(FormatterTest, FormatOct) {
EXPECT_EQ(buffer, format("{0:o}", ULONG_MAX));
}
TEST(FormatterTest, FormatIntLocale) {
ScopedMock<LocaleMock> mock;
lconv lc = lconv();
char sep[] = "--";
lc.thousands_sep = sep;
EXPECT_CALL(mock, localeconv()).Times(3).WillRepeatedly(testing::Return(&lc));
EXPECT_EQ("123", format("{:n}", 123));
EXPECT_EQ("1--234", format("{:n}", 1234));
EXPECT_EQ("1--234--567", format("{:n}", 1234567));
}
struct ConvertibleToLongLong {
operator fmt::LongLong() const {
return fmt::LongLong(1) << 32;
}
};
TEST(FormatterTest, FormatConvertibleToLongLong) {
EXPECT_EQ("100000000", format("{:x}", ConvertibleToLongLong()));
}
TEST(FormatterTest, FormatFloat) {
EXPECT_EQ("392.500000", format("{0:f}", 392.5f));
}
@ -1311,7 +1307,7 @@ TEST(FormatterTest, FormatLongDouble) {
}
TEST(FormatterTest, FormatChar) {
const char types[] = "cbBdoxX";
const char types[] = "cbBdoxXn";
check_unknown_types('a', types, "char");
EXPECT_EQ("a", format("{0}", 'a'));
EXPECT_EQ("z", format("{0:c}", 'z'));
@ -1357,6 +1353,8 @@ TEST(FormatterTest, FormatUCharString) {
EXPECT_EQ("test", format("{0:s}", str));
const unsigned char *const_str = str;
EXPECT_EQ("test", format("{0:s}", const_str));
unsigned char *ptr = str;
EXPECT_EQ("test", format("{0:s}", ptr));
}
TEST(FormatterTest, FormatPointer) {
@ -1380,20 +1378,20 @@ TEST(FormatterTest, FormatCStringRef) {
EXPECT_EQ("test", format("{0}", CStringRef("test")));
}
TEST(FormatterTest, FormatUsingIOStreams) {
EXPECT_EQ("a string", format("{0}", TestString("a string")));
std::string s = format("The date is {0}", Date(2012, 12, 9));
EXPECT_EQ("The date is 2012-12-9", s);
void format_arg(fmt::BasicFormatter<char> &f, const char *, const Date &d) {
f.writer() << d.year() << '-' << d.month() << '-' << d.day();
}
TEST(FormatterTest, FormatCustom) {
Date date(2012, 12, 9);
check_unknown_types(date, "s", "string");
EXPECT_EQ(L"The date is 2012-12-9",
format(L"The date is {0}", Date(2012, 12, 9)));
EXPECT_THROW_MSG(fmt::format("{:s}", date), FormatError,
"unmatched '}' in format string");
}
class Answer {};
template <typename Char>
void format(fmt::BasicFormatter<Char> &f, const Char *, Answer) {
void format_arg(fmt::BasicFormatter<Char> &f, const Char *, Answer) {
f.writer() << "42";
}
@ -1550,9 +1548,6 @@ TEST(FormatTest, Print) {
EXPECT_WRITE(stderr,
fmt::print(stderr, "Don't {}!", "panic"), "Don't panic!");
#endif
std::ostringstream os;
fmt::print(os, "Don't {}!", "panic");
EXPECT_EQ("Don't panic!", os.str());
}
#if FMT_USE_FILE_DESCRIPTORS
@ -1567,6 +1562,27 @@ TEST(FormatTest, Variadic) {
EXPECT_EQ(L"abc1", format(L"{}c{}", L"ab", 1));
}
TEST(FormatTest, JoinArg) {
using fmt::join;
int v1[3] = { 1, 2, 3 };
std::vector<float> v2;
v2.push_back(1.2f);
v2.push_back(3.4f);
EXPECT_EQ("(1, 2, 3)", format("({})", join(v1 + 0, v1 + 3, ", ")));
EXPECT_EQ("(1)", format("({})", join(v1 + 0, v1 + 1, ", ")));
EXPECT_EQ("()", format("({})", join(v1 + 0, v1 + 0, ", ")));
EXPECT_EQ("(001, 002, 003)", format("({:03})", join(v1 + 0, v1 + 3, ", ")));
EXPECT_EQ("(+01.20, +03.40)", format("({:+06.2f})", join(v2.begin(), v2.end(), ", ")));
EXPECT_EQ(L"(1, 2, 3)", format(L"({})", join(v1 + 0, v1 + 3, L", ")));
#if FMT_HAS_GXX_CXX11
EXPECT_EQ("(1, 2, 3)", format("({})", join(v1, ", ")));
EXPECT_EQ("(+01.20, +03.40)", format("({:+06.2f})", join(v2, ", ")));
#endif
}
template <typename T>
std::string str(const T &value) {
return fmt::format("{}", value);
@ -1638,23 +1654,40 @@ TEST(LiteralsTest, NamedArg) {
}
#endif // FMT_USE_USER_DEFINED_LITERALS
enum TestEnum {};
std::ostream &operator<<(std::ostream &os, TestEnum) {
return os << "TestEnum";
}
enum TestEnum2 { A };
enum TestEnum { A };
TEST(FormatTest, Enum) {
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
EXPECT_EQ("0", fmt::format("{}", A));
}
struct EmptyTest {};
std::ostream &operator<<(std::ostream &os, EmptyTest) {
return os << "";
class MockArgFormatter :
public fmt::internal::ArgFormatterBase<MockArgFormatter, char> {
public:
typedef fmt::internal::ArgFormatterBase<MockArgFormatter, char> Base;
MockArgFormatter(fmt::BasicFormatter<char, MockArgFormatter> &f,
fmt::FormatSpec &s, const char *)
: fmt::internal::ArgFormatterBase<MockArgFormatter, char>(f.writer(), s) {
EXPECT_CALL(*this, visit_int(42));
}
MOCK_METHOD1(visit_int, void (int value));
};
void custom_format(const char *format_str, fmt::ArgList args) {
fmt::MemoryWriter writer;
fmt::BasicFormatter<char, MockArgFormatter> formatter(args, writer);
formatter.format(format_str);
}
FMT_VARIADIC(void, custom_format, const char *)
TEST(FormatTest, CustomArgFormatter) {
custom_format("{}", 42);
}
TEST(FormatTest, EmptyCustomOutput) {
EXPECT_EQ("", fmt::format("{}", EmptyTest()));
void convert(int);
// Check if there is no collision with convert function in the global namespace.
TEST(FormatTest, ConvertCollision) {
fmt::format("{}", 42);
}

View File

@ -36,6 +36,10 @@
#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_H_
#define GMOCK_INCLUDE_GMOCK_GMOCK_H_
#ifdef __clang__
# pragma clang diagnostic ignored "-Wc99-extensions"
#endif
// This file implements the following syntax:
//
// ON_CALL(mock_object.Method(...))
@ -10086,8 +10090,9 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
// threads concurrently.
Result InvokeWith(const ArgumentTuple& args)
GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
return static_cast<const ResultHolder*>(
this->UntypedInvokeWith(&args))->GetValueAndDelete();
const ResultHolder *rh = static_cast<const ResultHolder*>(
this->UntypedInvokeWith(&args));
return rh ? rh->GetValueAndDelete() : Result();
}
// Adds and returns a default action spec for this mock function.

View File

@ -46,7 +46,7 @@ namespace {
std::string sanitize(const std::string &s) {
std::string result;
for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i)
result.push_back(*i & 0xff);
result.push_back(static_cast<char>(*i & 0xff));
return result;
}
@ -320,7 +320,7 @@ TEST(StreamingAssertionsTest, EXPECT_WRITE) {
TEST(UtilTest, FormatSystemError) {
fmt::MemoryWriter out;
fmt::internal::format_system_error(out, EDOM, "test message");
fmt::format_system_error(out, EDOM, "test message");
EXPECT_EQ(out.str(), format_system_error(EDOM, "test message"));
}

View File

@ -80,10 +80,10 @@ std::string OutputRedirect::restore_and_read() {
return content; // Already read.
enum { BUFFER_SIZE = 4096 };
char buffer[BUFFER_SIZE];
std::streamsize count = 0;
std::size_t count = 0;
do {
count = read_end_.read(buffer, BUFFER_SIZE);
content.append(buffer, static_cast<std::size_t>(count));
content.append(buffer, count);
} while (count != 0);
read_end_.close();
return content;
@ -91,8 +91,7 @@ std::string OutputRedirect::restore_and_read() {
std::string read(File &f, std::size_t count) {
std::string buffer(count, '\0');
std::streamsize n = 0;
std::size_t offset = 0;
std::size_t n = 0, offset = 0;
do {
n = f.read(&buffer[offset], count - offset);
// We can't read more than size_t bytes since count has type size_t.
@ -106,6 +105,6 @@ std::string read(File &f, std::size_t count) {
std::string format_system_error(int error_code, fmt::StringRef message) {
fmt::MemoryWriter out;
fmt::internal::format_system_error(out, error_code, message);
fmt::format_system_error(out, error_code, message);
return out.str();
}

View File

@ -29,16 +29,16 @@
#define FMT_GTEST_EXTRA_H_
#include <string>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "format.h"
#include "fmt/format.h"
#ifndef FMT_USE_FILE_DESCRIPTORS
# define FMT_USE_FILE_DESCRIPTORS 0
#endif
#if FMT_USE_FILE_DESCRIPTORS
# include "posix.h"
# include "fmt/posix.h"
#endif
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
@ -172,4 +172,10 @@ std::string read(fmt::File &f, std::size_t count);
#endif // FMT_USE_FILE_DESCRIPTORS
template <typename Mock>
struct ScopedMock : testing::StrictMock<Mock> {
ScopedMock() { Mock::instance = this; }
~ScopedMock() { Mock::instance = 0; }
};
#endif // FMT_GTEST_EXTRA_H_

View File

@ -2823,7 +2823,11 @@ inline int IsATTY(int /* fd */) { return 0; }
inline int IsATTY(int fd) { return _isatty(fd); }
# endif // GTEST_OS_WINDOWS_MOBILE
inline int StrCaseCmp(const char* s1, const char* s2) {
return _stricmp(s1, s2);
# if _EMULATE_GLIBC
return strcasecmp(s1, s2);
# else
return _stricmp(s1, s2);
# endif
}
inline char* StrDup(const char* src) { return _strdup(src); }
# endif // __BORLANDC__

View File

@ -1,28 +1,3 @@
/*
Header-only configuration test
// Header-only configuration test
Copyright (c) 2014, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "format.h"
#include "fmt/format.h"

View File

@ -1,28 +1,3 @@
/*
Additional translation unit for the header-only configuration test
// Additional translation unit for the header-only configuration test
Copyright (c) 2014, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "format.h"
#include "fmt/format.h"

View File

@ -29,7 +29,7 @@
#include <gtest/gtest.h>
#define FMT_USE_VARIADIC_TEMPLATES 0
#include "format.h"
#include "fmt/format.h"
#define IDENTITY(x) x
@ -67,7 +67,7 @@ TEST(UtilTest, NArg) {
int result;
#define MAKE_TEST(func) \
void func(const char *format, const fmt::ArgList &args) { \
void func(const char *, const fmt::ArgList &args) { \
result = 0; \
for (unsigned i = 0; args[i].type; ++i) \
result += args[i].int_value; \

View File

@ -36,8 +36,8 @@ class MockAllocator {
MockAllocator() {}
MockAllocator(const MockAllocator &) {}
typedef T value_type;
MOCK_METHOD1_T(allocate, T* (std::size_t n));
MOCK_METHOD2_T(deallocate, void (T* p, std::size_t n));
MOCK_METHOD2_T(allocate, T *(std::size_t n, const T *h));
MOCK_METHOD2_T(deallocate, void (T *p, std::size_t n));
};
template <typename Allocator>
@ -78,8 +78,10 @@ class AllocatorRef {
Allocator *get() const { return alloc_; }
value_type* allocate(std::size_t n) { return alloc_->allocate(n); }
void deallocate(value_type* p, std::size_t n) { alloc_->deallocate(p, n); }
value_type *allocate(std::size_t n, const value_type *h) {
return alloc_->allocate(n, h);
}
void deallocate(value_type *p, std::size_t n) { alloc_->deallocate(p, n); }
};
#endif // FMT_MOCK_ALLOCATOR_H_

174
test/ostream-test.cc Normal file
View File

@ -0,0 +1,174 @@
/*
std::ostream support tests
Copyright (c) 2012-2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmt/ostream.h"
#include <sstream>
#include "gmock/gmock.h"
#include "gtest-extra.h"
#include "util.h"
using fmt::format;
using fmt::FormatError;
std::ostream &operator<<(std::ostream &os, const Date &d) {
os << d.year() << '-' << d.month() << '-' << d.day();
return os;
}
std::wostream &operator<<(std::wostream &os, const Date &d) {
os << d.year() << L'-' << d.month() << L'-' << d.day();
return os;
}
enum TestEnum {};
std::ostream &operator<<(std::ostream &os, TestEnum) {
return os << "TestEnum";
}
enum TestEnum2 {A};
TEST(OStreamTest, Enum) {
EXPECT_FALSE(fmt::internal::ConvertToInt<TestEnum>::value);
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
EXPECT_EQ("0", fmt::format("{}", A));
}
struct TestArgFormatter : fmt::BasicArgFormatter<TestArgFormatter, char> {
TestArgFormatter(fmt::BasicFormatter<char, TestArgFormatter> &f,
fmt::FormatSpec &s, const char *fmt)
: fmt::BasicArgFormatter<TestArgFormatter, char>(f, s, fmt) {}
};
TEST(OStreamTest, CustomArg) {
fmt::MemoryWriter writer;
typedef fmt::BasicFormatter<char, TestArgFormatter> Formatter;
Formatter formatter(fmt::ArgList(), writer);
fmt::FormatSpec spec;
TestArgFormatter af(formatter, spec, "}");
af.visit(fmt::internal::MakeArg<Formatter>(TestEnum()));
EXPECT_EQ("TestEnum", writer.str());
}
TEST(OStreamTest, Format) {
EXPECT_EQ("a string", format("{0}", TestString("a string")));
std::string s = format("The date is {0}", Date(2012, 12, 9));
EXPECT_EQ("The date is 2012-12-9", s);
Date date(2012, 12, 9);
EXPECT_EQ(L"The date is 2012-12-9",
format(L"The date is {0}", Date(2012, 12, 9)));
}
TEST(OStreamTest, FormatSpecs) {
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")),
FormatError, "format specifier '=' requires numeric argument");
EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:+}", TestString()),
FormatError, "format specifier '+' requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", TestString()),
FormatError, "format specifier '-' requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", TestString()),
FormatError, "format specifier ' ' requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", TestString()),
FormatError, "format specifier '#' requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", TestString()),
FormatError, "format specifier '0' requires numeric argument");
EXPECT_EQ("test ", format("{0:13}", TestString("test")));
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));
EXPECT_EQ("te", format("{0:.2}", TestString("test")));
EXPECT_EQ("te", format("{0:.{1}}", TestString("test"), 2));
}
struct EmptyTest {};
std::ostream &operator<<(std::ostream &os, EmptyTest) {
return os << "";
}
TEST(OStreamTest, EmptyCustomOutput) {
EXPECT_EQ("", fmt::format("{}", EmptyTest()));
}
TEST(OStreamTest, Print) {
std::ostringstream os;
fmt::print(os, "Don't {}!", "panic");
EXPECT_EQ("Don't panic!", os.str());
}
TEST(OStreamTest, WriteToOStream) {
std::ostringstream os;
fmt::MemoryWriter w;
w << "foo";
fmt::internal::write(os, w);
EXPECT_EQ("foo", os.str());
}
TEST(OStreamTest, WriteToOStreamMaxSize) {
std::size_t max_size = std::numeric_limits<std::size_t>::max();
std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max();
if (max_size <= fmt::internal::to_unsigned(max_streamsize))
return;
class TestWriter : public fmt::BasicWriter<char> {
private:
struct TestBuffer : fmt::Buffer<char> {
explicit TestBuffer(std::size_t size) { size_ = size; }
void grow(std::size_t) {}
} buffer_;
public:
explicit TestWriter(std::size_t size)
: fmt::BasicWriter<char>(buffer_), buffer_(size) {}
} w(max_size);
struct MockStreamBuf : std::streambuf {
MOCK_METHOD2(xsputn, std::streamsize (const void *s, std::streamsize n));
std::streamsize xsputn(const char *s, std::streamsize n) {
const void *v = s;
return xsputn(v, n);
}
} buffer;
struct TestOStream : std::ostream {
explicit TestOStream(MockStreamBuf &buffer) : std::ostream(&buffer) {}
} os(buffer);
testing::InSequence sequence;
const char *data = 0;
std::size_t size = max_size;
do {
typedef fmt::internal::MakeUnsigned<std::streamsize>::Type UStreamSize;
UStreamSize n = std::min<UStreamSize>(
size, fmt::internal::to_unsigned(max_streamsize));
EXPECT_CALL(buffer, xsputn(data, static_cast<std::streamsize>(n)))
.WillOnce(testing::Return(max_streamsize));
data += n;
size -= static_cast<std::size_t>(n);
} while (size != 0);
fmt::internal::write(os, w);
}

View File

@ -29,7 +29,7 @@
#define _CRT_SECURE_NO_WARNINGS
#include "posix-mock.h"
#include "posix.cc"
#include "fmt/posix.cc"
#include <errno.h>
#include <fcntl.h>
@ -41,6 +41,7 @@
# undef ERROR
#endif
#include "gmock/gmock.h"
#include "gtest-extra.h"
#include "util.h"
@ -49,6 +50,9 @@ using fmt::ErrorCode;
using fmt::File;
using testing::internal::scoped_ptr;
using testing::_;
using testing::StrEq;
using testing::Return;
namespace {
int open_count;
@ -273,8 +277,7 @@ TEST(FileTest, Size) {
write_file("test", content);
File f("test", File::RDONLY);
EXPECT_GE(f.size(), 0);
fmt::ULongLong file_size = f.size();
EXPECT_EQ(content.size(), file_size);
EXPECT_EQ(content.size(), static_cast<fmt::ULongLong>(f.size()));
#ifdef _WIN32
fmt::MemoryWriter message;
fmt::internal::format_windows_error(
@ -304,7 +307,7 @@ TEST(FileTest, ReadRetry) {
write_end.write("test", SIZE);
write_end.close();
char buffer[SIZE];
std::streamsize count = 0;
std::size_t count = 0;
EXPECT_RETRY(count = read_end.read(buffer, SIZE),
read, "cannot read from file");
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
@ -314,7 +317,7 @@ TEST(FileTest, WriteRetry) {
File read_end, write_end;
File::pipe(read_end, write_end);
enum { SIZE = 4 };
std::streamsize count = 0;
std::size_t count = 0;
EXPECT_RETRY(count = write_end.write("test", SIZE),
write, "cannot write to file");
write_end.close();
@ -449,3 +452,103 @@ TEST(BufferedFileTest, FilenoNoRetry) {
EXPECT_EQ(2, fileno_count);
fileno_count = 0;
}
struct TestMock {
static TestMock *instance;
} *TestMock::instance;
TEST(ScopedMock, Scope) {
{
ScopedMock<TestMock> mock;
EXPECT_EQ(&mock, TestMock::instance);
TestMock &copy = mock;
}
EXPECT_EQ(0, TestMock::instance);
}
#ifdef FMT_LOCALE
typedef fmt::Locale::Type LocaleType;
struct LocaleMock {
static LocaleMock *instance;
MOCK_METHOD3(newlocale, LocaleType (int category_mask, const char *locale,
LocaleType base));
MOCK_METHOD1(freelocale, void (LocaleType locale));
MOCK_METHOD3(strtod_l, double (const char *nptr, char **endptr,
LocaleType locale));
} *LocaleMock::instance;
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4273)
_locale_t _create_locale(int category, const char *locale) {
return LocaleMock::instance->newlocale(category, locale, 0);
}
void _free_locale(_locale_t locale) {
LocaleMock::instance->freelocale(locale);
}
double _strtod_l(const char *nptr, char **endptr, _locale_t locale) {
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
}
# pragma warning(pop)
#endif
LocaleType newlocale(int category_mask, const char *locale, LocaleType base) {
return LocaleMock::instance->newlocale(category_mask, locale, base);
}
#if defined(__APPLE__) || defined(__FreeBSD__)
typedef int FreeLocaleResult;
#else
typedef void FreeLocaleResult;
#endif
FreeLocaleResult freelocale(LocaleType locale) {
LocaleMock::instance->freelocale(locale);
return FreeLocaleResult();
}
double strtod_l(const char *nptr, char **endptr, LocaleType locale) {
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
}
TEST(LocaleTest, LocaleMock) {
ScopedMock<LocaleMock> mock;
LocaleType locale = reinterpret_cast<LocaleType>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
newlocale(222, "foo", locale);
}
TEST(LocaleTest, Locale) {
#ifndef LC_NUMERIC_MASK
enum { LC_NUMERIC_MASK = LC_NUMERIC };
#endif
ScopedMock<LocaleMock> mock;
LocaleType impl = reinterpret_cast<LocaleType>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), 0))
.WillOnce(Return(impl));
EXPECT_CALL(mock, freelocale(impl));
fmt::Locale locale;
EXPECT_EQ(impl, locale.get());
}
TEST(LocaleTest, Strtod) {
ScopedMock<LocaleMock> mock;
EXPECT_CALL(mock, newlocale(_, _, _))
.WillOnce(Return(reinterpret_cast<LocaleType>(42)));
EXPECT_CALL(mock, freelocale(_));
fmt::Locale locale;
const char *str = "4.2";
char end = 'x';
EXPECT_CALL(mock, strtod_l(str, _, locale.get()))
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));
EXPECT_EQ(777, locale.strtod(str));
EXPECT_EQ(&end, str);
}
#endif // FMT_LOCALE

View File

@ -25,13 +25,14 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cstdlib> // std::exit
#include <cstring>
#include "fmt/posix.h"
#include "gtest-extra.h"
#include "posix.h"
#include "util.h"
#ifdef __MINGW32__
#ifdef fileno
# undef fileno
#endif
@ -68,7 +69,7 @@ void write(File &f, fmt::StringRef s) {
std::size_t num_chars_left = s.size();
const char *ptr = s.data();
do {
std::streamsize count = f.write(ptr, num_chars_left);
std::size_t count = f.write(ptr, num_chars_left);
ptr += count;
// We can't write more than size_t bytes since num_chars_left
// has type size_t.
@ -231,24 +232,24 @@ TEST(FileTest, MoveAssignmentClosesFile) {
File OpenBufferedFile(int &fd) {
File f = open_file();
fd = f.descriptor();
return std::move(f);
return f;
}
TEST(FileTest, MoveFromTemporaryInCtor) {
int fd = 0xdeadbeef;
int fd = 0xdead;
File f(OpenBufferedFile(fd));
EXPECT_EQ(fd, f.descriptor());
}
TEST(FileTest, MoveFromTemporaryInAssignment) {
int fd = 0xdeadbeef;
int fd = 0xdead;
File f;
f = OpenBufferedFile(fd);
EXPECT_EQ(fd, f.descriptor());
}
TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
int fd = 0xdeadbeef;
int fd = 0xdead;
File f = open_file();
int old_fd = f.descriptor();
f = OpenBufferedFile(fd);
@ -386,3 +387,12 @@ TEST(FileTest, FdopenError) {
EXPECT_SYSTEM_ERROR_NOASSERT(
f.fdopen("r"), EBADF, "cannot associate stream with file descriptor");
}
#ifdef FMT_LOCALE
TEST(LocaleTest, Strtod) {
fmt::Locale locale;
const char *start = "4.2", *ptr = start;
EXPECT_EQ(4.2, locale.strtod(ptr));
EXPECT_EQ(start + 3, ptr);
}
#endif

View File

@ -29,7 +29,8 @@
#include <climits>
#include <cstring>
#include "format.h"
#include "fmt/printf.h"
#include "fmt/format.h"
#include "gtest-extra.h"
#include "util.h"
@ -201,6 +202,8 @@ TEST(PrintfTest, HashFlag) {
TEST(PrintfTest, Width) {
EXPECT_PRINTF(" abc", "%5s", "abc");
EXPECT_PRINTF(" -42", "%5s", "-42");
EXPECT_PRINTF(" 0.123456", "%10s", 0.123456);
// Width cannot be specified twice.
EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), FormatError,
@ -289,24 +292,26 @@ SPECIALIZE_MAKE_SIGNED(unsigned, int);
SPECIALIZE_MAKE_SIGNED(unsigned long, long);
SPECIALIZE_MAKE_SIGNED(fmt::ULongLong, fmt::LongLong);
// Test length format specifier ``length_spec``.
template <typename T, typename U>
void TestLength(const char *length_spec, U value) {
fmt::LongLong signed_value = value;
fmt::ULongLong unsigned_value = value;
fmt::LongLong signed_value = 0;
fmt::ULongLong unsigned_value = 0;
// Apply integer promotion to the argument.
fmt::ULongLong max = std::numeric_limits<U>::max();
using fmt::internal::check;
if (check(max <= static_cast<unsigned>(std::numeric_limits<int>::max()))) {
using std::numeric_limits;
fmt::ULongLong max = numeric_limits<U>::max();
using fmt::internal::const_check;
if (const_check(max <= static_cast<unsigned>(numeric_limits<int>::max()))) {
signed_value = static_cast<int>(value);
unsigned_value = static_cast<int>(value);
} else if (check(max <= std::numeric_limits<unsigned>::max())) {
unsigned_value = static_cast<unsigned>(value);
} else if (const_check(max <= numeric_limits<unsigned>::max())) {
signed_value = static_cast<unsigned>(value);
unsigned_value = static_cast<unsigned>(value);
}
using fmt::internal::MakeUnsigned;
if (sizeof(U) <= sizeof(int) && sizeof(int) < sizeof(T)) {
signed_value = unsigned_value =
static_cast<typename MakeUnsigned<unsigned>::Type>(value);
signed_value = static_cast<fmt::LongLong>(value);
unsigned_value = static_cast<typename MakeUnsigned<unsigned>::Type>(value);
} else {
signed_value = static_cast<typename MakeSigned<T>::Type>(value);
unsigned_value = static_cast<typename MakeUnsigned<T>::Type>(value);
@ -336,12 +341,9 @@ void TestLength(const char *length_spec) {
TestLength<T>(length_spec, -42);
TestLength<T>(length_spec, min);
TestLength<T>(length_spec, max);
using fmt::internal::check;
fmt::LongLong long_long_min = std::numeric_limits<fmt::LongLong>::min();
if (check(min >= 0) || check(static_cast<fmt::LongLong>(min) > long_long_min))
TestLength<T>(length_spec, fmt::LongLong(min) - 1);
TestLength<T>(length_spec, fmt::LongLong(min) - 1);
fmt::ULongLong long_long_max = std::numeric_limits<fmt::LongLong>::max();
if (check(max < 0 || static_cast<fmt::ULongLong>(max) < long_long_max))
if (static_cast<fmt::ULongLong>(max) < long_long_max)
TestLength<T>(length_spec, fmt::LongLong(max) + 1);
TestLength<T>(length_spec, std::numeric_limits<short>::min());
TestLength<T>(length_spec, std::numeric_limits<unsigned short>::max());
@ -381,16 +383,29 @@ TEST(PrintfTest, Bool) {
TEST(PrintfTest, Int) {
EXPECT_PRINTF("-42", "%d", -42);
EXPECT_PRINTF("-42", "%i", -42);
unsigned u = -42;
EXPECT_PRINTF("-42", "%s", -42);
unsigned u = 0 - 42u;
EXPECT_PRINTF(fmt::format("{}", u), "%u", -42);
EXPECT_PRINTF(fmt::format("{:o}", u), "%o", -42);
EXPECT_PRINTF(fmt::format("{:x}", u), "%x", -42);
EXPECT_PRINTF(fmt::format("{:X}", u), "%X", -42);
EXPECT_PRINTF(fmt::format("{}", u), "%s", u);
}
TEST(PrintfTest, LongLong) {
// fmt::printf allows passing long long arguments to %d without length
// specifiers.
fmt::LongLong max = std::numeric_limits<fmt::LongLong>::max();
EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
}
TEST(PrintfTest, Float) {
EXPECT_PRINTF("392.650000", "%f", 392.65);
EXPECT_PRINTF("392.65", "%.2f", 392.65);
EXPECT_PRINTF("392.6", "%.1f", 392.65);
EXPECT_PRINTF("393", "%.f", 392.65);
EXPECT_PRINTF("392.650000", "%F", 392.65);
EXPECT_PRINTF("392.65", "%s", 392.65);
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%e", 392.65);
EXPECT_PRINTF(buffer, "%e", 392.65);
@ -415,6 +430,7 @@ TEST(PrintfTest, Inf) {
TEST(PrintfTest, Char) {
EXPECT_PRINTF("x", "%c", 'x');
EXPECT_PRINTF("x", "%s", 'x');
int max = std::numeric_limits<int>::max();
EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max);
//EXPECT_PRINTF("x", "%lc", L'x');
@ -433,17 +449,17 @@ TEST(PrintfTest, Pointer) {
int n;
void *p = &n;
EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
EXPECT_PRINTF(fmt::format("{}", p), "%s", p);
p = 0;
EXPECT_PRINTF("(nil)", "%p", p);
EXPECT_PRINTF(" (nil)", "%10p", p);
EXPECT_PRINTF("(nil)", "%s", p);
EXPECT_PRINTF(" (nil)", "%10s", p);
const char *s = "test";
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s);
const char *null_str = 0;
EXPECT_PRINTF("(nil)", "%p", null_str);
}
TEST(PrintfTest, Custom) {
EXPECT_PRINTF("abc", "%s", TestString("abc"));
EXPECT_PRINTF("(null)", "%s", null_str);
}
TEST(PrintfTest, Location) {
@ -474,5 +490,22 @@ TEST(PrintfTest, PrintfError) {
#endif
TEST(PrintfTest, WideString) {
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", TestWString(L"abc")));
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
}
TEST(PrintfTest, PrintfCustom) {
EXPECT_EQ("abc", fmt::sprintf("%s", TestString("abc")));
}
TEST(PrintfTest, OStream) {
std::ostringstream os;
int ret = fmt::fprintf(os, "Don't %s!", "panic");
EXPECT_EQ("Don't panic!", os.str());
EXPECT_EQ(12, ret);
}
TEST(PrintfTest, Writer) {
fmt::MemoryWriter writer;
printf(writer, "%d", 42);
EXPECT_EQ("42", writer.str());
}

80
test/string-test.cc Normal file
View File

@ -0,0 +1,80 @@
/*
Tests of string utilities
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "fmt/string.h"
#include "gtest/gtest.h"
using fmt::internal::StringBuffer;
TEST(StringBufferTest, Empty) {
StringBuffer<char> buffer;
EXPECT_EQ(0u, buffer.size());
EXPECT_EQ(0u, buffer.capacity());
std::string data;
// std::string may have initial capacity.
std::size_t capacity = data.capacity();
buffer.move_to(data);
EXPECT_EQ("", data);
EXPECT_EQ(capacity, data.capacity());
}
TEST(StringBufferTest, Reserve) {
StringBuffer<char> buffer;
std::size_t capacity = std::string().capacity() + 10;
buffer.reserve(capacity);
EXPECT_EQ(0u, buffer.size());
EXPECT_EQ(capacity, buffer.capacity());
std::string data;
buffer.move_to(data);
EXPECT_EQ("", data);
}
TEST(StringBufferTest, Resize) {
StringBuffer<char> buffer;
std::size_t size = std::string().capacity() + 10;
buffer.resize(size);
EXPECT_EQ(size, buffer.size());
EXPECT_EQ(size, buffer.capacity());
std::string data;
buffer.move_to(data);
EXPECT_EQ(size, data.size());
}
TEST(StringBufferTest, MoveTo) {
StringBuffer<char> buffer;
std::size_t size = std::string().capacity() + 10;
buffer.resize(size);
const char *p = &buffer[0];
std::string data;
buffer.move_to(data);
EXPECT_EQ(p, &data[0]);
EXPECT_EQ(0u, buffer.size());
EXPECT_EQ(0u, buffer.capacity());
}
TEST(StringWriterTest, MoveTo) {
fmt::StringWriter out;
out << "The answer is " << 42 << "\n";
std::string s;
out.move_to(s);
EXPECT_EQ("The answer is 42\n", s);
EXPECT_EQ(0u, out.size());
}
TEST(StringWriterTest, WString) {
fmt::WStringWriter out;
out << "The answer is " << 42 << "\n";
std::wstring s;
out.move_to(s);
EXPECT_EQ(L"The answer is 42\n", s);
}
TEST(StringTest, ToString) {
EXPECT_EQ("42", fmt::to_string(42));
}

View File

@ -1,5 +1,5 @@
/*
Tests of additional gtest macros.
Test main function.
Copyright (c) 2012-2014, Victor Zverovich
All rights reserved.

60
test/time-test.cc Normal file
View File

@ -0,0 +1,60 @@
/*
Time formatting tests
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifdef WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "gmock/gmock.h"
#include "fmt/time.h"
TEST(TimeTest, Format) {
std::tm tm = std::tm();
tm.tm_year = 116;
tm.tm_mon = 3;
tm.tm_mday = 25;
EXPECT_EQ("The date is 2016-04-25.",
fmt::format("The date is {:%Y-%m-%d}.", tm));
}
TEST(TimeTest, GrowBuffer) {
std::string s = "{:";
for (int i = 0; i < 30; ++i)
s += "%c";
s += "}\n";
std::time_t t = std::time(0);
fmt::format(s, *std::localtime(&t));
}
TEST(TimeTest, EmptyResult) {
EXPECT_EQ("", fmt::format("{}", std::tm()));
}
bool EqualTime(const std::tm &lhs, const std::tm &rhs) {
return lhs.tm_sec == rhs.tm_sec &&
lhs.tm_min == rhs.tm_min &&
lhs.tm_hour == rhs.tm_hour &&
lhs.tm_mday == rhs.tm_mday &&
lhs.tm_mon == rhs.tm_mon &&
lhs.tm_year == rhs.tm_year &&
lhs.tm_wday == rhs.tm_wday &&
lhs.tm_yday == rhs.tm_yday &&
lhs.tm_isdst == rhs.tm_isdst;
}
TEST(TimeTest, LocalTime) {
std::time_t t = std::time(0);
std::tm tm = *std::localtime(&t);
EXPECT_TRUE(EqualTime(tm, fmt::localtime(t)));
}
TEST(TimeTest, GMTime) {
std::time_t t = std::time(0);
std::tm tm = *std::gmtime(&t);
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
}

View File

@ -47,7 +47,7 @@
# include <windows.h>
#endif
#include "format.h"
#include "fmt/format.h"
#undef max
@ -62,16 +62,17 @@ using testing::StrictMock;
namespace {
struct Test {};
template <typename Char>
std::basic_ostream<Char> &operator<<(std::basic_ostream<Char> &os, Test) {
return os << "test";
void format_arg(fmt::BasicFormatter<Char> &f, const Char *, Test) {
f.writer() << "test";
}
template <typename Char, typename T>
Arg make_arg(const T &value) {
Arg arg = fmt::internal::MakeValue<Char>(value);
arg.type = static_cast<Arg::Type>(
fmt::internal::MakeValue<Char>::type(value));
typedef fmt::internal::MakeValue< fmt::BasicFormatter<Char> > MakeValue;
Arg arg = MakeValue(value);
arg.type = static_cast<Arg::Type>(MakeValue::type(value));
return arg;
}
} // namespace
@ -82,8 +83,8 @@ void CheckForwarding(
// Check if value_type is properly defined.
AllocatorRef< MockAllocator<int> >::value_type *ptr = &mem;
// Check forwarding.
EXPECT_CALL(alloc, allocate(42)).WillOnce(Return(ptr));
ref.allocate(42);
EXPECT_CALL(alloc, allocate(42, 0)).WillOnce(Return(ptr));
ref.allocate(42, 0);
EXPECT_CALL(alloc, deallocate(ptr, 42));
ref.deallocate(ptr, 42);
}
@ -332,17 +333,18 @@ TEST(MemoryBufferTest, Grow) {
void grow(std::size_t size) { Base::grow(size); }
} buffer((Allocator(&alloc)));
buffer.resize(7);
using fmt::internal::to_unsigned;
for (int i = 0; i < 7; ++i)
buffer[i] = i * i;
buffer[to_unsigned(i)] = i * i;
EXPECT_EQ(10u, buffer.capacity());
int mem[20];
mem[7] = 0xdead;
EXPECT_CALL(alloc, allocate(20)).WillOnce(Return(mem));
EXPECT_CALL(alloc, allocate(20, 0)).WillOnce(Return(mem));
buffer.grow(20);
EXPECT_EQ(20u, buffer.capacity());
// Check if size elements have been copied
for (int i = 0; i < 7; ++i)
EXPECT_EQ(i * i, buffer[i]);
EXPECT_EQ(i * i, buffer[to_unsigned(i)]);
// and no more than that.
EXPECT_EQ(0xdead, buffer[7]);
EXPECT_CALL(alloc, deallocate(mem, 20));
@ -358,7 +360,7 @@ TEST(MemoryBufferTest, Allocator) {
MemoryBuffer<char, 10, TestAllocator> buffer2((TestAllocator(&alloc)));
EXPECT_EQ(&alloc, buffer2.get_allocator().get());
std::size_t size = 2 * fmt::internal::INLINE_BUFFER_SIZE;
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem));
EXPECT_CALL(alloc, allocate(size, 0)).WillOnce(Return(&mem));
buffer2.reserve(size);
EXPECT_CALL(alloc, deallocate(&mem, size));
}
@ -371,13 +373,13 @@ TEST(MemoryBufferTest, ExceptionInDeallocate) {
std::size_t size = 2 * fmt::internal::INLINE_BUFFER_SIZE;
std::vector<char> mem(size);
{
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem[0]));
EXPECT_CALL(alloc, allocate(size, 0)).WillOnce(Return(&mem[0]));
buffer.resize(size);
std::fill(&buffer[0], &buffer[0] + size, 'x');
}
std::vector<char> mem2(2 * size);
{
EXPECT_CALL(alloc, allocate(2 * size)).WillOnce(Return(&mem2[0]));
EXPECT_CALL(alloc, allocate(2 * size, 0)).WillOnce(Return(&mem2[0]));
std::exception e;
EXPECT_CALL(alloc, deallocate(&mem[0], size)).WillOnce(testing::Throw(e));
EXPECT_THROW(buffer.reserve(2 * size), std::exception);
@ -410,7 +412,7 @@ struct ArgInfo;
template <> \
struct ArgInfo<Arg::type_code> { \
static Type get(const Arg &arg) { return arg.field; } \
};
}
ARG_INFO(INT, int, int_value);
ARG_INFO(UINT, unsigned, uint_value);
@ -575,6 +577,23 @@ TEST(UtilTest, ArgList) {
EXPECT_EQ(Arg::NONE, args[1].type);
}
struct CustomFormatter {
typedef char Char;
};
void format_arg(CustomFormatter &, const char *&s, const Test &) {
s = "custom_format";
}
TEST(UtilTest, MakeValueWithCustomFormatter) {
::Test t;
Arg arg = fmt::internal::MakeValue<CustomFormatter>(t);
CustomFormatter formatter;
const char *s = "";
arg.custom.format(&formatter, &t, &s);
EXPECT_STREQ("custom_format", s);
}
struct Result {
Arg arg;
@ -585,7 +604,7 @@ struct Result {
Result(const wchar_t *s) : arg(make_arg<wchar_t>(s)) {}
};
struct TestVisitor : fmt::internal::ArgVisitor<TestVisitor, Result> {
struct TestVisitor : fmt::ArgVisitor<TestVisitor, Result> {
Result visit_int(int value) { return value; }
Result visit_uint(unsigned value) { return value; }
Result visit_long_long(fmt::LongLong value) { return value; }
@ -594,10 +613,14 @@ struct TestVisitor : fmt::internal::ArgVisitor<TestVisitor, Result> {
Result visit_long_double(long double value) { return value; }
Result visit_char(int value) { return static_cast<char>(value); }
Result visit_cstring(const char *s) { return s; }
Result visit_string(Arg::StringValue<char> s) { return s.value; }
Result visit_wstring(Arg::StringValue<wchar_t> s) { return s.value; }
Result visit_string(fmt::internal::Arg::StringValue<char> s) {
return s.value;
}
Result visit_wstring(fmt::internal::Arg::StringValue<wchar_t> s) {
return s.value;
}
Result visit_pointer(const void *p) { return p; }
Result visit_custom(Arg::CustomValue c) {
Result visit_custom(fmt::internal::Arg::CustomValue c) {
return *static_cast<const ::Test*>(c.value);
}
};
@ -634,7 +657,7 @@ TEST(ArgVisitorTest, VisitAll) {
EXPECT_EQ(&t, result.arg.custom.value);
}
struct TestAnyVisitor : fmt::internal::ArgVisitor<TestAnyVisitor, Result> {
struct TestAnyVisitor : fmt::ArgVisitor<TestAnyVisitor, Result> {
template <typename T>
Result visit_any_int(T value) { return value; }
@ -659,7 +682,7 @@ TEST(ArgVisitorTest, VisitAny) {
}
struct TestUnhandledVisitor :
fmt::internal::ArgVisitor<TestUnhandledVisitor, const char *> {
fmt::ArgVisitor<TestUnhandledVisitor, const char *> {
const char *visit_unhandled_arg() { return "test"; }
};
@ -685,7 +708,7 @@ TEST(ArgVisitorTest, VisitUnhandledArg) {
TEST(ArgVisitorTest, VisitInvalidArg) {
Arg arg = Arg();
arg.type = static_cast<Arg::Type>(Arg::CUSTOM + 1);
arg.type = static_cast<Arg::Type>(Arg::NONE);
EXPECT_ASSERT(TestVisitor().visit(arg), "invalid argument type");
}
@ -811,10 +834,10 @@ void check_throw_error(int error_code, FormatErrorMessage format) {
TEST(UtilTest, FormatSystemError) {
fmt::MemoryWriter message;
fmt::internal::format_system_error(message, EDOM, "test");
fmt::format_system_error(message, EDOM, "test");
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), message.str());
message.clear();
fmt::internal::format_system_error(
fmt::format_system_error(
message, EDOM, fmt::StringRef(0, std::numeric_limits<size_t>::max()));
EXPECT_EQ(fmt::format("error {}", EDOM), message.str());
}
@ -823,12 +846,12 @@ TEST(UtilTest, SystemError) {
fmt::SystemError e(EDOM, "test");
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), e.what());
EXPECT_EQ(EDOM, e.error_code());
check_throw_error<fmt::SystemError>(EDOM, fmt::internal::format_system_error);
check_throw_error<fmt::SystemError>(EDOM, fmt::format_system_error);
}
TEST(UtilTest, ReportSystemError) {
fmt::MemoryWriter out;
fmt::internal::format_system_error(out, EDOM, "test error");
fmt::format_system_error(out, EDOM, "test error");
out << '\n';
EXPECT_WRITE(stderr, fmt::report_system_error(EDOM, "test error"), out.str());
}
@ -855,6 +878,27 @@ TEST(UtilTest, FormatWindowsError) {
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS), actual_message.str());
}
TEST(UtilTest, FormatLongWindowsError) {
LPWSTR message = 0;
// this error code is not available on all Windows platforms and
// Windows SDKs, so do not fail the test if the error string cannot
// be retrieved.
const int provisioning_not_allowed = 0x80284013L /*TBS_E_PROVISIONING_NOT_ALLOWED*/;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
provisioning_not_allowed, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0) == 0) {
return;
}
fmt::internal::UTF16ToUTF8 utf8_message(message);
LocalFree(message);
fmt::MemoryWriter actual_message;
fmt::internal::format_windows_error(
actual_message, provisioning_not_allowed, "test");
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
actual_message.str());
}
TEST(UtilTest, WindowsError) {
check_throw_error<fmt::WindowsError>(
ERROR_FILE_EXISTS, fmt::internal::format_windows_error);
@ -871,14 +915,11 @@ TEST(UtilTest, ReportWindowsError) {
#endif // _WIN32
enum TestEnum2 {};
enum TestEnum3 {};
std::ostream &operator<<(std::ostream &, TestEnum3);
TEST(UtilTest, ConvertToInt) {
EXPECT_TRUE(fmt::internal::ConvertToInt<char>::enable_conversion);
EXPECT_FALSE(fmt::internal::ConvertToInt<const char *>::enable_conversion);
EXPECT_TRUE(fmt::internal::ConvertToInt<TestEnum2>::value);
EXPECT_FALSE(fmt::internal::ConvertToInt<TestEnum3>::value);
}
#if FMT_USE_ENUM_BASE
@ -915,3 +956,17 @@ TEST(UtilTest, Conditional) {
fmt::internal::Conditional<false, int, char>::type *pc = &c;
(void)pc;
}
struct TestLConv {
char *thousands_sep;
};
struct EmptyLConv {};
TEST(UtilTest, ThousandsSep) {
char foo[] = "foo";
TestLConv lc = {foo};
EXPECT_EQ("foo", fmt::internal::thousands_sep(&lc).to_string());
EmptyLConv empty_lc;
EXPECT_EQ("", fmt::internal::thousands_sep(&empty_lc));
}

View File

@ -29,7 +29,7 @@
#include <cstdio>
#include <string>
#include "posix.h"
#include "fmt/posix.h"
enum {BUFFER_SIZE = 256};
@ -78,11 +78,7 @@ class BasicTestString {
public:
explicit BasicTestString(const Char *value = EMPTY) : value_(value) {}
friend std::basic_ostream<Char> &operator<<(
std::basic_ostream<Char> &os, const BasicTestString &s) {
os << s.value_;
return os;
}
const std::basic_string<Char> &value() const { return value_; }
};
template <typename Char>
@ -90,3 +86,20 @@ const Char BasicTestString<Char>::EMPTY[] = {0};
typedef BasicTestString<char> TestString;
typedef BasicTestString<wchar_t> TestWString;
template <typename Char>
std::basic_ostream<Char> &operator<<(
std::basic_ostream<Char> &os, const BasicTestString<Char> &s) {
os << s.value();
return os;
}
class Date {
int year_, month_, day_;
public:
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
int year() const { return year_; }
int month() const { return month_; }
int day() const { return day_; }
};