diff --git a/include/fmt/async.h b/include/fmt/async.h index 4f81df61..cd96f72c 100644 --- a/include/fmt/async.h +++ b/include/fmt/async.h @@ -91,6 +91,9 @@ namespace detail { namespace detail = fmt::detail; template using decay_t = typename std::decay::type; template using add_const_t = typename std::add_const::type; +template struct disjunction : std::false_type {}; +template struct disjunction : B1 {}; +template struct disjunction : conditional_t> {}; enum class store_method { numeric, // stored by libfmt as numeric value, no need for extra storage @@ -135,45 +138,29 @@ struct stored_method_constant : std::integral_constant {}; custom_store_method::store_type::value> {}; -struct stored_objs_dtor_base : std::integral_constant { - static void destruct(void* p) {} +// Check for integer_sequence +#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 +template +using integer_sequence = std::integer_sequence; +template using index_sequence = std::index_sequence; +template using make_index_sequence = std::make_index_sequence; +#else +template struct integer_sequence { + using value_type = T; + + static FMT_CONSTEXPR size_t size() { return sizeof...(N); } }; -template -struct stored_objs_dtor_gen : std::integral_constant { - static void destruct(void* p) { - reinterpret_cast((reinterpret_cast(p) + offset))->~RawT(); - Base::destruct(p); - } -}; +template using index_sequence = integer_sequence; -template -struct stored_objs_dtor_select { - using RawT = remove_reference_t; - using type = conditional_t::value == store_method::object, - typename stored_objs_dtor_select, Args...>::type, - typename stored_objs_dtor_select::type>; -}; +template +struct make_integer_sequence : make_integer_sequence {}; +template +struct make_integer_sequence : integer_sequence {}; -template -struct stored_objs_dtor_select { using type = Base; }; - -template -using stored_objs_dtor = typename stored_objs_dtor_select::type; - -// Collects internal details for generating index ranges [MIN, MAX) -template struct index_list {}; - -// Induction step -template -struct range_builder : public range_builder {}; - -// Base step -template struct range_builder { typedef index_list type; }; - -// Meta-function that returns a [MIN, MAX) index range -template -using index_range = typename range_builder::type; +template +using make_index_sequence = make_integer_sequence; +#endif template struct arg_transformer { @@ -194,50 +181,60 @@ using transformed_arg_type = conditional_t::cu template struct async_entry_constructor { - using Entry = async_entry>...>; - using Dtor = stored_objs_dtor; - using Trans = arg_transformer; - template static size_t construct(void* buf, const S& format_str, Args... args) { return async_entry_constructor(buf, format_str, range(), std::forward(args)...).get_total_size(); } private: - using range = typename range_builder<0, sizeof...(Args)>::type; + using entry = async_entry>...>; + using trans = arg_transformer; + using char_type = typename Context::char_type; + using range = make_index_sequence; + template - FMT_CONSTEXPR async_entry_constructor(void* buf, const S& format_str, index_list, Args... args) : pEntry(reinterpret_cast(buf)), pBuffer(get_buffer_store(buf)) { - auto p = new(buf) Entry(format_str, store(std::forward(args))...); - p->set_dtor(Dtor::value ? Dtor::destruct : nullptr); + FMT_CONSTEXPR async_entry_constructor(void* buf, const S& format_str, index_sequence, Args... args) : pentry(reinterpret_cast(buf)), pBuffer(get_buffer_store(buf)) { + auto p = new(buf) entry(format_str, store(std::forward(args))...); + if (disjunction...>::value) p->set_dtor(destructor::dtor); } - template using arg_at = typename Trans::template arg_at; - template transformed_arg_type, Context> store(typename Trans::template arg_at arg) { - using Arg = typename Trans::template arg_at; + template using arg_at = typename trans::template arg_at; + template using type_at = typename trans::template type_at; + + template using need_destruct = std::integral_constant, Context>::value == store_method::object && std::is_destructible>::value && !std::is_trivially_destructible>::value>; + template static bool destruct(void *p, std::true_type) { reinterpret_cast*>(p + sizeof(entry) + trans::template objoffset_at::value)->~type_at(); return true; } + template static bool destruct(void *p, std::false_type) { return false; } + template struct destructor { + static void dummy(...) {} + static void dtor(void *p) { dummy(destruct(p, need_destruct())...); } + }; + + template transformed_arg_type, Context> store(arg_at arg) { + using Arg = arg_at; using select_store_method = custom_store_method; - using MappedType = detail::mapped_type_constant, Context>; + using mapped_type = detail::mapped_type_constant, Context>; if constexpr (select_store_method::custom_store == true) { using Formatter = typename Context::template formatter_type>; return Formatter::store(pBuffer, std::forward(arg)); } - else if constexpr (stored_as_string::value && !stored_as_string_object::value) { + else if constexpr (stored_as_string::value && !stored_as_string_object::value) { return copy_string(pBuffer, detail::arg_mapper().map(std::forward(arg))); } - else if constexpr (stored_as_numeric::value) { + else if constexpr (stored_as_numeric::value) { return std::forward(arg); } else { - char* const pobjs = pEntry + sizeof(Entry); - char* const pobj = pobjs + Trans::template objoffset_at::value; - using Type = typename Trans::template type_at; + char* const pobjs = pentry + sizeof(entry); + char* const pobj = pobjs + trans::template objoffset_at::value; + using Type = type_at; auto p = new(pobj) Type(std::forward(arg)); return *p; } } - static basic_string_view copy_string(char*& pBuffer, const typename Context::char_type* cstr) { - if constexpr (std::is_same::value) { + static basic_string_view copy_string(char*& pBuffer, const char_type* cstr) { + if constexpr (std::is_same::value) { wchar_t* pStart = reinterpret_cast(pBuffer); wchar_t* pEnd = wcpcpy(pStart, cstr); pBuffer = reinterpret_cast(pEnd); @@ -250,23 +247,23 @@ private: return basic_string_view(pStart, pEnd - pStart); } } - static basic_string_view copy_string(char*& pBuffer, basic_string_view sv) { - typename Context::char_type* pStart = reinterpret_cast(pBuffer); - size_t size = sizeof(typename Context::char_type) * sv.size(); + static basic_string_view copy_string(char*& pBuffer, basic_string_view sv) { + char_type* pStart = reinterpret_cast(pBuffer); + size_t size = sizeof(char_type) * sv.size(); std::memcpy(pStart, sv.data(), size); pBuffer += size; - return basic_string_view(pStart, sv.size()); + return basic_string_view(pStart, sv.size()); } static FMT_CONSTEXPR char* get_buffer_store(void* buf) { char* const pentry = reinterpret_cast(buf); // entry will be constructed here - char* const pobjs = pentry + sizeof(Entry); // objects will be stored starting here + char* const pobjs = pentry + sizeof(entry); // objects will be stored starting here char* const pbufs = pobjs + get_obj_size(); // buffers will be stored starting here return pbufs; } - static constexpr size_t get_obj_size() { return Trans::template objsizesum_at::value; } - constexpr size_t get_total_size() const { return pBuffer - pEntry; } - char* const pEntry; + static constexpr size_t get_obj_size() { return trans::template objsizesum_at::value; } + constexpr size_t get_total_size() const { return pBuffer - pentry; } + char* const pentry; char* pBuffer; }; @@ -323,4 +320,4 @@ inline size_t store_async_entry(void* buf, const S& format_str, Args&&... args) } } // namespace fmt -#endif // FMT_CORE_H_ +#endif // FMT_ASYNC_H_ diff --git a/test/async-test.cc b/test/async-test.cc index 0ab6a8bf..50d8b9ff 100644 --- a/test/async-test.cc +++ b/test/async-test.cc @@ -61,6 +61,26 @@ void make_async_entry_and_alter(const std::string& s) { formatted.front() = formatted.back() = '#'; EXPECT_EQ(formatted, fmt::format("{}", str)); } + +void make_async_entry_dtor_test(std::string s) { + static void* constructed; + static void* destructed; + struct my_string : std::string { + my_string(const std::string& s) : std::string(s) { + constructed = this; + } + ~my_string() { destructed = this; } + }; + { + my_string str(s); + size_t entry_size = fmt::store_async_entry(buf, "{}", str); + fmt::async::format(entry); + } + EXPECT_NE(nullptr, constructed); + EXPECT_EQ(constructed, destructed); +}; + + } TEST(AsyncTest, TrivialEntry) { @@ -98,6 +118,9 @@ TEST(AsyncTest, StoredEntry) { make_async_entry_test_args(short(1), (unsigned short)2, 3, 4U, 5L, 6UL, 7LL, 8ULL, 9.0F, 10.0, 11, 12, 13, 14, 15, 16, 17, 18); // make_async_entry_test(TWENTY_ARGS "{narg}", 1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,0/*ignore*/,"narg"_a="bingo"); - // this API copies only reference. + // this API copies buffer. make_async_entry_and_alter("[change me]"); + + // dtor + make_async_entry_dtor_test("custom string copied as object"); }