├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE_1_0.txt ├── README.md ├── cmake └── mpark_format-config.cmake.in ├── include └── mpark │ ├── format.hpp │ └── formats │ ├── lib.hpp │ └── parse.hpp └── test ├── CMakeLists.txt ├── README.md └── intro.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/googletest"] 2 | path = 3rdparty/googletest 3 | url = https://github.com/google/googletest.git 4 | [submodule "3rdparty/variant"] 5 | path = 3rdparty/variant 6 | url = https://github.com/mpark/variant.git 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MPark.Format 2 | # 3 | # Copyright Michael Park, 2014-2017 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE_1_0.txt or copy at 7 | # http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | cmake_minimum_required(VERSION 3.6.3) 10 | 11 | project(MPark.Format VERSION 1.0.0 LANGUAGES CXX) 12 | 13 | # Option. 14 | option(MPARK_FORMAT_INCLUDE_TESTS "Build tests." OFF) 15 | 16 | # Target. 17 | add_library(mpark_format INTERFACE) 18 | target_include_directories(mpark_format INTERFACE 19 | $ 20 | $) 21 | 22 | # Config. 23 | include(CMakePackageConfigHelpers) 24 | 25 | configure_package_config_file( 26 | cmake/mpark_format-config.cmake.in 27 | "${CMAKE_CURRENT_BINARY_DIR}/cmake/mpark_format-config.cmake" 28 | INSTALL_DESTINATION lib/cmake/mpark_format 29 | NO_CHECK_REQUIRED_COMPONENTS_MACRO) 30 | 31 | write_basic_package_version_file( 32 | "${CMAKE_CURRENT_BINARY_DIR}/cmake/mpark_format-config-version.cmake" 33 | COMPATIBILITY AnyNewerVersion) 34 | 35 | # Export. 36 | export( 37 | TARGETS mpark_format 38 | FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/mpark_format-targets.cmake") 39 | 40 | # Install. 41 | install(TARGETS mpark_format EXPORT mpark_format) 42 | 43 | install( 44 | EXPORT mpark_format 45 | FILE mpark_format-targets.cmake 46 | DESTINATION lib/cmake/mpark_format) 47 | 48 | install(DIRECTORY include/mpark DESTINATION include) 49 | 50 | install( 51 | FILES 52 | "${CMAKE_CURRENT_BINARY_DIR}/cmake/mpark_format-config.cmake" 53 | "${CMAKE_CURRENT_BINARY_DIR}/cmake/mpark_format-config-version.cmake" 54 | DESTINATION lib/cmake/mpark_format) 55 | 56 | if(MPARK_FORMAT_INCLUDE_TESTS) 57 | enable_testing() 58 | add_subdirectory(3rdparty/googletest/googletest) 59 | add_subdirectory(test) 60 | endif() 61 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MPark.Format 2 | 3 | > Compile-time Checked, Type-Safe Formatting in __C++14__. 4 | 5 | [![stability][badge.stability]][stability] 6 | [![license][badge.license]][license] 7 | [![wandbox][badge.wandbox]][wandbox] 8 | 9 | [badge.stability]: https://img.shields.io/badge/stability-experimental-orange.svg 10 | [badge.license]: http://img.shields.io/badge/license-boost-blue.svg 11 | [badge.wandbox]: https://img.shields.io/badge/try%20it-on%20wandbox-green.svg 12 | 13 | [stability]: http://github.com/badges/stability-badges 14 | [license]: https://github.com/mpark/patterns/blob/master/LICENSE_1_0.txt 15 | [wandbox]: https://wandbox.org/permlink/NfSCfnToS2QCJy36 16 | 17 | ## Introduction 18 | 19 | __MPark.Format__ is an experimental compile-time checked, type-safe formatting 20 | library for __C++14__. 21 | 22 | Currently, it supports Python-like format strings with positional parameters. 23 | 24 | ```cpp 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | struct Date { 31 | int year, month, day; 32 | }; 33 | 34 | std::ostream &operator<<(std::ostream &strm, const Date &that) { 35 | return strm << mpark::format( 36 | FS("{0}-{1}-{2}"), that.year, that.month, that.day); 37 | } 38 | 39 | int main() { 40 | std::cout << mpark::format(FS("{0}{1}{0}\n"), "abra", "cad"); 41 | // prints: "abracadabra" 42 | 43 | // std::cout << mpark::format(FS("{0}, {1}\n"), 'x'); 44 | // error: Index out of range. 45 | 46 | std::cout << mpark::format(FS("The date is {0}.\n"), Date{2016, 4, 19}); 47 | // prints: "The date is 2016-4-19." 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /cmake/mpark_format-config.cmake.in: -------------------------------------------------------------------------------- 1 | # MPark.Format 2 | # 3 | # Copyright Michael Park, 2014-2017 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE_1_0.txt or copy at 7 | # http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | # Config file for MPark.Format 10 | # 11 | # `MPARK_FORMAT_INCLUDE_DIRS` - include directories 12 | # `MPARK_FORMAT_LIBRARIES` - libraries to link against 13 | # 14 | # The following `IMPORTED` target is also defined: 15 | # 16 | # `mpark_format` 17 | 18 | @PACKAGE_INIT@ 19 | 20 | include("${CMAKE_CURRENT_LIST_DIR}/mpark_format-targets.cmake") 21 | 22 | get_target_property( 23 | MPARK_FORMAT_INCLUDE_DIRS 24 | mpark_format INTERFACE_INCLUDE_DIRECTORIES) 25 | 26 | set_and_check(MPARK_FORMAT_INCLUDE_DIRS "${MPARK_FORMAT_INCLUDE_DIRS}") 27 | set(MPARK_FORMAT_LIBRARIES mpark_format) 28 | -------------------------------------------------------------------------------- /include/mpark/format.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Format 2 | // 3 | // Copyright Michael Park, 2014-2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef MPARK_FORMAT_HPP 10 | #define MPARK_FORMAT_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #define FS(str_literal) \ 22 | [] { \ 23 | constexpr char s[] = #str_literal; \ 24 | static_assert(s[0] == '"' && s[sizeof(s) - 2] == '"', \ 25 | "Argument to `FS` must be a string literal."); \ 26 | using StrView = CONSTANT(::mpark::formats::lib::string_view( \ 27 | str_literal, sizeof(str_literal) - 1)); \ 28 | return StrView{}; \ 29 | }() 30 | 31 | namespace mpark { 32 | 33 | namespace formats { 34 | namespace detail { 35 | 36 | template 37 | class Format { 38 | public: 39 | 40 | explicit constexpr Format(std::tuple &&args) 41 | : args_(std::move(args)) {} 42 | 43 | operator std::string() && { 44 | std::ostringstream strm; 45 | strm << std::move(*this); 46 | return strm.str(); 47 | } 48 | 49 | template 50 | void write(std::ostream &strm, std::index_sequence) && { 51 | int dummy[] = {(std::move(*this).write_elem( 52 | strm, 53 | [] { 54 | using Elem = 55 | CONSTANT(std::get(Tuple::value())); 56 | return Elem{}; 57 | }()), 58 | 0)...}; 59 | static_cast(dummy); 60 | } 61 | 62 | private: 63 | 64 | template 65 | void write_elem(std::ostream &strm, Elem) && { 66 | std::move(*this).write_elem_impl( 67 | strm, Elem{}, lib::identity{}); 68 | } 69 | 70 | template 71 | void write_elem_impl(std::ostream &strm, 72 | Idx, 73 | lib::identity) && { 74 | static_assert(Idx::value() < sizeof...(Args), "Index out of range."); 75 | strm << std::get(std::move(args_)); 76 | } 77 | 78 | template 79 | void write_elem_impl(std::ostream &strm, 80 | StrView, 81 | lib::identity) && { 82 | strm << StrView::value(); 83 | } 84 | 85 | std::tuple args_; 86 | }; 87 | 88 | template 89 | auto format(Tuple, Args &&... args) { 90 | return Format( 91 | std::forward_as_tuple(std::forward(args)...)); 92 | } 93 | 94 | template 95 | std::ostream &operator<<(std::ostream &strm, 96 | const Format &) { 97 | static_assert(lib::false_v, 98 | "Result of `format` must be used immediately within the " 99 | "expression!."); 100 | return strm; 101 | } 102 | 103 | template 104 | std::ostream &operator<<(std::ostream &strm, 105 | Format &&that) { 106 | constexpr auto tuple = Tuple::value(); 107 | std::move(that).write(strm, 108 | std::make_index_sequence< 109 | std::tuple_size::value>{}); 110 | return strm; 111 | } 112 | 113 | } // namespace detail 114 | } // namespace formats 115 | 116 | template 117 | [[clang::warn_unused_result]] 118 | auto format(StrView, Args &&... args) { 119 | using Parse = CONSTANT(formats::parse(StrView{})); 120 | return formats::detail::format(Parse{}, std::forward(args)...); 121 | } 122 | 123 | } // namespace mpark 124 | 125 | #endif // MPARK_FORMAT_HPP 126 | -------------------------------------------------------------------------------- /include/mpark/formats/lib.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Format 2 | // 3 | // Copyright Michael Park, 2014-2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef MPARK_FORMATS_LIB_HPP 10 | #define MPARK_FORMATS_LIB_HPP 11 | 12 | #include 13 | #include 14 | 15 | namespace mpark { 16 | namespace formats { 17 | namespace lib { 18 | 19 | template 20 | struct identity { using type = T; }; 21 | 22 | #define CONSTANT(...) \ 23 | struct { \ 24 | static constexpr auto value() { return __VA_ARGS__; } \ 25 | } 26 | 27 | template 28 | using size_constant = std::integral_constant; 29 | 30 | template 31 | constexpr bool false_v = false; 32 | 33 | inline namespace cpp17 { 34 | 35 | template 36 | using bool_constant = std::integral_constant; 37 | 38 | class string_view { 39 | public: 40 | constexpr string_view(const char *str, std::size_t size) 41 | : str_(str), size_(size) {} 42 | 43 | constexpr const char *begin() const noexcept { return str_; } 44 | constexpr const char *end() const noexcept { return str_ + size_; } 45 | 46 | constexpr const char *data() const noexcept { return str_; } 47 | constexpr std::size_t size() const noexcept { return size_; } 48 | 49 | private: 50 | const char *str_; 51 | std::size_t size_; 52 | 53 | friend std::ostream &operator<<(std::ostream &strm, 54 | const string_view &that) { 55 | return strm.write(that.str_, that.size_); 56 | } 57 | }; 58 | 59 | } // namespace cpp17 60 | 61 | } // namespace lib 62 | } // namespace formats 63 | } // namespace mpark 64 | 65 | #endif // MPARK_FORMATS_LIB_HPP 66 | -------------------------------------------------------------------------------- /include/mpark/formats/parse.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Format 2 | // 3 | // Copyright Michael Park, 2014-2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef MPARK_FORMATS_PARSE_HPP 10 | #define MPARK_FORMATS_PARSE_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | namespace mpark { 19 | namespace formats { 20 | 21 | namespace detail { 22 | 23 | constexpr bool is_double_open(const char *text, std::size_t size) { 24 | return *text == '{' && size > 1 && *(text + 1) == '{'; 25 | } 26 | 27 | constexpr bool is_double_close(const char *text, std::size_t size) { 28 | return *text == '}' && size > 1 && *(text + 1) == '}'; 29 | } 30 | 31 | constexpr bool is_open(const char *text, std::size_t size) { 32 | return *text == '{' && !is_double_open(text, size); 33 | } 34 | 35 | constexpr bool is_close(const char *text) { return *text == '}'; } 36 | 37 | constexpr auto convert_impl(lib::string_view sv, std::true_type) { 38 | std::size_t idx = 0; 39 | for (std::size_t i = 1; i < sv.size() - 1; ++i) { 40 | idx *= 10; 41 | idx += sv.data()[i] - '0'; 42 | } // for 43 | return idx; 44 | } 45 | 46 | constexpr auto convert_impl(lib::string_view sv, std::false_type) { 47 | return sv; 48 | } 49 | 50 | template 51 | constexpr auto convert(StrView) { 52 | constexpr auto sv = StrView::value(); 53 | constexpr auto data = sv.data(); 54 | constexpr auto size = sv.size(); 55 | return convert_impl(sv, 56 | lib::bool_constant<(is_open(data, size) && 57 | is_close(data + size - 1))>{}); 58 | } 59 | 60 | enum class State { Success, SingleOpen, SingleClose }; 61 | 62 | template 63 | struct Report; 64 | 65 | template 66 | struct Report {}; 67 | 68 | template 69 | struct Report { 70 | static_assert(lib::false_v, 71 | "Single '{' encountered in format string."); 72 | }; 73 | 74 | template 75 | struct Report { 76 | static_assert(lib::false_v, 77 | "Single '}' encountered in format string."); 78 | }; 79 | 80 | constexpr auto get_state(const char *text, std::size_t size) { 81 | struct Result { 82 | State state; 83 | const char *end; 84 | const char *next; 85 | }; // Result 86 | std::size_t i = 0; 87 | if (is_open(text + i, size - i)) { 88 | while (i < size && !is_close(text + i)) { 89 | ++i; 90 | } // while 91 | if (i >= size) { 92 | return Result{State::SingleOpen, nullptr, nullptr}; 93 | } // if 94 | ++i; 95 | } else { 96 | while (i < size && !is_open(text + i, size - i)) { 97 | if (is_double_open(text + i, size - i) || 98 | is_double_close(text + i, size - i)) { 99 | return Result{State::Success, text + i + 1, text + i + 2}; 100 | } // if 101 | if (is_close(text + i)) { 102 | return Result{State::SingleClose, nullptr, nullptr}; 103 | } // if 104 | ++i; 105 | } // while 106 | } // if 107 | return Result{State::Success, text + i, text + i}; 108 | } 109 | 110 | } // namespace detail 111 | 112 | template 113 | constexpr auto parse_impl(End, lib::size_constant<0>) { 114 | return std::make_tuple(); 115 | } 116 | 117 | template 118 | constexpr auto parse_impl(Cur, lib::size_constant) { 119 | using State = CONSTANT(detail::get_state(Cur::value(), Size)); 120 | static_cast(detail::Report{}); 121 | using Token = CONSTANT( 122 | lib::string_view(Cur::value(), State::value().end - Cur::value())); 123 | using Next = CONSTANT(State::value().next); 124 | constexpr std::size_t size = Next::value() - Cur::value(); 125 | return std::tuple_cat( 126 | std::make_tuple(detail::convert(Token{})), 127 | parse_impl(Next{}, lib::size_constant{})); 128 | } 129 | 130 | template 131 | constexpr auto parse(StrView) { 132 | using Data = CONSTANT(StrView::value().data()); 133 | return parse_impl(Data{}, lib::size_constant{}); 134 | } 135 | 136 | } // namespace formats 137 | } // namespace mpark 138 | 139 | #endif // MPARK_FORMATS_PARSE_HPP 140 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MPark.Format 2 | # 3 | # Copyright Michael Park, 2014-2017 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE_1_0.txt or copy at 7 | # http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | cmake_minimum_required(VERSION 3.6.3) 10 | 11 | set(CMAKE_CXX_STANDARD 14) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | set(CMAKE_CXX_EXTENSIONS OFF) 14 | 15 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND 16 | "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") 17 | add_compile_options(-Qunused-arguments) 18 | add_compile_options(-Wno-deprecated-declarations) 19 | add_compile_options(-Wno-unknown-argument) 20 | endif() 21 | 22 | if(MPARK_FORMAT_INCLUDE_TESTS) 23 | 24 | function(add_gtest name) 25 | add_executable(${name} ${name}.cpp) 26 | set_target_properties(${name} PROPERTIES COMPILE_FLAGS "${cxx_strict}") 27 | target_link_libraries(${name} gtest_main mpark_format) 28 | add_test(${name} ${name} --gtest_color=yes) 29 | endfunction(add_gtest) 30 | 31 | add_gtest(intro) 32 | 33 | endif() 34 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # MPark.Format 2 | 3 | > Compile-time checked formatting in __C++14__. 4 | 5 | [![stability][badge.stability]][stability] 6 | [![license][badge.license]][license] 7 | [![wandbox][badge.wandbox]][wandbox] 8 | 9 | [badge.stability]: https://img.shields.io/badge/stability-experimental-orange.svg 10 | [badge.license]: http://img.shields.io/badge/license-boost-blue.svg 11 | [badge.wandbox]: https://img.shields.io/badge/try%20it-on%20wandbox-green.svg 12 | 13 | [stability]: http://github.com/badges/stability-badges 14 | [license]: https://github.com/mpark/patterns/blob/master/LICENSE_1_0.txt 15 | [wandbox]: https://wandbox.org/permlink/NfSCfnToS2QCJy36 16 | 17 | ## Test 18 | 19 | This directory contains the tests for __MPark.Format__. 20 | 21 | ### Building and Running Tests 22 | 23 | Execute the following commands from the top-level directory: 24 | 25 | ```bash 26 | mkdir build 27 | cd build 28 | cmake -DMPARK_FORMAT_INCLUDE_TESTS=ON .. 29 | cmake --build . 30 | ctest -V 31 | ``` 32 | -------------------------------------------------------------------------------- /test/intro.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Format 2 | // 3 | // Copyright Michael Park, 2014-2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | /* Missing features: 18 | 19 | 1. Formatting of arguments. 20 | 2. Pass arguments by name. 21 | 3. Allow FormatString construction from dynamic strings. */ 22 | 23 | void expect_eq(const std::string &expected, const std::string &actual) { 24 | EXPECT_EQ(expected, actual); 25 | } 26 | 27 | struct Date { int year, month, day; }; 28 | 29 | std::ostream &operator<<(std::ostream &strm, const Date &that) { 30 | return strm << mpark::format( 31 | FS("{0}-{1}-{2}"), that.year, that.month, that.day); 32 | } 33 | 34 | TEST(Format, Intro) { 35 | // error: Index out of range. 36 | // std::cout << mpark::format(FS("{0}")); 37 | 38 | // error: Single '{' encountered in format string. 39 | // std::cout << mpark::format(FS("{")); 40 | 41 | // error: Single '}' encountered in format string. 42 | // std::cout << mpark::format(FS("}")); 43 | 44 | // error: Result of `format` must be used immediately within the expression! 45 | // auto save = mpark::format(FS("{0}"), 42); 46 | // std::cout << save; 47 | 48 | expect_eq(mpark::format(FS("so much depends upon {0}"), "a red wheel barrow"), 49 | "so much depends upon a red wheel barrow"); 50 | 51 | expect_eq(mpark::format(FS("glazed with {0} water beside the {1} chickens\n"), 52 | "rain", 53 | "white"), 54 | "glazed with rain water beside the white chickens\n"); 55 | 56 | expect_eq(mpark::format(FS("{0} is better than {1}.\n"), "vim", "emacs"), 57 | "vim is better than emacs.\n"); 58 | 59 | expect_eq(mpark::format(FS("{1} is better than {0}.\n"), "vim", "emacs"), 60 | "emacs is better than vim.\n"); 61 | 62 | expect_eq( 63 | mpark::format(FS("I {0} the {1} off the {2}"), "took", "cheese", "table"), 64 | "I took the cheese off the table"); 65 | 66 | expect_eq(mpark::format(FS("Oh {0}, {0}! wherefore art thou {0}?"), "Romeo"), 67 | "Oh Romeo, Romeo! wherefore art thou Romeo?"); 68 | 69 | // Escaped braces. 70 | expect_eq( 71 | mpark::format(FS("The {0} set is often represented as {{0}}\n"), "empty"), 72 | "The empty set is often represented as {0}\n"); 73 | 74 | // Nested formatting. 75 | expect_eq(mpark::format(FS("{0} {1}\n"), 76 | mpark::format(FS("Hello, {0}!"), "there"), 77 | mpark::format(FS("My name is {0}."), "mpark")), 78 | "Hello, there! My name is mpark.\n"); 79 | 80 | // Reusing a template. 81 | auto bordered = FS("{0}{1}{0}"); 82 | expect_eq(mpark::format(bordered, "abra", "cad"), "abracadabra"); 83 | expect_eq(mpark::format(bordered, "ph", "otogra"), "photograph"); 84 | 85 | // Custom type. 86 | expect_eq(mpark::format(FS("The date is {0}"), Date{2012, 12, 9}), 87 | "The date is 2012-12-9"); 88 | } 89 | --------------------------------------------------------------------------------