diff --git a/include/fmt/core.h b/include/fmt/core.h index 0669e7c1..993a9e96 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -10,9 +10,12 @@ #include // std::FILE #include +#include #include +#include #include #include +#include // The fmt library version in the form major * 10000 + minor * 100 + patch. #define FMT_VERSION 60103 @@ -1626,6 +1629,194 @@ inline void print(const S& format_str, Args&&... args) { internal::make_args_checked(format_str, args...)); #endif } + +namespace internal { + +template struct is_string_view : std::false_type {}; + +template +struct is_string_view, Char> : std::true_type {}; + +#ifdef FMT_USE_STRING_VIEW +template +struct is_string_view, Char> + : std::true_type {}; +#endif + +#ifdef FMT_USE_EXPERIMENTAL_STRING_VIEW +template +struct is_string_view, Char> + : std::true_type {}; +#endif + +template struct is_ref_wrapper : std::false_type {}; + +template +struct is_ref_wrapper> : std::true_type {}; + +template struct need_dyn_copy { + using mapped_type = mapped_type_constant; + static_assert(mapped_type::value != internal::type::named_arg_type, + "Bug indicator. Named arguments must be processed separately"); + + using type = std::integral_constant< + bool, !(is_ref_wrapper::value || + is_string_view::value || + (mapped_type::value != internal::type::cstring_type && + mapped_type::value != internal::type::custom_type && + mapped_type::value != internal::type::string_type))>; +}; + +template +using need_dyn_copy_t = typename need_dyn_copy::type; + +class dyn_arg_storage { + // Workaround clang's -Wweak-vtables. For templates (unlike regular classes + // doesn't complain about inability to deduce translation unit to place vtable + // So dyn_arg_node_base is made a fake template + + template struct storage_node_base { + using owning_ptr = std::unique_ptr>; + virtual ~storage_node_base() = default; + owning_ptr next_; + }; + + using owning_ptr = storage_node_base<>::owning_ptr; + + template struct storage_node : storage_node_base<> { + T value_; + FMT_CONSTEXPR explicit storage_node(const T& arg, owning_ptr&& next) + : value_{arg} { + // Must be initialised after value_ + next_ = std::move(next); + } + }; + + owning_ptr head_{nullptr}; + + public: + dyn_arg_storage() = default; + dyn_arg_storage(const dyn_arg_storage&) = delete; + dyn_arg_storage(dyn_arg_storage&&) = default; + + dyn_arg_storage& operator=(const dyn_arg_storage&) = delete; + dyn_arg_storage& operator=(dyn_arg_storage&&) = default; + + template const T& push(const Arg& arg) { + auto node = new storage_node{arg, std::move(head_)}; + head_.reset(node); + return node->value_; + } +}; + +} // namespace internal + +/** + \rst + A dynamic version of `fmt::format_arg_store<>`. + It's equipped with a storage to potentially temporary objects which lifetime + could be shorter than the format arguments object. + + It can be implicitly converted into `~fmt::basic_format_args` for passing + into type-erased formatting functions such as `~fmt::vformat`. + \endrst + */ +template +class dynamic_format_arg_store +#if FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + using char_type = typename Context::char_type; + using string_type = std::basic_string; + using value_type = basic_format_arg; + + template + using stored_type = + conditional_t::value, string_type, T>; + + // Storage of basic_format_arg must be contiguous + // Required by basic_format_args::args_ which is just a pointer + std::vector data_; + + // Storage of arguments not fitting into basic_format_arg must grow + // without relocation because items in data_ refer to it. + + internal::dyn_arg_storage storage_; + + friend class basic_format_args; + + unsigned long long get_types() const { + return internal::is_unpacked_bit | data_.size(); + } + + template const T& stored_value(const T& arg, std::false_type) { + return arg; + } + + template + const T& stored_value(const std::reference_wrapper& arg, std::false_type) { + return arg.get(); + } + + template + const stored_type& stored_value(const T& arg, std::true_type) { + return storage_.push>(arg); + } + + template void emplace_arg(const T& arg) { + data_.emplace_back(internal::make_arg(arg)); + } + + public: + dynamic_format_arg_store() = default; + ~dynamic_format_arg_store() = default; + + dynamic_format_arg_store(const dynamic_format_arg_store&) = delete; + dynamic_format_arg_store& operator=(const dynamic_format_arg_store&) = delete; + + dynamic_format_arg_store(dynamic_format_arg_store&&) = default; + dynamic_format_arg_store& operator=(dynamic_format_arg_store&&) = default; + + /** + \rst + Adds an argument into the dynamic store for later passing to a formating + function. + + Note that custom types and string types (but not string views!) are copied + into the store with dynamic memory (in addition to resizing vector). + + **Example**:: + + #include + fmt::dynamic_format_arg_store store; + store.push_back(42); + store.push_back("abc1"); + store.push_back(1.5f); + std::string result = fmt::vformat("{} and {} and {}", store); + \endrst + */ + template void push_back(const T& arg) { + static_assert( + !std::is_base_of, T>::value, + "Named arguments are not supported yet"); + emplace_arg(stored_value(arg, internal::need_dyn_copy_t{})); + } + + /** + \rst + Adds an argument into the dynamic store for later passing to a formating + function without copying into type-erasing list. + + Note that primitive type values are copied into basic_format_arg<>. + \endrst + */ + template void push_back(std::reference_wrapper arg) { + emplace_arg(arg.get()); + } +}; FMT_END_NAMESPACE #endif // FMT_CORE_H_ diff --git a/include/fmt/dyn-args.h b/include/fmt/dyn-args.h deleted file mode 100644 index 58e347dc..00000000 --- a/include/fmt/dyn-args.h +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) 2020 Vladimir Solontsov -// SPDX-License-Identifier: MIT Licence - -#ifndef FMT_DYN_ARGS_H_ -#define FMT_DYN_ARGS_H_ - -#include -#include -#include -#include - -#include "core.h" - -FMT_BEGIN_NAMESPACE - -namespace internal { - -template struct is_string_view : std::false_type {}; - -template -struct is_string_view, Char> : std::true_type {}; - -#ifdef FMT_USE_STRING_VIEW -template -struct is_string_view, Char> - : std::true_type {}; -#endif - -#ifdef FMT_USE_EXPERIMENTAL_STRING_VIEW -template -struct is_string_view, Char> - : std::true_type {}; -#endif - -template struct is_ref_wrapper : std::false_type {}; - -template -struct is_ref_wrapper> : std::true_type {}; - -template struct need_dyn_copy { - using mapped_type = mapped_type_constant; - static_assert(mapped_type::value != internal::type::named_arg_type, - "Bug indicator. Named arguments must be processed separately"); - - using type = std::integral_constant< - bool, !(is_ref_wrapper::value || - is_string_view::value || - (mapped_type::value != internal::type::cstring_type && - mapped_type::value != internal::type::custom_type && - mapped_type::value != internal::type::string_type))>; -}; - -template -using need_dyn_copy_t = typename need_dyn_copy::type; - -class dyn_arg_storage { - // Workaround clang's -Wweak-vtables. For templates (unlike regular classes - // doesn't complain about inability to deduce translation unit to place vtable - // So dyn_arg_node_base is made a fake template - - template struct storage_node_base { - using owning_ptr = std::unique_ptr>; - virtual ~storage_node_base() = default; - owning_ptr next_; - }; - - using owning_ptr = storage_node_base<>::owning_ptr; - - template struct storage_node : storage_node_base<> { - T value_; - FMT_CONSTEXPR explicit storage_node(const T& arg, owning_ptr&& next) - : value_{arg} { - // Must be initialised after value_ - next_ = std::move(next); - } - }; - - owning_ptr head_{nullptr}; - - public: - dyn_arg_storage() = default; - dyn_arg_storage(const dyn_arg_storage&) = delete; - dyn_arg_storage(dyn_arg_storage&&) = default; - - dyn_arg_storage& operator=(const dyn_arg_storage&) = delete; - dyn_arg_storage& operator=(dyn_arg_storage&&) = default; - - template const T& push(const Arg& arg) { - auto node = new storage_node{arg, std::move(head_)}; - head_.reset(node); - return node->value_; - } -}; - -} // namespace internal - -/** - \rst - A dynamic version of `fmt::format_arg_store<>`. - It's equipped with a storage to potentially temporary objects which lifetime - could be shorter than the format arguments object. - - It can be implicitly converted into `~fmt::basic_format_args` for passing - into type-erased formatting functions such as `~fmt::vformat`. - \endrst - */ -template -class dynamic_format_arg_store -#if FMT_GCC_VERSION < 409 - // Workaround a GCC template argument substitution bug. - : public basic_format_args -#endif -{ - private: - using char_type = typename Context::char_type; - using string_type = std::basic_string; - using value_type = basic_format_arg; - - template - using stored_type = - conditional_t::value, string_type, T>; - - // Storage of basic_format_arg must be contiguous - // Required by basic_format_args::args_ which is just a pointer - std::vector data_; - - // Storage of arguments not fitting into basic_format_arg must grow - // without relocation because items in data_ refer to it. - - internal::dyn_arg_storage storage_; - - friend class basic_format_args; - - unsigned long long get_types() const { - return internal::is_unpacked_bit | data_.size(); - } - - template const T& stored_value(const T& arg, std::false_type) { - return arg; - } - - template - const T& stored_value(const std::reference_wrapper& arg, std::false_type) { - return arg.get(); - } - - template - const stored_type& stored_value(const T& arg, std::true_type) { - return storage_.push>(arg); - } - - template void emplace_arg(const T& arg) { - data_.emplace_back(internal::make_arg(arg)); - } - - public: - dynamic_format_arg_store() = default; - ~dynamic_format_arg_store() = default; - - dynamic_format_arg_store(const dynamic_format_arg_store&) = delete; - dynamic_format_arg_store& operator=(const dynamic_format_arg_store&) = delete; - - dynamic_format_arg_store(dynamic_format_arg_store&&) = default; - dynamic_format_arg_store& operator=(dynamic_format_arg_store&&) = default; - - template void push_back(const T& arg) { - static_assert( - !std::is_base_of, T>::value, - "Named arguments are not supported yet"); - emplace_arg(stored_value(arg, internal::need_dyn_copy_t{})); - } - - template void push_back(std::reference_wrapper arg) { - emplace_arg(arg.get()); - } -}; - -FMT_END_NAMESPACE - -#endif /* FMT_DYN_ARGS_H_ */ diff --git a/test/format-dyn-args-test.cc b/test/format-dyn-args-test.cc index 90216290..da4da7ff 100644 --- a/test/format-dyn-args-test.cc +++ b/test/format-dyn-args-test.cc @@ -1,7 +1,7 @@ // Copyright (c) 2020 Vladimir Solontsov // SPDX-License-Identifier: MIT Licence -#include +#include #include "gtest-extra.h"