Minor tweaks for dynamic_format_arg_store

This commit is contained in:
Victor Zverovich
2020-03-16 07:51:57 -07:00
parent 6012dc9ab4
commit 9f70fc3e7a
4 changed files with 236 additions and 259 deletions

View File

@@ -1205,6 +1205,62 @@ template <bool IS_PACKED, typename Context, typename T,
inline basic_format_arg<Context> make_arg(const T& value) {
return make_arg<Context>(value);
}
template <typename T, typename Char> struct is_string_view : std::false_type {};
template <typename Char>
struct is_string_view<basic_string_view<Char>, Char> : std::true_type {};
template <typename Char>
struct is_string_view<std_string_view<Char>, Char> : std::true_type {};
template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
class dyn_arg_storage {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct storage_node_base {
using owning_ptr = std::unique_ptr<storage_node_base<>>;
virtual ~storage_node_base() = default;
owning_ptr next;
};
template <typename T> struct storage_node : storage_node_base<> {
T value;
template <typename Arg>
FMT_CONSTEXPR storage_node(const Arg& arg, owning_ptr&& next) : value(arg) {
this->next = std::move(next); // Must be initialised after value.
}
template <typename Char>
FMT_CONSTEXPR storage_node(const basic_string_view<Char>& arg,
owning_ptr&& next)
: value(arg.data(), arg.size()) {
this->next = std::move(next); // Must be initialised after value.
}
};
storage_node_base<>::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 <typename T, typename Arg> const T& push(const Arg& arg) {
auto node = new storage_node<T>(arg, std::move(head_));
head_.reset(node);
return node->value;
}
};
} // namespace internal
// Formatting context.
@@ -1314,7 +1370,108 @@ inline format_arg_store<Context, Args...> make_format_args(
return {args...};
}
template <typename Context> class dynamic_format_arg_store;
/**
\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 <typename Context>
class dynamic_format_arg_store
#if FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
using char_type = typename Context::char_type;
template <typename T> struct need_dyn_copy {
static constexpr internal::type mapped_type =
internal::mapped_type_constant<T, Context>::value;
using type = std::integral_constant<
bool, !(internal::is_reference_wrapper<T>::value ||
internal::is_string_view<T, char_type>::value ||
(mapped_type != internal::type::cstring_type &&
mapped_type != internal::type::string_type &&
mapped_type != internal::type::custom_type &&
mapped_type != internal::type::named_arg_type))>;
};
template <typename T>
using stored_type = conditional_t<internal::is_string<T>::value,
std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> 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<Context>;
unsigned long long get_types() const {
return internal::is_unpacked_bit | data_.size();
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(internal::make_arg<Context>(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**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
template <typename T> void push_back(const T& arg) {
static_assert(
!std::is_base_of<internal::named_arg_base<char_type>, T>::value,
"named arguments are not supported yet");
if (internal::const_check(need_dyn_copy<T>::type::value))
emplace_arg(storage_.push<stored_type<T>>(arg));
else
emplace_arg(arg);
}
/**
Adds a reference to the argument into the dynamic store for later passing to
a formating function.
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_dyn_copy<T>::type::value,
"objects of built-in types and string views are always copied");
emplace_arg(arg.get());
}
};
/**
\rst
@@ -1639,180 +1796,6 @@ inline void print(const S& format_str, Args&&... args) {
internal::make_args_checked<Args...>(format_str, args...));
#endif
}
namespace internal {
template <typename T, typename Char> struct is_string_view : std::false_type {};
template <typename Char>
struct is_string_view<basic_string_view<Char>, Char> : std::true_type {};
template <typename Char>
struct is_string_view<std_string_view<Char>, Char> : std::true_type {};
template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
class dyn_arg_storage {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct storage_node_base {
using owning_ptr = std::unique_ptr<storage_node_base<>>;
virtual ~storage_node_base() = default;
owning_ptr next;
};
template <typename T> struct storage_node : storage_node_base<> {
T value;
template <typename Arg>
FMT_CONSTEXPR storage_node(const Arg& arg, owning_ptr&& next) : value{arg} {
// Must be initialised after value_
this->next = std::move(next);
}
template <typename Char>
FMT_CONSTEXPR storage_node(const basic_string_view<Char>& arg,
owning_ptr&& next)
: value{arg.data(), arg.size()} {
// Must be initialised after value
this->next = std::move(next);
}
};
storage_node_base<>::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 <typename T, typename Arg> const T& push(const Arg& arg) {
auto node = new storage_node<T>{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 <typename Context>
class dynamic_format_arg_store
#if FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
using char_type = typename Context::char_type;
template <typename T> struct need_dyn_copy {
static constexpr internal::type mapped_type =
internal::mapped_type_constant<T, Context>::value;
// static_assert(
// mapped_type != internal::type::named_arg_type,
// "Bug indicator. Named arguments must be processed separately");
using type = std::integral_constant<
bool, !(internal::is_reference_wrapper<T>::value ||
internal::is_string_view<T, char_type>::value ||
(mapped_type != internal::type::cstring_type &&
mapped_type != internal::type::string_type &&
mapped_type != internal::type::custom_type &&
mapped_type != internal::type::named_arg_type))>;
};
template <typename T>
using stored_type = conditional_t<internal::is_string<T>::value,
std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous
// Required by basic_format_args::args_ which is just a pointer.
std::vector<basic_format_arg<Context>> 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<Context>;
unsigned long long get_types() const {
return internal::is_unpacked_bit | data_.size();
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(internal::make_arg<Context>(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/core.h>
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc1");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
template <typename T> void push_back(const T& arg) {
static_assert(
!std::is_base_of<internal::named_arg_base<char_type>, T>::value,
"Named arguments are not supported yet");
if (internal::const_check(need_dyn_copy<T>::type::value))
emplace_arg(storage_.push<stored_type<T>>(arg));
else
emplace_arg(arg);
}
/**
\rst
Adds an argument into the dynamic store for later passing to a formating
function without copying into type-erasing list.
\endrst
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_dyn_copy<T>::type::value,
"Primitive types and string views directly supported by "
"basic_format_arg. Passing them by reference is not allowed");
emplace_arg(arg.get());
}
};
FMT_END_NAMESPACE
#endif // FMT_CORE_H_