View on GitHub
jbson
C++11/1y BSON library
document.hpp
1 // Copyright Christian Manning 2013.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef JBSON_DOCUMENT_HPP
7 #define JBSON_DOCUMENT_HPP
8 
9 #include <memory>
10 #include <vector>
11 #include <set>
12 #include <codecvt>
13 
14 #include "detail/config.hpp"
15 
16 JBSON_PUSH_DISABLE_DOCUMENTATION_WARNING
17 #include <boost/iterator/iterator_facade.hpp>
18 #include <boost/iterator/iterator_traits.hpp>
19 #include <boost/optional.hpp>
20 JBSON_CLANG_POP_WARNINGS
21 
22 #include "document_fwd.hpp"
23 #include "element_fwd.hpp"
24 #include "detail/traits.hpp"
25 #include "element.hpp"
26 
27 namespace jbson {
28 
33  const char* what() const noexcept override { return "invalid_document_size"; }
34 };
35 
36 namespace detail {
37 
44 template <typename Value, typename BaseIterator>
46  : boost::iterator_facade<document_iter<Value, BaseIterator>, Value, boost::forward_traversal_tag, Value&> {
47  static_assert(detail::is_element<std::remove_const_t<Value>>::value, "");
48 
50  using element_type = Value;
51 
53  document_iter() = default;
54 
60  document_iter(BaseIterator it1, BaseIterator it2) : m_start(it1), m_end(it2) {
61  if(m_start == m_end)
62  return;
63  m_cur = element_type{m_start, m_end};
64  }
65 
71  template <class OtherValue, typename OtherIt>
73  std::enable_if_t<std::is_convertible<OtherValue, element_type>::value&&
74  std::is_convertible<OtherIt, BaseIterator>::value>* = nullptr)
75  : m_start(other.m_start), m_end(other.m_end) {
76  if(m_start == m_end)
77  return;
78  m_cur = element_type{m_start, m_end};
79  }
80 
81 #ifndef DOXYGEN_SHOULD_SKIP_THIS
82  private:
83  friend class boost::iterator_core_access;
84  template <typename, typename> friend struct document_iter;
85  template <typename, typename> friend class jbson::basic_document;
86 #endif
87 
88  template <typename OtherValue, typename OtherIterator>
89  bool equal(const document_iter<OtherValue, OtherIterator>& other) const {
90  return other.m_start == m_start;
91  }
92 
93  void increment() {
94  if(m_start == m_end) {
95  m_cur = boost::none;
96  return;
97  }
98 
99  std::advance(m_start, m_cur->size());
100  if(m_start == m_end)
101  m_cur = boost::none;
102  else
103  m_cur = element_type{m_start, m_end};
104  }
105 
113  assert(m_cur);
114  return const_cast<element_type&>(*m_cur);
115  }
116 
117  private:
118  BaseIterator m_start;
119  BaseIterator m_end;
120  boost::optional<std::remove_const_t<element_type>> m_cur;
121 };
122 
123 #ifdef DOXYGEN_SHOULD_SKIP_THIS
124 
127 template <typename Container> void init_empty(Container& c);
128 #else
129 template <typename Container>
130 void init_empty(Container& c, std::enable_if_t<container_has_push_back<Container>::value>* = nullptr) {
131  static constexpr std::array<char, 5> arr{{5, 0, 0, 0, '\0'}};
132  boost::range::push_back(c, arr);
133 }
134 
135 template <typename Container>
136 void init_empty(Container& c,
137  std::enable_if_t<std::is_constructible<Container, std::array<char, 5>>::value>* = nullptr) {
138  static constexpr std::array<char, 5> arr{{5, 0, 0, 0, '\0'}};
139  c = arr;
140 }
141 
142 template <typename Container>
143 void init_empty(Container&, std::enable_if_t<!container_has_push_back<Container>::value>* = nullptr,
144  std::enable_if_t<!std::is_constructible<Container, std::array<char, 5>>::value>* = nullptr) {}
145 
146 #endif // DOXYGEN_SHOULD_SKIP_THIS
147 
148 } // namespace detail
149 
150 struct builder;
151 
153 enum class validity_level : uint8_t {
155  data_size = 0,
157  bson_size = 0b0001,
159  element_construct = 0b0010,
161  unicode_valid = 0b0100,
163  array_indices = 0b1000
164 };
165 
168  return (std::underlying_type_t<validity_level>)a < (std::underlying_type_t<validity_level>)b;
169 }
170 
172 inline bool operator>(validity_level a, validity_level b) { return b < a; }
173 
176  return static_cast<validity_level>((std::underlying_type_t<validity_level>)a &
177  (std::underlying_type_t<validity_level>)b);
178 }
179 
182  return static_cast<validity_level>((std::underlying_type_t<validity_level>)a |
183  (std::underlying_type_t<validity_level>)b);
184 }
185 
191 template <class Container, class ElementContainer> class basic_document {
192  public:
194  using container_type = std::decay_t<Container>;
195  static_assert(detail::is_nothrow_swappable<container_type>::value, "container_type must have noexcept swap()");
196 
205 
212 
225  template <typename SomeType>
226  explicit basic_document(SomeType&& c,
227  std::enable_if_t<std::is_same<container_type, std::decay_t<SomeType>>::value>* = nullptr)
228  : m_data(std::forward<SomeType>(c)) {
230  BOOST_THROW_EXCEPTION(invalid_document_size{} << detail::actual_size(boost::distance(m_data))
231  << detail::expected_size(sizeof(int32_t)));
233  BOOST_THROW_EXCEPTION(invalid_document_size{}
234  << detail::expected_size(
235  detail::little_endian_to_native<int32_t>(m_data.begin(), m_data.end()))
236  << detail::actual_size(boost::distance(m_data)));
237  }
238 
244  template <typename OtherContainer>
246  std::enable_if_t<std::is_constructible<container_type, OtherContainer>::value>* =
247  nullptr) noexcept(std::is_nothrow_constructible<container_type, OtherContainer>::value)
248  : m_data(other.m_data) {}
249 
255  template <typename OtherContainer>
257  std::enable_if_t<!std::is_constructible<container_type, OtherContainer>::value &&
259  std::is_constructible<container_type, typename OtherContainer::const_iterator,
260  typename OtherContainer::const_iterator>::value>* = nullptr)
261  : m_data(other.m_data.begin(), other.m_data.end()) {}
262 
264  template <size_t N> explicit basic_document(std::array<char, N>, std::enable_if_t<(N < 5)>* = nullptr) = delete;
265 
271  template <typename ForwardRange>
272  explicit basic_document(
273  ForwardRange&& rng, std::enable_if_t<detail::is_range_of_same_value<ForwardRange, char>::value>* = nullptr,
274  std::enable_if_t<
277  ForwardRange, boost::mpl::bind<detail::quote<std::is_constructible>,
278  typename container_type::iterator, boost::mpl::_1>>,
279  std::true_type>::value>* = nullptr,
280  std::enable_if_t<!std::is_same<container_type, std::decay_t<ForwardRange>>::value &&
282  ForwardRange, boost::mpl::bind<detail::quote<std::is_constructible>, container_type,
283  boost::mpl::_1, boost::mpl::_1>>::value>* = nullptr)
284  : basic_document(container_type(std::begin(rng), std::end(rng))) {}
285 
294  template <typename ForwardRange>
295  explicit basic_document(
296  ForwardRange&& rng,
297  std::enable_if_t<
298  boost::mpl::and_<detail::is_range_of_value<ForwardRange, boost::mpl::quote1<detail::is_element>>,
300  boost::mpl::not_<detail::is_document<std::decay_t<ForwardRange>>>>::type::value>* =
301  nullptr) {
302  static_assert(!detail::is_document<std::decay_t<ForwardRange>>::value, "");
303  std::array<char, 4> arr{{0, 0, 0, 0}};
304  boost::range::push_back(m_data, arr);
305  for(auto&& e : rng) {
306  e.write_to_container(m_data, m_data.end());
307  }
308  m_data.push_back('\0');
309  auto size = jbson::detail::native_to_little_endian(static_cast<int32_t>(m_data.size()));
310  static_assert(4 == size.size(), "");
311 
312  boost::range::copy(size, m_data.begin());
313  }
314 
318  template <typename ForwardIterator>
319  basic_document(ForwardIterator first, ForwardIterator last,
320  std::enable_if_t<
321  std::is_constructible<basic_document, boost::iterator_range<ForwardIterator>>::value>* = nullptr)
322  : basic_document(boost::make_iterator_range(first, last)) {}
323 
332  if(boost::distance(m_data) <= static_cast<ptrdiff_t>(sizeof(int32_t)))
333  BOOST_THROW_EXCEPTION(invalid_document_size{});
334  return {std::next(m_data.begin(), sizeof(int32_t)), std::prev(m_data.end())};
335  }
336 
342  const_iterator end() const noexcept {
343  auto last = std::prev(m_data.end());
344  return {last, last};
345  }
346 
352  const_iterator find(boost::string_ref elem_name) const {
353  return std::find_if(begin(), end(), [&elem_name](auto&& elem) { return elem.name() == elem_name; });
354  }
355 
367  assert(it != end());
368  auto pos = m_data.erase(it.m_start, std::next(it.m_start, it.m_cur->size()));
369 
370  auto size = jbson::detail::native_to_little_endian(static_cast<int32_t>(m_data.size()));
371  static_assert(4 == size.size(), "");
372 
373  boost::range::copy(size, m_data.begin());
374 
375  return {pos, std::prev(m_data.end())};
376  }
377 
391  template <typename EContainer>
394  el.write_to_container(data, data.end());
395 
396  auto pos = m_data.insert(it.m_start, data.begin(), data.end());
397 
398  auto size = jbson::detail::native_to_little_endian(static_cast<int32_t>(m_data.size()));
399  static_assert(4 == size.size(), "");
400 
401  boost::range::copy(size, m_data.begin());
402 
403  return {pos, std::prev(m_data.end())};
404  }
405 
419  template <typename... Args> const_iterator emplace(const const_iterator& it, Args&&... args) {
421  basic_element<container_type>::write_to_container(data, data.end(), std::forward<Args>(args)...);
422 
423  auto pos = m_data.insert(it.m_start, data.begin(), data.end());
424 
425  auto size = jbson::detail::native_to_little_endian(static_cast<int32_t>(m_data.size()));
426  static_assert(4 == size.size(), "");
427 
428  boost::range::copy(size, m_data.begin());
429 
430  return {pos, std::prev(m_data.end())};
431  }
432 
442  int32_t size() const noexcept { return boost::distance(m_data); }
443 
446  const container_type& data() const& noexcept { return m_data; }
447 
450  container_type data() const&& noexcept(std::is_nothrow_copy_constructible<container_type>::value) { return m_data; }
451 
454  container_type&& data() && noexcept(std::is_nothrow_move_constructible<container_type>::value) {
455  return std::move(m_data);
456  }
457 
467  bool valid(const validity_level lvl = validity_level::bson_size, const bool recurse = true) const {
468  bool ret{true};
469 
470  if(ret && lvl >= validity_level::data_size)
471  ret = static_cast<ptrdiff_t>(boost::distance(m_data)) > static_cast<ptrdiff_t>(sizeof(int32_t));
472  if(ret && lvl >= validity_level::bson_size) {
473  ret = static_cast<ptrdiff_t>(boost::distance(m_data)) ==
474  detail::little_endian_to_native<int32_t>(m_data.begin(), m_data.end());
475  }
476  if(ret && lvl >= validity_level::element_construct) {
477  try {
478  for(auto&& e : *this) {
479  if(recurse && e.type() == jbson::element_type::document_element)
480  ret = get<jbson::element_type::document_element>(e).valid(lvl, recurse);
481  else if(recurse && e.type() == jbson::element_type::array_element)
482  ret = get<jbson::element_type::array_element>(e).valid(lvl, recurse);
483  else if(recurse && e.type() == jbson::element_type::scoped_javascript_element) {
484  decltype(get<jbson::element_type::document_element>(e)) scope;
485  std::tie(std::ignore, scope) = get<jbson::element_type::scoped_javascript_element>(e);
486  ret = scope.valid(lvl, recurse);
487  }
488  if(!ret)
489  break;
490 
491  if((lvl & validity_level::unicode_valid) == validity_level::unicode_valid &&
493  std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> u8to32{};
494  auto str = get<jbson::element_type::string_element>(e);
495  u8to32.from_bytes(str.data(), str.data() + str.size());
496  }
497  if(!ret)
498  break;
499  }
500  }
501  catch(...) {
502  ret = false;
503  }
504  }
505  return ret;
506  }
507 
509  template <typename EContainer> explicit operator basic_document_set<EContainer>() const {
510  auto set = basic_document_set<EContainer>{};
511  for(auto&& e : *this) {
512  set.emplace(e);
513  }
514  return std::move(set);
515  }
516 
518  template <typename C, typename EC> bool operator==(const basic_document<C, EC>& other) const {
519  return boost::equal(m_data, other.m_data);
520  }
521 
523  void swap(basic_document& other) noexcept {
524  using std::swap;
525  swap(m_data, other.m_data);
526  }
527 
528  private:
529  container_type m_data;
530  template <typename, typename> friend class basic_document;
531  template <typename, typename> friend class basic_array;
532 };
533 
534 template <class Container, class ElementContainer> class basic_array : basic_document<Container, ElementContainer> {
536  template <typename, typename> friend class basic_array;
537 
538  public:
539  using typename base::container_type;
540  using typename base::element_type;
541  using typename base::iterator;
542  using typename base::const_iterator;
543  using typename base::value_type;
544 
545  using base::m_data;
546  using base::begin;
547  using base::end;
548  using base::data;
549  using base::size;
550  using base::swap;
551 
552  basic_array() : base() {}
553 
554  template <typename OtherContainer>
556  std::enable_if_t<std::is_constructible<container_type, OtherContainer>::value>* =
557  nullptr) noexcept(std::is_nothrow_constructible<container_type, OtherContainer>::value)
558  : base(other.m_data) {}
559 
560  template <typename Arg>
561  explicit basic_array(
562  Arg&& arg,
563  std::enable_if_t<std::is_constructible<base, Arg&&>::value && !std::is_convertible<Arg&&, base>::value>* =
564  nullptr) noexcept(std::is_nothrow_constructible<base, Arg&&>::value)
565  : base(std::forward<Arg>(arg)) {}
566 
567  template <typename Arg1, typename Arg2>
568  basic_array(Arg1&& arg1, Arg2&& arg2,
569  std::enable_if_t<std::is_constructible<base, Arg1&&, Arg2&&>::value>* =
570  nullptr) noexcept(std::is_nothrow_constructible<base, Arg1&&, Arg2&&>::value)
571  : base(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2)) {}
572 
573  const_iterator find(int32_t idx) const { return base::find(std::to_string(idx)); }
574 
575  bool valid(const validity_level lvl = validity_level::bson_size, const bool recurse = true) const {
576  bool ret = base::valid(lvl, recurse);
577  if(ret && (lvl & validity_level::array_indices) == validity_level::array_indices) {
578  try {
579  int32_t count{0};
580  for(auto&& e : *this) {
581  if(!(ret = (e.name() == std::to_string(count))))
582  break;
583 
584  ++count;
585  }
586  }
587  catch(...) {
588  ret = false;
589  }
590  }
591  return ret;
592  }
593 
594  template <typename SequenceContainer,
595  typename = std::enable_if_t<detail::container_has_push_back<SequenceContainer>::value>,
596  typename = std::enable_if_t<
598  explicit operator SequenceContainer() const {
599  auto fun = [](auto&& a, auto&& b) {
600 #ifdef _GNU_SOURCE
601  // natural sort
602  return strverscmp(a.name().data(), b.name().data());
603 #else
604  return a.name().compare(b.name());
605 #endif
606  };
607  auto vec = SequenceContainer{};
608  for(auto&& e : *this)
609  vec.push_back(e);
610  boost::range::sort(vec, [fun](auto&& e1, auto&& e2) { return fun(e1, e2) < 0; });
611  assert(std::unique(vec.begin(), vec.end(), [fun](auto&& e1, auto&& e2) { return fun(e1, e2) == 0; }) ==
612  vec.end());
613  return std::move(vec);
614  }
615 };
616 
618 template <typename Container, typename EContainer>
620  basic_document<Container, EContainer>& b) noexcept(noexcept(a.swap(b))) {
621  std::abort();
622  a.swap(b);
623 }
624 
626 template <typename Container, typename EContainer>
628  std::abort();
629  a.swap(b);
630 }
631 
632 // document_set
633 template <typename Container, typename SetContainer>
634 void value_set(basic_element<Container>& c, const basic_document_set<SetContainer>& val) {
635  c.value(document(val));
636 }
637 
638 } // namespace jbson
639 
640 #endif // JBSON_DOCUMENT_HPP
Type trait to determine whether a type is a basic_document (or basic_array)
bool operator>(validity_level a, validity_level b)
Allow more than (>) comparison for validity_level.
Definition: document.hpp:172
Forward traversal iterator type for traversing basic_document and basic_array.
Definition: document.hpp:45
Tests that array elements are integers named consecutively from 0 (zero). Includes above except unico...
const_iterator emplace(const const_iterator &it, Args &&...args)
Inserts an element in-place directly before another.
Definition: document.hpp:419
const_iterator find(boost::string_ref elem_name) const
Find an element with the specified name.
Definition: document.hpp:352
std::string or boost::string_ref (string_type)
bool operator==(const basic_document< C, EC > &other) const
Determines equality with another basic_document.
Definition: document.hpp:518
const_iterator iterator
Forward iterator for traversal of elements.
Definition: document.hpp:202
Basic test for the size of the data.
BSON document.
Definition: document.hpp:191
document_iter(const document_iter< OtherValue, OtherIt > &other, std::enable_if_t< std::is_convertible< OtherValue, element_type >::value &&std::is_convertible< OtherIt, BaseIterator >::value > *=nullptr)
Copy constructor.
Definition: document.hpp:72
std::multiset< basic_element< Container >, detail::elem_compare > basic_document_set
BSON document in the form of a std::set for ease of manipulation.
Definition: element_fwd.hpp:90
void init_empty(Container &c)
Initialises a container or range for a valid empty basic_document or basic_array. ...
document_iter(BaseIterator it1, BaseIterator it2)
Construct document_iter from pair of BaseIterators.
Definition: document.hpp:60
BSON element.
Definition: element.hpp:69
document_iter()=default
Default constructor. Results in an invalid iterator.
void swap(basic_document &other) noexcept
Swaps contents with another basic_document.
Definition: document.hpp:523
validity_level operator&(validity_level a, validity_level b)
Allow bitwise-and (&) for validity_level.
Definition: document.hpp:175
basic_document(SomeType &&c, std::enable_if_t< std::is_same< container_type, std::decay_t< SomeType >>::value > *=nullptr)
Constructs a document with an existing container of data.
Definition: document.hpp:226
bool valid(const validity_level lvl=validity_level::bson_size, const bool recurse=true) const
Validates document/array according to a validity_level.
Definition: document.hpp:467
Basic test for the equality of the data size with that reported by the data. Includes above...
is_range_of_value< RangeT, mpl::bind2< mpl::quote2< std::is_same >, ElementT, mpl::_1 >> is_range_of_same_value
Type trait to determine equivalence of the value_type of a Range.
Definition: traits.hpp:212
validity_level
Level of validity to test for with basic_document::valid.
Definition: document.hpp:153
const_iterator end() const noexcept
Definition: document.hpp:342
is_range_of< RangeT, ElementTrait, mpl::quote1< boost::range_mutable_iterator >> is_range_of_iterator
Type trait to apply a unary metafunction trait to the iterator type of a Range.
Definition: traits.hpp:219
Variadic version of boost::mpl::quoteN.
Definition: traits.hpp:224
container_type && data()&&noexcept(std::is_nothrow_move_constructible< container_type >::value)
Definition: document.hpp:454
const container_type & data() const &noexcept
Definition: document.hpp:446
Type trait to determine whether a type is a boost::iterator_range.
Definition: traits.hpp:155
typename mpl::eval_if< has_iterator< std::decay_t< Container >>, container_has_push_back_impl< std::decay_t< Container >>, std::false_type >::type container_has_push_back
Type trait to determine if type is a container with a push_back() function.
Definition: traits.hpp:183
Tests that each element can be constructed without error. Includes above.
element_type & dereference() const
Dereferences currently held basic_element.
Definition: document.hpp:112
Tests for valid unicode in elements with type element_type::string_element. Includes above...
const_iterator insert(const const_iterator &it, const basic_element< EContainer > &el)
Inserts an element directly before another.
Definition: document.hpp:392
int32_t size() const noexcept
Returns data's size in bytes.
Definition: document.hpp:442
basic_element< ElementContainer > element_type
Type of constituent elements.
Definition: document.hpp:198
is_range_of< RangeT, ElementTrait, mpl::quote1< boost::range_value >> is_range_of_value
Type trait to apply a unary metafunction trait to the value_type of a Range.
Definition: traits.hpp:205
Type trait to determine if a type is nothrow/noexcept swappable.
Definition: traits.hpp:282
validity_level operator|(validity_level a, validity_level b)
Allow bitwise-or (|) for validity_level.
Definition: document.hpp:181
basic_document(const basic_document< OtherContainer > &other, std::enable_if_t<!std::is_constructible< container_type, OtherContainer >::value &&detail::container_has_push_back< container_type >::value &&std::is_constructible< container_type, typename OtherContainer::const_iterator, typename OtherContainer::const_iterator >::value > *=nullptr)
Copy constructor from a basic_document with a different container_type.
Definition: document.hpp:256
const_iterator erase(const const_iterator &it)
Removes the element at it.
Definition: document.hpp:366
Exception type. Base class of all exceptions thrown directly by jbson.
Definition: error.hpp:23
basic_document(ForwardRange &&rng, std::enable_if_t< boost::mpl::and_< detail::is_range_of_value< ForwardRange, boost::mpl::quote1< detail::is_element >>, detail::container_has_push_back< container_type >, boost::mpl::not_< detail::is_document< std::decay_t< ForwardRange >>>>::type::value > *=nullptr)
Constructs a document from a range of basic_element.
Definition: document.hpp:295
basic_document> (document_type)
basic_document(ForwardIterator first, ForwardIterator last, std::enable_if_t< std::is_constructible< basic_document, boost::iterator_range< ForwardIterator >>::value > *=nullptr)
Constructs a document from two iterators. Forwards to the range-based constructors.
Definition: document.hpp:319
Value element_type
Alias to Value.
Definition: document.hpp:50
basic_document< std::vector< char >> document
Default basic_document type alias for owned BSON data.
Type trait to determine whether a type is a basic_element.
Definition: element_fwd.hpp:79
BSON array.
Definition: document.hpp:534
static void write_to_container(container_type &, typename container_type::const_iterator, boost::string_ref, element_type)
Constructs a BSON element without data, in-place into a container.
Definition: element.hpp:649
typename detail::document_iter< const element_type, typename container_type::const_iterator > const_iterator
Forward iterator for traversal of elements.
Definition: document.hpp:200
Exception thrown when an document's data size differs from that reported.
Definition: document.hpp:32
std::decay_t< Container > container_type
Type of underlying storage container/range.
Definition: document.hpp:194
const_iterator begin() const
Returns an iterator to the first element of the container.
Definition: document.hpp:331
basic_document()
Default constructor.
Definition: document.hpp:211
bool operator<(validity_level a, validity_level b)
Allow less than (<) comparison for validity_level.
Definition: document.hpp:167
basic_document(ForwardRange &&rng, std::enable_if_t< detail::is_range_of_same_value< ForwardRange, char >::value > *=nullptr, std::enable_if_t< std::conditional_t< detail::is_iterator_range< container_type >::value, detail::is_range_of_iterator< ForwardRange, boost::mpl::bind< detail::quote< std::is_constructible >, typename container_type::iterator, boost::mpl::_1 >>, std::true_type >::value > *=nullptr, std::enable_if_t<!std::is_same< container_type, std::decay_t< ForwardRange >>::value &&detail::is_range_of_iterator< ForwardRange, boost::mpl::bind< detail::quote< std::is_constructible >, container_type, boost::mpl::_1, boost::mpl::_1 >>::value > *=nullptr)
Constructs a document from a range of char.
Definition: document.hpp:272
basic_document(const basic_document< OtherContainer > &other, std::enable_if_t< std::is_constructible< container_type, OtherContainer >::value > *=nullptr) noexcept(std::is_nothrow_constructible< container_type, OtherContainer >::value)
Copy constructor from a basic_document with a different container_type.
Definition: document.hpp:245
container_type data() const &&noexcept(std::is_nothrow_copy_constructible< container_type >::value)
Definition: document.hpp:450