From b7df89b5195b7118c8992f630cf3a560a02c5654 Mon Sep 17 00:00:00 2001 From: "Adam C. Emerson" Date: Thu, 22 Dec 2016 17:43:03 -0500 Subject: [PATCH] denc: Scrap the container boilerplate! Using template templates, kill off the reduplication in container encoding. Signed-off-by: Adam C. Emerson --- src/include/denc.h | 737 ++++++++++++------------------------------ src/test/test_denc.cc | 85 ++--- 2 files changed, 244 insertions(+), 578 deletions(-) diff --git a/src/include/denc.h b/src/include/denc.h index c058b8e05fc..e5631737109 100644 --- a/src/include/denc.h +++ b/src/include/denc.h @@ -636,563 +636,222 @@ struct denc_traits< } }; -// -// std::list -// -template -struct denc_traits< - std::list, - typename std::enable_if::supported != 0>::type> { - typedef denc_traits traits; +namespace _denc { + template class C, typename Details, typename ...Ts> + struct container_base { + private: + using container = C; + using T = typename Details::T; - enum { supported = true }; - enum { featured = traits::featured }; - enum { bounded = false }; + public: + using traits = denc_traits; - template - static typename std::enable_if::type - bound_encode(const std::list& s, size_t& p) { - p += sizeof(uint32_t); - for (const T& e : s) { - denc(e, p); - } - } - template - static typename std::enable_if::type - bound_encode(const std::list& s, size_t& p) { - size_t elem_size = 0; - denc(*(const T*)nullptr, elem_size); - p += sizeof(uint32_t) + elem_size * s.size(); - } - template - static typename std::enable_if::type - bound_encode(const std::list& s, size_t& p, uint64_t f) { - p += sizeof(uint32_t); - for (const T& e : s) { - denc(e, p, f); - } - } - template - static typename std::enable_if::type - bound_encode(const std::list& s, size_t& p, uint64_t f) { - size_t elem_size = 0; - denc(*(const T*)nullptr, elem_size, f); - p += sizeof(uint32_t) + elem_size * s.size(); - } + enum { supported = true }; + enum { featured = traits::featured }; + enum { bounded = false }; - template - static typename std::enable_if::type - encode(const std::list& s, buffer::list::contiguous_appender& p) { - denc((uint32_t)s.size(), p); - for (const T& e : s) { - denc(e, p); + template + static typename std::enable_if::type + bound_encode(const container& s, size_t& p) { + p += sizeof(uint32_t); + for (const T& e : s) { + denc(e, p); + } } - } - template - static typename std::enable_if::type - encode(const std::list& s, buffer::list::contiguous_appender& p, + template + static typename std::enable_if::type + bound_encode(const container& s, size_t& p) { + size_t elem_size = 0; + denc(*(const T*)nullptr, elem_size); + p += sizeof(uint32_t) + elem_size * s.size(); + } + template + static typename std::enable_if::type + bound_encode(const container& s, size_t& p, uint64_t f) { + p += sizeof(uint32_t); + for (const T& e : s) { + denc(e, p, f); + } + } + template + static typename std::enable_if::type + bound_encode(const container& s, size_t& p, uint64_t f) { + size_t elem_size = 0; + denc(*(const T*)nullptr, elem_size, f); + p += sizeof(uint32_t) + elem_size * s.size(); + } + + template + static typename std::enable_if::type + encode(const container& s, buffer::list::contiguous_appender& p) { + denc((uint32_t)s.size(), p); + encode_nohead(s, p); + } + template + static typename std::enable_if::type + encode(const container& s, buffer::list::contiguous_appender& p, uint64_t f) { - denc((uint32_t)s.size(), p); - for (const T& e : s) { - denc(e, p, f); + denc((uint32_t)s.size(), p); + encode_nohead(s, p, f); } - } - static void decode(std::list& s, buffer::ptr::iterator& p, - uint64_t f=0) { - s.clear(); - uint32_t num; - denc(num, p); - while (num--) { - s.emplace_back(T()); - denc(s.back(), p, f); + static void decode(container& s, buffer::ptr::iterator& p, uint64_t f = 0) { + uint32_t num; + denc(num, p); + decode_nohead(num, s, p, f); } - } -}; -// -// std::vector -// -template + // nohead + template + static typename std::enable_if::type + encode_nohead(const container& s, buffer::list::contiguous_appender& p) { + for (const T& e : s) { + denc(e, p); + } + } + template + static typename std::enable_if::type + encode_nohead(const container& s, buffer::list::contiguous_appender& p, + uint64_t f) { + for (const T& e : s) { + denc(e, p, f); + } + } + static void decode_nohead(size_t num, container& s, + buffer::ptr::iterator& p, uint64_t f=0) { + s.clear(); + Details::reserve(s, num); + while (num--) { + T t; + denc(t, p, f); + Details::insert(s, std::move(t)); + } + } + }; + + template + class container_has_reserve { + template struct SFINAE_match; + template + static std::true_type test(SFINAE_match*); + + template + static std::false_type test(...); + + public: + static constexpr bool value = decltype( + test>(0))::value; + }; + + + template::value> + struct reserve_switch; + + template + struct reserve_switch { + static void reserve(Container& c, size_t s) { + c.reserve(s); + } + }; + + template + struct reserve_switch { + static void reserve(Container& c, size_t s) {} + }; + + template + struct container_details_base : public reserve_switch { + using T = typename Container::value_type; + }; + + template + struct pushback_details : public container_details_base { + template + static void insert(Container& c, Args&& ...args) { + c.emplace_back(std::forward(args)...); + } + }; +} + +template struct denc_traits< - std::vector, - typename std::enable_if::supported != 0>::type> { - typedef denc_traits traits; + std::list, + typename std::enable_if::supported != 0>::type> + : public _denc::container_base>, + T, Ts...> {}; - enum { supported = true }; - enum { featured = traits::featured }; - enum { bounded = false }; - - template - static typename std::enable_if::type - bound_encode(const std::vector& s, size_t& p) { - p += sizeof(uint32_t); - for (const T& e : s) { - denc(e, p); - } - } - template - static typename std::enable_if::type - bound_encode(const std::vector& s, size_t& p) { - size_t elem_size = 0; - denc(*(const T*)nullptr, elem_size); - p += sizeof(uint32_t) + elem_size * s.size(); - } - template - static typename std::enable_if::type - bound_encode(const std::vector& s, size_t& p, uint64_t f) { - p += sizeof(uint32_t); - for (const T& e : s) { - denc(e, p, f); - } - } - template - static typename std::enable_if::type - bound_encode(const std::vector& s, size_t& p, uint64_t f) { - size_t elem_size = 0; - denc(*(const T*)nullptr, elem_size, f); - p += sizeof(uint32_t) + elem_size * s.size(); - } - - template - static typename std::enable_if::type - encode(const std::vector& s, buffer::list::contiguous_appender& p) { - denc((uint32_t)s.size(), p); - for (const T& e : s) { - denc(e, p); - } - } - template - static typename std::enable_if::type - encode(const std::vector& s, buffer::list::contiguous_appender& p, - uint64_t f) { - denc((uint32_t)s.size(), p); - for (const T& e : s) { - denc(e, p, f); - } - } - static void decode(std::vector& s, buffer::ptr::iterator& p, uint64_t f=0) { - s.clear(); - uint32_t num; - denc(num, p); - s.resize(num); - for (unsigned i=0; i - static typename std::enable_if::type - encode_nohead(const std::vector& s, buffer::list::contiguous_appender& p) { - for (const T& e : s) { - denc(e, p); - } - } - template - static typename std::enable_if::type - encode_nohead(const std::vector& s, buffer::list::contiguous_appender& p, - uint64_t f) { - for (const T& e : s) { - denc(e, p, f); - } - } - static void decode_nohead(size_t num, std::vector& s, - buffer::ptr::iterator& p, uint64_t f=0) { - s.resize(num); - for (unsigned i=0; i -// -template +template struct denc_traits< - std::set, - typename std::enable_if::supported != 0>::type> { - typedef denc_traits traits; + std::vector, + typename std::enable_if::supported != 0>::type> + : public _denc::container_base>, + T, Ts...> {}; - enum { supported = true }; - enum { featured = traits::featured }; - enum { bounded = false }; +namespace _denc { + template + struct setlike_details : public container_details_base { + using T = typename Container::value_type; + template + static void insert(Container& c, Args&& ...args) { + c.emplace_hint(c.cend(), std::forward(args)...); + } + }; +} - template - static typename std::enable_if::type - bound_encode(const std::set& s, size_t& p) { - p += sizeof(uint32_t); - for (const T& e : s) { - denc(e, p); - } - } - template - static typename std::enable_if::type - bound_encode(const std::set& s, size_t& p) { - size_t elem_size = 0; - denc(*(const T*)nullptr, elem_size); - p += sizeof(uint32_t) + elem_size * s.size(); - } - template - static typename std::enable_if::type - bound_encode(const std::set& s, size_t& p, uint64_t f) { - p += sizeof(uint32_t); - for (const T& e : s) { - denc(e, p, f); - } - } - template - static typename std::enable_if::type - bound_encode(const std::set& s, size_t& p, uint64_t f) { - size_t elem_size = 0; - denc(*(const T*)nullptr, elem_size, f); - p += sizeof(uint32_t) + elem_size * s.size(); - } - - template - static typename std::enable_if::type - encode(const std::set& s, buffer::list::contiguous_appender& p) { - denc((uint32_t)s.size(), p); - for (const T& e : s) { - denc(e, p); - } - } - template - static typename std::enable_if::type - encode(const std::set& s, buffer::list::contiguous_appender& p, - uint64_t f) { - denc((uint32_t)s.size(), p); - for (const T& e : s) { - denc(e, p, f); - } - } - static void decode(std::set& s, buffer::ptr::iterator& p, uint64_t f=0) { - s.clear(); - uint32_t num; - denc(num, p); - while (num--) { - T temp; - denc(temp, p, f); - s.insert(temp); - } - } - - // nohead - template - static typename std::enable_if::type - encode_nohead(const std::set& s, buffer::list::contiguous_appender& p) { - for (const T& e : s) { - denc(e, p); - } - } - template - static typename std::enable_if::type - encode_nohead(const std::set& s, buffer::list::contiguous_appender& p, - uint64_t f) { - for (const T& e : s) { - denc(e, p, f); - } - } - static void decode_nohead(size_t num, std::set& s, - buffer::ptr::iterator& p, uint64_t f=0) { - s.clear(); - while (num--) { - T temp; - denc(temp, p, f); - s.insert(temp); - } - } - -}; - -// -// std::map -// -template +template struct denc_traits< - std::map, + std::set, + typename std::enable_if::supported != 0>::type> + : public _denc::container_base>, + T, Ts...> {}; + +namespace _denc { + template + struct maplike_details : public container_details_base { + using T = std::pair; + template + static void insert(Container& c, Args&& ...args) { + c.emplace_hint(c.cend(), std::forward(args)...); + } + }; +} + +template +struct denc_traits< + std::map, typename std::enable_if::supported != 0 && - denc_traits::supported != 0>::type> { - typedef denc_traits a_traits; - typedef denc_traits b_traits; + denc_traits::supported != 0>::type> + : public _denc::container_base>, + A, B, Ts...> {}; - enum { supported = true }; - enum { featured = a_traits::featured || b_traits::featured }; - enum { bounded = a_traits::bounded && b_traits::bounded }; - - template - static typename std::enable_if::type - bound_encode(const std::map& v, size_t& p) { - denc((uint32_t)v.size(), p); - for (const auto& i : v) { - denc(i.first, p); - denc(i.second, p); - } - } - template - static typename std::enable_if::type - bound_encode(const std::map& v, size_t& p, uint64_t f) { - denc((uint32_t)v.size(), p); - for (const auto& i : v) { - denc(i.first, p, f); - denc(i.second, p, f); - } - } - template - static typename std::enable_if::type - bound_encode(const std::map& v, size_t& p) { - denc((uint32_t)v.size(), p); - size_t elem_size = 0; - denc(*(A*)nullptr, elem_size); - denc(*(B*)nullptr, elem_size); - p += v.size() * elem_size; - } - template - static typename std::enable_if::type - bound_encode(const std::map& v, size_t& p, uint64_t f) { - denc((uint32_t)v.size(), p); - size_t elem_size = 0; - denc(*(A*)nullptr, elem_size, f); - denc(*(B*)nullptr, elem_size, f); - p += v.size() * elem_size; - } - - template - static typename std::enable_if::type - encode(const std::map& v, bufferlist::contiguous_appender& p) { - denc((uint32_t)v.size(), p); - for (const auto& i : v) { - denc(i.first, p); - denc(i.second, p); - } - } - template - static typename std::enable_if::type - encode(const std::map& v, bufferlist::contiguous_appender& p, - uint64_t f) { - denc((uint32_t)v.size(), p); - for (const auto& i : v) { - denc(i.first, p, f); - denc(i.second, p, f); - } - } - - static void decode(std::map& v, buffer::ptr::iterator& p, uint64_t f=0) { - v.clear(); - uint32_t num; - denc(num, p); - A key; - while (num--) { - denc(key, p, f); - denc(v[key], p, f); - } - } - - // nohead variants - template - static typename std::enable_if::type - encode_nohead(const std::map& v, bufferlist::contiguous_appender& p) { - for (const auto& i : v) { - denc(i.first, p); - denc(i.second, p); - } - } - template - static typename std::enable_if::type - encode_nohead(const std::map& v, bufferlist::contiguous_appender& p, - uint64_t f) { - for (const auto& i : v) { - denc(i.first, p, f); - denc(i.second, p, f); - } - } - static void decode_nohead(size_t num, std::map& v, - buffer::ptr::iterator& p, - uint64_t f=0) { - v.clear(); - A key; - while (num--) { - denc(key, p, f); - denc(v[key], p, f); - } - } -}; - -// boost::container::flat_map -template +template struct denc_traits< - boost::container::flat_map, + boost::container::flat_map, typename std::enable_if::supported != 0 && - denc_traits::supported != 0>::type> { - typedef denc_traits a_traits; - typedef denc_traits b_traits; - - enum { supported = true }; - enum { featured = a_traits::featured || b_traits::featured }; - enum { bounded = a_traits::bounded && b_traits::bounded }; - - template - static typename std::enable_if::type - bound_encode(const boost::container::flat_map& v, size_t& p) { - denc((uint32_t)v.size(), p); - for (const auto& i : v) { - denc(i.first, p); - denc(i.second, p); - } - } - template - static typename std::enable_if::type - bound_encode(const boost::container::flat_map& v, size_t& p, - uint64_t f) { - denc((uint32_t)v.size(), p); - for (const auto& i : v) { - denc(i.first, p, f); - denc(i.second, p, f); - } - } - template - static typename std::enable_if::type - bound_encode(const boost::container::flat_map& v, size_t& p) { - denc((uint32_t)v.size(), p); - size_t elem_size = 0; - denc(*(A*)nullptr, elem_size); - denc(*(B*)nullptr, elem_size); - p += v.size() * elem_size; - } - template - static typename std::enable_if::type - bound_encode(const boost::container::flat_map& v, size_t& p, - uint64_t f) { - denc((uint32_t)v.size(), p); - size_t elem_size = 0; - denc(*(A*)nullptr, elem_size, f); - denc(*(B*)nullptr, elem_size, f); - p += v.size() * elem_size; - } - - template - static typename std::enable_if::type - encode(const boost::container::flat_map& v, - bufferlist::contiguous_appender& p) { - denc((uint32_t)v.size(), p); - for (const auto& i : v) { - denc(i.first, p); - denc(i.second, p); - } - } - template - static typename std::enable_if::type - encode(const boost::container::flat_map& v, - bufferlist::contiguous_appender& p, - uint64_t f) { - denc((uint32_t)v.size(), p); - for (const auto& i : v) { - denc(i.first, p, f); - denc(i.second, p, f); - } - } - - static void decode(boost::container::flat_map& v, - buffer::ptr::iterator& p) { - v.clear(); - uint32_t num; - denc(num, p); - A key; - while (num--) { - denc(key, p); - denc(v[key], p); - } - } - - // nohead variants - template - static typename std::enable_if::type - encode_nohead(const boost::container::flat_map& v, - bufferlist::contiguous_appender& p) { - for (const auto& i : v) { - denc(i.first, p); - denc(i.second, p); - } - } - template - static typename std::enable_if::type - encode_nohead(const boost::container::flat_map& v, - bufferlist::contiguous_appender& p, - uint64_t f) { - for (const auto& i : v) { - denc(i.first, p, f); - denc(i.second, p, f); - } - } - static void decode_nohead(size_t num, boost::container::flat_map& v, - buffer::ptr::iterator& p) { - v.clear(); - A key; - while (num--) { - denc(key, p); - denc(v[key], p); - } - } -}; + denc_traits::supported != 0>::type> + : public _denc::container_base< + boost::container::flat_map, + _denc::maplike_details>, + A, B, Ts...> {}; // ---------------------------------------------------------------------- // class helpers diff --git a/src/test/test_denc.cc b/src/test/test_denc.cc index a73780b858e..3e79d83a940 100644 --- a/src/test/test_denc.cc +++ b/src/test/test_denc.cc @@ -198,11 +198,11 @@ struct legacy_t { }; WRITE_CLASS_ENCODER(legacy_t) -TEST(denc, vector) -{ +template class C> +void test_common_veclist(const char* c) { { - cout << "vector" << std::endl; - std::vector s; + cout << c << "" << std::endl; + C s; s.push_back("foo"); s.push_back("bar"); s.push_back("baz"); @@ -210,20 +210,32 @@ TEST(denc, vector) test_denc(s); } { - cout << "vector" << std::endl; - std::vector s; + cout << c << "" << std::endl; + C s; s.push_back(1); s.push_back(2); s.push_back(3); test_denc(s); } { - cout << "vector" << std::endl; - std::vector s; + cout << c << "" << std::endl; + C s; s.push_back(legacy_t(1)); s.push_back(legacy_t(2)); test_encode_decode(s); } +} + +// We only care about specializing the type, all other template +// parameters should have the default values. (Like first-class +// functions, first-class templates do not bring their defaults.) + +template +using default_vector = std::vector; + +TEST(denc, vector) +{ + test_common_veclist("std::vector"); { counts.reset(); vector v, v2; @@ -252,31 +264,12 @@ TEST(denc, vector) } } +template +using default_list = std::list; + TEST(denc, list) { - { - cout << "list" << std::endl; - std::list s; - s.push_back("foo"); - s.push_back("bar"); - s.push_back("baz"); - test_denc(s); - } - { - cout << "list" << std::endl; - std::list s; - s.push_back(1); - s.push_back(2); - s.push_back(3); - test_denc(s); - } - { - cout << "list" << std::endl; - std::list s; - s.push_back(legacy_t(1)); - s.push_back(legacy_t(2)); - test_encode_decode(s); - } + test_common_veclist("std::list"); { counts.reset(); list l, l2; @@ -402,34 +395,48 @@ TEST(denc, pair) ::encode(lp, bl); } -TEST(denc, map) -{ +template class C> +void test_common_maplike(const char* c) { { - cout << "map" << std::endl; - std::map s; + cout << c << "" << std::endl; + C s; s["foo"] = foo_t(); s["bar"] = foo_t(); s["baz"] = foo_t(); test_denc(s); } { - cout << "map" << std::endl; - std::map s; + cout << c << "" << std::endl; + C s; s["foo"] = bar_t(); s["bar"] = bar_t(); s["baz"] = bar_t(); test_denc_featured(s); } { - cout << "map" << std::endl; - std::map s; + cout << c << "" << std::endl; + C s; s["foo"] = legacy_t(1); s["bar"] = legacy_t(2); test_encode_decode(s); } } +template +using default_map = std::map; +TEST(denc, map) +{ + test_common_maplike("std::map"); +} + +template +using default_flat_map = boost::container::flat_map; + +TEST(denc, flat_map) +{ + test_common_maplike("boost::container::flat_map"); +} TEST(denc, bufferptr_shallow_and_deep) { // shallow encode