Compare commits

...

212 Commits
2.0.1 ... 3.0.0

Author SHA1 Message Date
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
66 changed files with 3691 additions and 1870 deletions

2
.gitignore vendored
View File

@ -9,8 +9,8 @@ bin/
*.a
*.so*
*.zip
/*.cmake
cmake_install.cmake
CPack*Config.cmake
CTestTestfile.cmake
CMakeCache.txt
CMakeFiles

View File

@ -1,4 +1,5 @@
language: cpp
sudo: required # the doc target uses sudo to install dependencies
os:
- linux
@ -12,13 +13,21 @@ env:
6pxmyzLHSn1ZR7OX5rfPvwM3tOyZ3H0=
matrix:
- BUILD=Doc
- BUILD=Debug
- BUILD=Release
- BUILD=Debug STANDARD=0x
- BUILD=Release STANDARD=98
- BUILD=Release STANDARD=0x
matrix:
exclude:
- os: osx
env: BUILD=Doc
addons:
apt:
sources:
- kubuntu-backports # cmake 2.8.12
packages:
- cmake
script:
- support/travis-build.py

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,6 +1,13 @@
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 ()
# Set the default CMAKE_BUILD_TYPE to Release.
# This should be done before the project command since the latter can set
@ -13,52 +20,38 @@ 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.
set(FMT_VERSION 3.0.0)
if (NOT FMT_VERSION MATCHES "^([0-9]+).([0-9]+).([0-9]+)$")
message(FATAL_ERROR "Invalid version format ${FMT_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})
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 +65,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,32 +83,23 @@ 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}")
string(REPLACE "*" ".*" line "${line}")
set(ignored_files ${ignored_files} "${line}$" "${line}/")
endforeach ()
set(ignored_files ${ignored_files}
set(ignored_files ${ignored_files} ${PROJECT_BINARY_DIR}
/.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees)
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 ()

View File

@ -1,3 +1,218 @@
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 +220,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 +231,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 +244,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 +258,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 +275,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 +284,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 +297,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 +310,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 +328,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 +370,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 +402,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 +412,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 +423,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 +473,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 +536,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 +573,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 +586,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 +652,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 +715,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 +755,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 +791,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 +824,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,21 +1,21 @@
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
--------
@ -26,11 +26,11 @@ Features
* 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
@ -42,21 +42,21 @@ Features
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 +75,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++
@ -103,10 +103,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 +132,17 @@ 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>`_:
* `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
* `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,7 +152,7 @@ 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/>`_:
@ -166,7 +170,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 +192,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 +281,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 +297,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 +305,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 +320,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 +336,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 +352,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,23 +374,18 @@ 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
---------------

2
cppformat/format.h Normal file
View File

@ -0,0 +1,2 @@
#include "../fmt/format.h"
#warning Including cppformat/format.h is deprecated. Include fmt/format.h instead.

2
cppformat/posix.h Normal file
View File

@ -0,0 +1,2 @@
#include "../fmt/posix.h"
#warning Including cppformat/posix.h is deprecated. Include fmt/posix.h instead.

View File

@ -5,7 +5,6 @@ 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})
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
DESTINATION share/doc/cppformat)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/ DESTINATION share/doc/fmt)

View File

@ -1,17 +1,17 @@
{% 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)
})(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 %}
@ -42,7 +42,7 @@
<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 #}
@ -53,8 +53,9 @@
<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>
<li><a href="http://fmtlib.net/2.0.0/">2.0.0</a></li>
<li><a href="http://fmtlib.net/1.1.0/">1.1.0</a></li>
<li><a href="http://fmtlib.net/1.0.0/">1.0.0</a></li>
</ul>
</li>
{% for name in ['Contents', 'Usage', 'API', 'Syntax'] %}
@ -76,18 +77,18 @@
{% if pagename == "index" %}
<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">
<a class="btn btn-success"
href="https://github.com/cppformat/cppformat/releases/download/2.0.0/cppformat-2.0.0.zip">
href="https://github.com/fmtlib/fmt/releases/download/2.0.0/cppformat-2.0.0.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>
<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>
<li><a href="https://github.com/fmtlib/fmt/releases/download/2.0.0/cppformat-2.0.0.zip">Version 2.0.0</a></li>
<li><a href="https://github.com/fmtlib/fmt/releases/download/1.1.0/cppformat-1.1.0.zip">Version 1.1.0</a></li>
<li><a href="https://github.com/fmtlib/fmt/releases/download/1.0.0/cppformat-1.0.0.zip">Version 1.0.0</a></li>
</ul>
</div>
</div>

View File

@ -4,12 +4,12 @@
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
@ -34,10 +34,94 @@ 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>`_.
``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)
.. doxygenfunction:: fprintf(std::ostream&, CStringRef, ArgList)
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 functions
===========================
---------------------------
The following functions use `printf format string syntax
<http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html>`_ with
@ -45,7 +129,7 @@ a POSIX extension for positional arguments.
.. doxygenfunction:: printf(CStringRef, ArgList)
.. doxygenfunction:: fprintf(std::FILE*, CStringRef, ArgList)
.. doxygenfunction:: fprintf(std::FILE *, CStringRef, ArgList)
.. doxygenfunction:: sprintf(CStringRef, ArgList)
@ -61,13 +145,13 @@ Write API
.. doxygenclass:: fmt::BasicArrayWriter
:members:
.. doxygenfunction:: bin
.. doxygenfunction:: bin(int)
.. doxygenfunction:: oct
.. doxygenfunction:: oct(int)
.. doxygenfunction:: hex
.. doxygenfunction:: hex(int)
.. doxygenfunction:: hexu
.. doxygenfunction:: hexu(int)
.. doxygenfunction:: pad(int, unsigned, Char)
@ -95,7 +179,7 @@ Utilities
:protected-members:
:members:
System Errors
System errors
=============
.. doxygenclass:: fmt::SystemError
@ -109,7 +193,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`::

View File

@ -46,7 +46,7 @@ def build_docs(version='dev'):
except DistributionNotFound:
pass
# Install Sphinx and Breathe.
pip_install('cppformat/sphinx',
pip_install('fmtlib/sphinx',
'12dde8afdb0a7bb5576e2656692c3478c69d8cc3',
check_version='1.4a0.dev-20151013')
pip_install('michaeljones/breathe',
@ -55,12 +55,12 @@ def build_docs(version='dev'):
cmd = ['doxygen', '-']
p = Popen(cmd, stdin=PIPE)
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}/format.h {0}/ostream.h
QUIET = YES
JAVADOC_AUTOBRIEF = YES
AUTOLINK_SUPPORT = NO
@ -69,12 +69,14 @@ def build_docs(version='dev'):
XML_OUTPUT = doxyxml
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(os.path.join(os.path.dirname(doc_dir), 'fmt')).encode('UTF-8'))
if p.returncode != 0:
raise CalledProcessError(p.returncode, cmd)
check_call(['sphinx-build',
@ -84,8 +86,8 @@ def build_docs(version='dev'):
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'),
'html/_static/fmt.css'])
except OSError as e:
if e.errno != errno.ENOENT:
raise

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'),
]

View File

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

View File

@ -1,9 +1,9 @@
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
@ -146,10 +146,10 @@ is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is needed
Portability
-----------
C++ Format is highly portable. Here is an incomplete list of operating systems and
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>`_,
* 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
@ -157,7 +157,7 @@ compilers where it has been tested and known to work:
* 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
@ -188,16 +188,16 @@ always prints ``inf``.
Ease of Use
-----------
C++ Format has small self-contained code base consisting of a single header file
fmt has a 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
<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

@ -216,6 +216,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'``. |
+---------+----------------------------------------------------------+
@ -262,6 +266,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.
@ -69,22 +69,22 @@ 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

91
fmt/CMakeLists.txt Normal file
View File

@ -0,0 +1,91 @@
# Define the fmt library, its includes and the needed defines.
# format.cc is added to FMT_HEADERS for the header-only configuration.
set(FMT_HEADERS format.h format.cc ostream.h ostream.cc 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} ../ChangeLog.rst)
option(FMT_CPPFORMAT "Build cppformat library for backward compatibility." OFF)
if (FMT_CPPFORMAT)
message(WARNING "The cppformat library is deprecated, use fmt instead.")
add_library(cppformat ${FMT_SOURCES} ${FMT_HEADERS})
endif ()
# Starting with cmake 3.1 the CXX_STANDARD property can be used instead.
target_compile_options(fmt PUBLIC ${CPP11_FLAG})
if (FMT_PEDANTIC)
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
target_include_directories(fmt INTERFACE
$<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)
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} 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})
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} DESTINATION ${FMT_LIB_DIR})
install(FILES ${FMT_HEADERS} DESTINATION include/fmt)
if (FMT_CPPFORMAT)
install(TARGETS cppformat DESTINATION ${FMT_LIB_DIR})
endif ()
endif ()

View File

@ -1,7 +1,7 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2015, Victor Zverovich
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -52,17 +52,6 @@
using fmt::internal::Arg;
// Check if exceptions are disabled.
#if defined(__GNUC__) && !defined(__EXCEPTIONS)
# define FMT_EXCEPTIONS 0
#endif
#if defined(_MSC_VER) && !_HAS_EXCEPTIONS
# define FMT_EXCEPTIONS 0
#endif
#ifndef FMT_EXCEPTIONS
# define FMT_EXCEPTIONS 1
#endif
#if FMT_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
@ -71,20 +60,6 @@ using fmt::internal::Arg;
# define FMT_CATCH(x) if (false)
#endif
#ifndef FMT_THROW
# if FMT_EXCEPTIONS
# define FMT_THROW(x) throw x
# else
# define FMT_THROW(x) assert(false)
# endif
#endif
#ifdef FMT_HEADER_ONLY
# define FMT_FUNC inline
#else
# define FMT_FUNC
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
@ -148,7 +123,7 @@ struct IntChecker<true> {
const char RESET_COLOR[] = "\x1b[0m";
typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef);
typedef void (*FormatFunc)(Writer &, int, StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
@ -188,7 +163,7 @@ int safe_strerror(
}
// Handle the case when strerror_r is not available.
int handle(fmt::internal::Null<>) {
int handle(internal::Null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
@ -200,7 +175,7 @@ int safe_strerror(
}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(fmt::internal::Null<>) {
int fallback(internal::Null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
@ -218,27 +193,32 @@ int safe_strerror(
return StrError(error_code, buffer, buffer_size).run();
}
void format_error_code(fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT {
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 ";
fmt::internal::IntTraits<int>::MainType ec_value = error_code;
// 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;
error_code_size += fmt::internal::count_digits(ec_value);
if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size)
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() <= fmt::internal::INLINE_BUFFER_SIZE);
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
}
void report_error(FormatFunc func,
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
fmt::MemoryWriter full_message;
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.
@ -247,111 +227,79 @@ void report_error(FormatFunc func,
}
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public fmt::internal::ArgVisitor<IsZeroInt, bool> {
class IsZeroInt : public ArgVisitor<IsZeroInt, bool> {
public:
template <typename T>
bool visit_any_int(T value) { return value == 0; }
};
// Parses an unsigned integer advancing s to the end of the parsed input.
// This function assumes that the first character of s is a digit.
template <typename Char>
int parse_nonnegative_int(const Char *&s) {
assert('0' <= *s && *s <= '9');
unsigned value = 0;
do {
unsigned new_value = value * 10 + (*s++ - '0');
// Check if value wrapped around.
if (new_value < value) {
value = UINT_MAX;
break;
}
value = new_value;
} while ('0' <= *s && *s <= '9');
if (value > INT_MAX)
FMT_THROW(fmt::FormatError("number is too big"));
return value;
}
template <typename Char>
inline bool is_name_start(Char c) {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
}
inline void require_numeric_argument(const Arg &arg, char spec) {
if (arg.type > Arg::LAST_NUMERIC_TYPE) {
std::string message =
fmt::format("format specifier '{}' requires numeric argument", spec);
FMT_THROW(fmt::FormatError(message));
}
}
template <typename Char>
void check_sign(const Char *&s, const Arg &arg) {
char sign = static_cast<char>(*s);
require_numeric_argument(arg, sign);
if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) {
FMT_THROW(fmt::FormatError(fmt::format(
"format specifier '{}' requires signed argument", sign)));
}
++s;
}
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, unsigned> {
class WidthHandler : public ArgVisitor<WidthHandler, unsigned> {
private:
fmt::FormatSpec &spec_;
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {}
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg() {
FMT_THROW(fmt::FormatError("width is not integer"));
FMT_THROW(FormatError("width is not integer"));
}
template <typename T>
unsigned visit_any_int(T value) {
typedef typename fmt::internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = value;
if (fmt::internal::is_negative(value)) {
spec_.align_ = fmt::ALIGN_LEFT;
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;
}
if (width > INT_MAX)
FMT_THROW(fmt::FormatError("number is too big"));
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
}
};
class PrecisionHandler :
public fmt::internal::ArgVisitor<PrecisionHandler, int> {
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> {
public:
void report_unhandled_arg() {
FMT_THROW(fmt::FormatError("precision is not integer"));
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(fmt::FormatError("number is too big"));
FMT_THROW(FormatError("number is too big"));
return static_cast<int>(value);
}
};
// Converts an integer argument to an integral type T for printf.
template <typename T, typename U>
struct is_same {
enum { value = 0 };
};
template <typename T>
class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> {
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:
fmt::internal::Arg &arg_;
internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(fmt::internal::Arg &arg, wchar_t type)
ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {}
void visit_bool(bool value) {
@ -362,44 +310,48 @@ class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> {
template <typename U>
void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using fmt::internal::Arg;
if (sizeof(T) <= sizeof(int)) {
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<T>(value));
arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
} else {
arg_.type = Arg::UINT;
arg_.uint_value = static_cast<unsigned>(
static_cast<typename fmt::internal::MakeUnsigned<T>::Type>(value));
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;
arg_.long_long_value =
static_cast<typename fmt::internal::MakeUnsigned<U>::Type>(value);
// 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 fmt::internal::MakeUnsigned<U>::Type>(value);
static_cast<typename internal::MakeUnsigned<U>::Type>(value);
}
}
}
};
// Converts an integer argument to char for printf.
class CharConverter : public fmt::internal::ArgVisitor<CharConverter, void> {
class CharConverter : public ArgVisitor<CharConverter, void> {
private:
fmt::internal::Arg &arg_;
internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public:
explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {}
explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
template <typename T>
void visit_any_int(T value) {
arg_.type = Arg::CHAR;
arg_.type = internal::Arg::CHAR;
arg_.int_value = static_cast<char>(value);
}
};
@ -407,134 +359,20 @@ class CharConverter : public fmt::internal::ArgVisitor<CharConverter, void> {
namespace internal {
template <typename Impl, typename Char>
class BasicArgFormatter : public ArgVisitor<Impl, void> {
private:
BasicWriter<Char> &writer_;
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(BasicArgFormatter);
void write_pointer(const void *p) {
spec_.flags_ = HASH_FLAG;
spec_.type_ = 'x';
writer_.write_int(reinterpret_cast<uintptr_t>(p), spec_);
}
protected:
BasicWriter<Char> &writer() { return writer_; }
FormatSpec &spec() { return spec_; }
void write(bool value) {
const char *str_value = value ? "true" : "false";
Arg::StringValue<char> str = { str_value, strlen(str_value) };
writer_.write_str(str, spec_);
}
void write(const char *value) {
Arg::StringValue<char> str = {value, value != 0 ? strlen(value) : 0};
writer_.write_str(str, spec_);
}
public:
BasicArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: writer_(w), spec_(s) {}
template <typename T>
void visit_any_int(T value) { writer_.write_int(value, spec_); }
template <typename T>
void visit_any_double(T value) { writer_.write_double(value, spec_); }
void visit_bool(bool value) {
if (spec_.type_)
return visit_any_int(value);
write(value);
}
void visit_char(int value) {
if (spec_.type_ && spec_.type_ != 'c') {
spec_.flags_ |= CHAR_FLAG;
writer_.write_int(value, spec_);
return;
}
if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0)
FMT_THROW(FormatError("invalid format specifier for char"));
typedef typename BasicWriter<Char>::CharPtr CharPtr;
Char fill = internal::CharTraits<Char>::cast(spec_.fill());
CharPtr out = CharPtr();
const unsigned CHAR_WIDTH = 1;
if (spec_.width_ > CHAR_WIDTH) {
out = writer_.grow_buffer(spec_.width_);
if (spec_.align_ == ALIGN_RIGHT) {
std::fill_n(out, spec_.width_ - CHAR_WIDTH, fill);
out += spec_.width_ - CHAR_WIDTH;
} else if (spec_.align_ == ALIGN_CENTER) {
out = writer_.fill_padding(out, spec_.width_,
internal::check(CHAR_WIDTH), fill);
} else {
std::fill_n(out + CHAR_WIDTH, spec_.width_ - CHAR_WIDTH, fill);
}
} else {
out = writer_.grow_buffer(CHAR_WIDTH);
}
*out = internal::CharTraits<Char>::cast(value);
}
void visit_cstring(const char *value) {
if (spec_.type_ == 'p')
return write_pointer(value);
write(value);
}
void visit_string(Arg::StringValue<char> value) {
writer_.write_str(value, spec_);
}
using ArgVisitor<Impl, void>::visit_wstring;
void visit_wstring(Arg::StringValue<Char> value) {
writer_.write_str(value, spec_);
}
void visit_pointer(const void *value) {
if (spec_.type_ && spec_.type_ != 'p')
report_unknown_type(spec_.type_, "pointer");
write_pointer(value);
}
};
// An argument formatter.
template <typename Char>
class ArgFormatter : public BasicArgFormatter<ArgFormatter<Char>, Char> {
private:
BasicFormatter<Char> &formatter_;
const Char *format_;
public:
ArgFormatter(BasicFormatter<Char> &f, FormatSpec &s, const Char *fmt)
: BasicArgFormatter<ArgFormatter<Char>, Char>(f.writer(), s),
formatter_(f), format_(fmt) {}
void visit_custom(Arg::CustomValue c) {
c.format(&formatter_, c.value, &format_);
}
};
template <typename Char>
class PrintfArgFormatter :
public BasicArgFormatter<PrintfArgFormatter<Char>, Char> {
public ArgFormatterBase<PrintfArgFormatter<Char>, Char> {
void write_null_pointer() {
this->spec().type_ = 0;
this->write("(nil)");
}
typedef BasicArgFormatter<PrintfArgFormatter<Char>, Char> Base;
typedef ArgFormatterBase<PrintfArgFormatter<Char>, Char> Base;
public:
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicArgFormatter<PrintfArgFormatter<Char>, Char>(w, s) {}
: ArgFormatterBase<PrintfArgFormatter<Char>, Char>(w, s) {}
void visit_bool(bool value) {
FormatSpec &fmt_spec = this->spec();
@ -728,27 +566,25 @@ FMT_FUNC void fmt::WindowsError::init(
FMT_FUNC void fmt::internal::format_windows_error(
fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT {
class String {
private:
LPWSTR str_;
public:
String() : str_() {}
~String() { LocalFree(str_); }
LPWSTR *ptr() { return &str_; }
LPCWSTR c_str() const { return str_; }
};
FMT_TRY {
String system_message;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(system_message.ptr()), 0, 0)) {
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) {
out << message << ": " << utf8_message;
return;
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,
0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
system_message, static_cast<uint32_t>(buffer.size()), 0);
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.
@ -793,7 +629,7 @@ void fmt::internal::ArgMap<Char>::init(const ArgList &args) {
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
map_.insert(Pair(named_arg->name, *named_arg));
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
@ -805,7 +641,7 @@ void fmt::internal::ArgMap<Char>::init(const ArgList &args) {
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_.insert(Pair(named_arg->name, *named_arg));
map_.push_back(Pair(named_arg->name, *named_arg));
}
}
for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
@ -814,7 +650,7 @@ void fmt::internal::ArgMap<Char>::init(const ArgList &args) {
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.insert(Pair(named_arg->name, *named_arg));
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
@ -827,68 +663,6 @@ void fmt::internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
template <typename Char>
template <typename StrChar>
void fmt::BasicWriter<Char>::write_str(
const Arg::StringValue<StrChar> &s, const FormatSpec &spec) {
// Check if StrChar is convertible to Char.
internal::CharTraits<Char>::convert(StrChar());
if (spec.type_ && spec.type_ != 's')
internal::report_unknown_type(spec.type_, "string");
const StrChar *str_value = s.value;
std::size_t str_size = s.size;
if (str_size == 0) {
if (!str_value) {
FMT_THROW(FormatError("string pointer is null"));
return;
}
}
std::size_t precision = spec.precision_;
if (spec.precision_ >= 0 && precision < str_size)
str_size = spec.precision_;
write_str(str_value, str_size, spec);
}
template <typename Char>
inline Arg fmt::BasicFormatter<Char>::get_arg(
BasicStringRef<Char> arg_name, const char *&error) {
if (check_no_auto_index(error)) {
map_.init(args());
const Arg *arg = map_.find(arg_name);
if (arg)
return *arg;
error = "argument not found";
}
return Arg();
}
template <typename Char>
inline Arg fmt::BasicFormatter<Char>::parse_arg_index(const Char *&s) {
const char *error = 0;
Arg arg = *s < '0' || *s > '9' ?
next_arg(error) : get_arg(parse_nonnegative_int(s), error);
if (error) {
FMT_THROW(FormatError(
*s != '}' && *s != ':' ? "invalid format string" : error));
}
return arg;
}
template <typename Char>
inline Arg fmt::BasicFormatter<Char>::parse_arg_name(const Char *&s) {
assert(is_name_start(*s));
const Char *start = s;
Char c;
do {
c = *++s;
} while (is_name_start(c) || ('0' <= c && c <= '9'));
const char *error = 0;
Arg arg = get_arg(fmt::BasicStringRef<Char>(start, s - start), error);
if (error)
FMT_THROW(fmt::FormatError(error));
return arg;
}
FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index];
@ -898,34 +672,13 @@ FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(
break;
case Arg::NAMED_ARG:
arg = *static_cast<const internal::Arg*>(arg.pointer);
break;
default:
/*nothing*/;
}
return arg;
}
inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) {
if (next_arg_index_ >= 0)
return do_get_arg(next_arg_index_++, error);
error = "cannot switch from manual to automatic argument indexing";
return Arg();
}
inline bool fmt::internal::FormatterBase::check_no_auto_index(
const char *&error) {
if (next_arg_index_ > 0) {
error = "cannot switch from automatic to manual argument indexing";
return false;
}
next_arg_index_ = -1;
return true;
}
inline Arg fmt::internal::FormatterBase::get_arg(
unsigned arg_index, const char *&error) {
return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg();
}
template <typename Char>
void fmt::internal::PrintfFormatter<Char>::parse_flags(
FormatSpec &spec, const Char *&s) {
@ -1024,7 +777,7 @@ void fmt::internal::PrintfFormatter<Char>::format(
if (*s == '.') {
++s;
if ('0' <= *s && *s <= '9') {
spec.precision_ = parse_nonnegative_int(s);
spec.precision_ = static_cast<int>(parse_nonnegative_int(s));
} else if (*s == '*') {
++s;
spec.precision_ = PrecisionHandler().visit(get_arg(s));
@ -1033,7 +786,7 @@ void fmt::internal::PrintfFormatter<Char>::format(
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg))
spec.flags_ &= ~HASH_FLAG;
spec.flags_ &= ~to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0') {
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
@ -1070,7 +823,7 @@ void fmt::internal::PrintfFormatter<Char>::format(
break;
default:
--s;
ArgConverter<int>(arg, *s).visit(arg);
ArgConverter<void>(arg, *s).visit(arg);
}
// Parse type.
@ -1098,196 +851,6 @@ void fmt::internal::PrintfFormatter<Char>::format(
write(writer, start, s);
}
template <typename Char>
const Char *fmt::BasicFormatter<Char>::format(
const Char *&format_str, const Arg &arg) {
const Char *s = format_str;
FormatSpec spec;
if (*s == ':') {
if (arg.type == Arg::CUSTOM) {
arg.custom.format(this, arg.custom.value, &s);
return s;
}
++s;
// Parse fill and alignment.
if (Char c = *s) {
const Char *p = s + 1;
spec.align_ = ALIGN_DEFAULT;
do {
switch (*p) {
case '<':
spec.align_ = ALIGN_LEFT;
break;
case '>':
spec.align_ = ALIGN_RIGHT;
break;
case '=':
spec.align_ = ALIGN_NUMERIC;
break;
case '^':
spec.align_ = ALIGN_CENTER;
break;
}
if (spec.align_ != ALIGN_DEFAULT) {
if (p != s) {
if (c == '}') break;
if (c == '{')
FMT_THROW(FormatError("invalid fill character '{'"));
s += 2;
spec.fill_ = c;
} else ++s;
if (spec.align_ == ALIGN_NUMERIC)
require_numeric_argument(arg, '=');
break;
}
} while (--p >= s);
}
// Parse sign.
switch (*s) {
case '+':
check_sign(s, arg);
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '-':
check_sign(s, arg);
spec.flags_ |= MINUS_FLAG;
break;
case ' ':
check_sign(s, arg);
spec.flags_ |= SIGN_FLAG;
break;
}
if (*s == '#') {
require_numeric_argument(arg, '#');
spec.flags_ |= HASH_FLAG;
++s;
}
// Parse zero flag.
if (*s == '0') {
require_numeric_argument(arg, '0');
spec.align_ = ALIGN_NUMERIC;
spec.fill_ = '0';
++s;
}
// Parse width.
if ('0' <= *s && *s <= '9') {
spec.width_ = parse_nonnegative_int(s);
} else if (*s == '{') {
++s;
Arg width_arg = is_name_start(*s) ?
parse_arg_name(s) : parse_arg_index(s);
if (*s++ != '}')
FMT_THROW(FormatError("invalid format string"));
ULongLong value = 0;
switch (width_arg.type) {
case Arg::INT:
if (width_arg.int_value < 0)
FMT_THROW(FormatError("negative width"));
value = width_arg.int_value;
break;
case Arg::UINT:
value = width_arg.uint_value;
break;
case Arg::LONG_LONG:
if (width_arg.long_long_value < 0)
FMT_THROW(FormatError("negative width"));
value = width_arg.long_long_value;
break;
case Arg::ULONG_LONG:
value = width_arg.ulong_long_value;
break;
default:
FMT_THROW(FormatError("width is not integer"));
}
if (value > INT_MAX)
FMT_THROW(FormatError("number is too big"));
spec.width_ = static_cast<int>(value);
}
// Parse precision.
if (*s == '.') {
++s;
spec.precision_ = 0;
if ('0' <= *s && *s <= '9') {
spec.precision_ = parse_nonnegative_int(s);
} else if (*s == '{') {
++s;
Arg precision_arg =
is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s);
if (*s++ != '}')
FMT_THROW(FormatError("invalid format string"));
ULongLong value = 0;
switch (precision_arg.type) {
case Arg::INT:
if (precision_arg.int_value < 0)
FMT_THROW(FormatError("negative precision"));
value = precision_arg.int_value;
break;
case Arg::UINT:
value = precision_arg.uint_value;
break;
case Arg::LONG_LONG:
if (precision_arg.long_long_value < 0)
FMT_THROW(FormatError("negative precision"));
value = precision_arg.long_long_value;
break;
case Arg::ULONG_LONG:
value = precision_arg.ulong_long_value;
break;
default:
FMT_THROW(FormatError("precision is not integer"));
}
if (value > INT_MAX)
FMT_THROW(FormatError("number is too big"));
spec.precision_ = static_cast<int>(value);
} else {
FMT_THROW(FormatError("missing precision specifier"));
}
if (arg.type <= Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) {
FMT_THROW(FormatError(
fmt::format("precision not allowed in {} format specifier",
arg.type == Arg::POINTER ? "pointer" : "integer")));
}
}
// Parse type.
if (*s != '}' && *s)
spec.type_ = static_cast<char>(*s++);
}
if (*s++ != '}')
FMT_THROW(FormatError("missing '}' in format string"));
// Format argument.
internal::ArgFormatter<Char>(*this, spec, s - 1).visit(arg);
return s;
}
template <typename Char>
void fmt::BasicFormatter<Char>::format(BasicCStringRef<Char> format_str) {
const Char *s = format_str.c_str();
const Char *start = s;
while (*s) {
Char c = *s++;
if (c != '{' && c != '}') continue;
if (*s == c) {
write(writer_, start, s);
start = ++s;
continue;
}
if (c == '}')
FMT_THROW(FormatError("unmatched '}' in format string"));
write(writer_, start, s - 1);
Arg arg = is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s);
start = s = format(s, arg);
}
write(writer_, start, s);
}
FMT_FUNC void fmt::report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
@ -1312,12 +875,6 @@ FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void fmt::print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
os.write(w.data(), w.size());
}
FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
@ -1341,10 +898,7 @@ template struct fmt::internal::BasicData<void>;
template void fmt::internal::FixedBuffer<char>::grow(std::size_t);
template const char *fmt::BasicFormatter<char>::format(
const char *&format_str, const fmt::internal::Arg &arg);
template void fmt::BasicFormatter<char>::format(CStringRef format);
template void fmt::internal::ArgMap<char>::init(const fmt::ArgList &args);
template void fmt::internal::PrintfFormatter<char>::format(
BasicWriter<char> &writer, CStringRef format);
@ -1361,11 +915,7 @@ template int fmt::internal::CharTraits<char>::format_float(
template void fmt::internal::FixedBuffer<wchar_t>::grow(std::size_t);
template const wchar_t *fmt::BasicFormatter<wchar_t>::format(
const wchar_t *&format_str, const fmt::internal::Arg &arg);
template void fmt::BasicFormatter<wchar_t>::format(
BasicCStringRef<wchar_t> format);
template void fmt::internal::ArgMap<wchar_t>::init(const fmt::ArgList &args);
template void fmt::internal::PrintfFormatter<wchar_t>::format(
BasicWriter<wchar_t> &writer, WCStringRef format);

File diff suppressed because it is too large Load Diff

61
fmt/ostream.cc Normal file
View File

@ -0,0 +1,61 @@
/*
Formatting library for C++ - std::ostream support
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 "ostream.h"
namespace fmt {
namespace {
// Write the content of w to os.
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);
write(os, w);
}
FMT_FUNC int fprintf(std::ostream &os, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
write(os, w);
return static_cast<int>(w.size());
}
} // namespace fmt

133
fmt/ostream.h Normal file
View File

@ -0,0 +1,133 @@
/*
Formatting library for C++ - std::ostream support
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.
*/
#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_;
Char *start_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0]) {
this->setp(start_, start_ + buffer_.capacity());
}
int_type overflow(int_type ch = traits_type::eof()) {
if (!traits_type::eq_int_type(ch, traits_type::eof())) {
size_t buf_size = size();
buffer_.resize(buf_size);
buffer_.reserve(buf_size * 2);
start_ = &buffer_[0];
start_[buf_size] = traits_type::to_char_type(ch);
this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
}
return ch;
}
size_t size() const {
return to_unsigned(this->pptr() - start_);
}
};
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)
};
};
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter, typename T>
void format(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], format_buf.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)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "ostream.cc"
#endif
#endif // FMT_OSTREAM_H_

View File

@ -1,7 +1,7 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2014 - 2015, Victor Zverovich
Copyright (c) 2014 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -55,11 +55,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.
@ -170,7 +173,7 @@ std::size_t fmt::File::read(void *buffer, std::size_t count) {
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0)
throw SystemError(errno, "cannot read from file");
return result;
return internal::to_unsigned(result);
}
std::size_t fmt::File::write(const void *buffer, std::size_t count) {
@ -178,7 +181,7 @@ std::size_t fmt::File::write(const void *buffer, std::size_t count) {
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0)
throw SystemError(errno, "cannot write to file");
return result;
return internal::to_unsigned(result);
}
fmt::File fmt::File::dup(int fd) {

View File

@ -1,7 +1,7 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2014 - 2015, Victor Zverovich
Copyright (c) 2014 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -34,11 +34,17 @@
#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>
#ifdef __APPLE__
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_POSIX
@ -139,7 +145,7 @@ 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;
}
@ -245,7 +251,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;
}
@ -299,7 +305,8 @@ class File {
// Closes the file.
void close();
// Returns the file size.
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
LongLong size() const;
// Attempts to read count bytes from the file into the specified buffer.
@ -331,6 +338,58 @@ class File {
// Returns the memory page size.
long getpagesize();
#if defined(LC_NUMERIC_MASK) || defined(_MSC_VER)
# 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", NULL)) {
if (!locale_)
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 = 0;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
} // namespace fmt
#if !FMT_USE_RVALUE_REFERENCES

64
fmt/time.h Normal file
View File

@ -0,0 +1,64 @@
/*
Formatting library for C++ - time formatting
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.
*/
#ifndef FMT_TIME_H_
#define FMT_TIME_H_
#include "fmt/format.h"
#include <ctime>
namespace fmt {
template <typename ArgFormatter>
void format(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;
}
const std::size_t MIN_GROWTH = 10;
buffer.reserve(buffer.capacity() + size > MIN_GROWTH ? size : MIN_GROWTH);
}
format_str = end + 1;
}
}
#endif // FMT_TIME_H_

View File

@ -24,8 +24,8 @@ else:
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']
build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4']
test_command = ['ctest', '-C', config]
check_call(cmake_command)
check_call(build_command)

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 ()

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

@ -0,0 +1,73 @@
# 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)
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 ()
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,8 +1,13 @@
#!/usr/bin/env python
# Release script
"""Create a release.
Usage:
release.py [<branch>]
"""
from __future__ import print_function
import datetime, fileinput, json, os, re, requests, shutil, sys, tempfile
import datetime, docopt, fileinput, json, os, re, requests, shutil, sys, tempfile
from docutils import nodes, writers, core
from subprocess import check_call
@ -126,58 +131,63 @@ class Runner:
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)
if __name__ == '__main__':
args = docopt.docopt(__doc__)
workdir = tempfile.mkdtemp()
try:
run = Runner()
fmt_dir = os.path.join(workdir, 'fmt')
branch = args.get('<branch>')
if branch is None:
branch = 'master'
run('git', 'clone', '-b', branch, 'git@github.com:fmtlib/fmt.git', fmt_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)
# Convert changelog from RST to GitHub-flavored Markdown and get the version.
changelog = 'ChangeLog.rst'
changelog_path = os.path.join(fmt_dir, changelog)
changes, version = core.publish_file(source_path=changelog_path, writer=MDWriter())
cmakelists = 'CMakeLists.txt'
for line in fileinput.input(os.path.join(fmt_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.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')
# 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)
run.cwd = fmt_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')
# Build the docs and package.
run('cmake', '.')
run('make', 'doc', 'package_source')
site_dir = os.path.join(workdir, 'fmtlib.github.io')
run('git', 'clone', 'git@github.com:fmtlib/fmtlib.github.io.git', site_dir)
doc_dir = os.path.join(site_dir, version)
shutil.copytree(os.path.join(fmt_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)
# Create a release on GitHub.
run('git', 'push', 'origin', 'release', cwd=fmt_dir)
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))
finally:
shutil.rmtree(workdir)

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

@ -12,6 +12,15 @@ def rmtree_if_exists(dir):
if e.errno == errno.ENOENT:
pass
def makedirs_if_not_exist(dir):
try:
os.makedirs(dir)
except OSError as e:
if e.errno != errno.EEXIST:
raise
fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
build = os.environ['BUILD']
if build == 'Doc':
travis = 'TRAVIS' in os.environ
@ -32,20 +41,19 @@ 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'))
sys.path.insert(0, os.path.join(fmt_dir, 'doc'))
import build
html_dir = build.build_docs()
repo = 'cppformat.github.io'
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 cppformat.github.io repo.
# 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 + 'cppformat/{}.git'.format(repo)])
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)
@ -59,7 +67,7 @@ if build == 'Doc':
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'
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'))
@ -67,11 +75,39 @@ if build == 'Doc':
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'])
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):
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

@ -19,7 +19,7 @@ class Git:
dir = tempfile.mkdtemp()
try:
git = Git(dir)
git('clone', '-b', 'coverity', 'git@github.com:cppformat/cppformat.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')

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,18 +32,41 @@ 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 ()
# 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_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()
@ -62,56 +74,22 @@ endfunction()
add_fmt_test(assert-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(util-test mock-allocator.h)
if (CPP11_FLAG)
set_target_properties(util-test PROPERTIES COMPILE_FLAGS ${CPP11_FLAG})
add_fmt_test(macro-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_executable(posix-mock-test posix-mock-test.cc ../fmt/format.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)
@ -119,37 +97,61 @@ endif ()
add_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_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_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)
if (TARGET fmt-header-only)
add_executable(header-only-test "main.cc")
target_link_libraries(header-only-test 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")

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)
if (TARGET fmt-header-only)
add_executable(header-only-test main.cc)
target_link_libraries(header-only-test 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

@ -29,13 +29,16 @@
#include "test-assert.h"
// Include format.cc instead of format.h to test implementation-specific stuff.
#include "format.cc"
#include "fmt/format.cc"
#include <algorithm>
#include <cstring>
#include "gmock/gmock.h"
#include "gtest-extra.h"
#include "util.h"
#undef min
#undef max
TEST(FormatTest, ArgConverter) {
@ -59,7 +62,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 +105,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,9 @@
// Test that the library compiles if None is defined to 0 as done by xlib.h.
#define None 0
#include "format.h"
#include "fmt/format.h"
#include "fmt/time.h"
#include "util.h"
#include "mock-allocator.h"
#include "gtest-extra.h"
@ -383,30 +383,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 +642,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 +659,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 +684,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 +701,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 +720,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 +744,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 +768,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 +792,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 +837,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 +857,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 +884,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) {
@ -979,7 +942,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 +1001,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) {
@ -1123,7 +1084,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 +1124,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 +1208,16 @@ TEST(FormatterTest, FormatOct) {
EXPECT_EQ(buffer, format("{0:o}", ULONG_MAX));
}
TEST(FormatterTest, FormatIntLocale) {
#ifndef _WIN32
const char *locale = "en_US.utf-8";
#else
const char *locale = "English_United States";
#endif
std::setlocale(LC_ALL, locale);
EXPECT_EQ("1,234,567", format("{:n}", 1234567));
}
TEST(FormatterTest, FormatFloat) {
EXPECT_EQ("392.500000", format("{0:f}", 392.5f));
}
@ -1311,7 +1281,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'));
@ -1380,14 +1350,14 @@ 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(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 {};
@ -1550,9 +1520,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 +1534,15 @@ TEST(FormatTest, Variadic) {
EXPECT_EQ(L"abc1", format(L"{}c{}", L"ab", 1));
}
TEST(FormatTest, Time) {
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));
}
template <typename T>
std::string str(const T &value) {
return fmt::format("{}", value);
@ -1638,23 +1614,33 @@ 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;
TEST(FormatTest, EmptyCustomOutput) {
EXPECT_EQ("", fmt::format("{}", EmptyTest()));
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);
}

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(...))

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;
}

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.

View File

@ -31,14 +31,14 @@
#include <string>
#include <gtest/gtest.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) \

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; \

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

@ -0,0 +1,192 @@
/*
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.cc"
#include <sstream>
#include "gmock/gmock.h"
#include "gtest-extra.h"
#include "util.h"
using fmt::format;
using fmt::FormatError;
template <typename Char>
std::basic_ostream<Char> &operator<<(
std::basic_ostream<Char> &os, const BasicTestString<Char> &s) {
os << s.value();
return os;
}
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, PrintfCustom) {
EXPECT_EQ("abc", fmt::sprintf("%s", TestString("abc")));
}
TEST(OStreamTest, FPrintf) {
std::ostringstream os;
int ret = fmt::fprintf(os, "Don't %s!", "panic");
EXPECT_EQ("Don't panic!", os.str());
EXPECT_EQ(12, ret);
}
TEST(OStreamTest, WriteToOStream) {
std::ostringstream os;
fmt::MemoryWriter w;
w << "foo";
fmt::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::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,109 @@ TEST(BufferedFileTest, FilenoNoRetry) {
EXPECT_EQ(2, fileno_count);
fileno_count = 0;
}
template <typename Mock>
struct ScopedMock : testing::StrictMock<Mock> {
ScopedMock() { Mock::instance = this; }
~ScopedMock() { Mock::instance = 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);
}
#ifdef __APPLE__
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.
@ -235,20 +236,20 @@ File OpenBufferedFile(int &fd) {
}
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,7 @@
#include <climits>
#include <cstring>
#include "format.h"
#include "fmt/format.h"
#include "gtest-extra.h"
#include "util.h"
@ -289,24 +289,25 @@ 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()))) {
signed_value = static_cast<int>(value);
unsigned_value = static_cast<int>(value);
unsigned_value = static_cast<unsigned>(value);
} else if (check(max <= std::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 +337,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,13 +379,20 @@ TEST(PrintfTest, Bool) {
TEST(PrintfTest, Int) {
EXPECT_PRINTF("-42", "%d", -42);
EXPECT_PRINTF("-42", "%i", -42);
unsigned u = -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);
}
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.650000", "%F", 392.65);
@ -442,10 +447,6 @@ TEST(PrintfTest, Pointer) {
EXPECT_PRINTF("(nil)", "%p", null_str);
}
TEST(PrintfTest, Custom) {
EXPECT_PRINTF("abc", "%s", TestString("abc"));
}
TEST(PrintfTest, Location) {
// TODO: test %n
}
@ -474,5 +475,5 @@ 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"));
}

View File

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

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(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
@ -332,8 +333,9 @@ 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;
@ -342,7 +344,7 @@ TEST(MemoryBufferTest, Grow) {
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));
@ -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(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"; }
};
@ -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

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,13 @@ const Char BasicTestString<Char>::EMPTY[] = {0};
typedef BasicTestString<char> TestString;
typedef BasicTestString<wchar_t> TestWString;
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_; }
};