6 #ifndef JBSON_ELEMENT_HPP
7 #define JBSON_ELEMENT_HPP
12 #include "detail/config.hpp"
14 JBSON_PUSH_DISABLE_DOCUMENTATION_WARNING
15 #include <boost/range/algorithm.hpp>
16 #include <boost/range/algorithm_ext.hpp>
17 #include <boost/utility/string_ref.hpp>
18 #include <boost/mpl/or.hpp>
19 JBSON_CLANG_POP_WARNINGS
21 #include "element_fwd.hpp"
22 #include "document_fwd.hpp"
23 #include "detail/error.hpp"
24 #include "detail/get.hpp"
25 #include "detail/set.hpp"
26 #include "detail/traits.hpp"
27 #include "detail/visit.hpp"
29 JBSON_PUSH_DISABLE_DEPRECATED_WARNING
35 template <element_type EType,
typename Element,
typename Enable =
void>
struct typeid_visitor;
37 #ifndef DOXYGEN_SHOULD_SKIP_THIS
38 template <element_type EType,
typename Element>
39 struct typeid_visitor<EType, Element, std::enable_if_t<is_element<std::decay_t<Element>>::value>> {
41 template <
typename... Args> std::type_index operator()(Args&&...)
const {
46 template <element_type EType,
typename Element>
47 struct typeid_visitor<EType, Element, std::enable_if_t<!is_element<std::decay_t<Element>>::value>> {
49 template <
typename... Args> std::type_index operator()(Args&&...)
const {
50 return typeid(ElementTypeMap<EType, std::decay_t<Element>>);
53 #endif // DOXYGEN_SHOULD_SKIP_THIS
72 static_assert(!std::is_convertible<container_type, std::string>::value,
73 "container_type must not be a string type (or convertible)");
74 static_assert(std::is_same<typename container_type::value_type, char>::value,
75 "container_type's value_type must be char");
86 template <
typename OtherContainer>
88 std::enable_if_t<std::is_constructible<container_type, OtherContainer>::value>* =
nullptr);
91 template <
typename OtherContainer>
93 std::enable_if_t<!std::is_constructible<container_type, OtherContainer>::value>* =
nullptr,
94 std::enable_if_t<std::is_constructible<
container_type,
typename OtherContainer::const_iterator,
95 typename OtherContainer::const_iterator>::
value>* =
nullptr);
98 template <
typename OtherContainer>
100 std::enable_if_t<std::is_constructible<container_type, OtherContainer&&>::value>* =
nullptr);
103 template <
typename ForwardRange>
105 ForwardRange&&, std::enable_if_t<!std::is_constructible<std::string, ForwardRange>::value>* =
nullptr,
117 template <
typename ForwardIterator>
120 !std::is_constructible<boost::string_ref, ForwardIterator>::value ||
121 std::is_convertible<ForwardIterator, typename container_type::const_iterator>::value>* =
nullptr,
123 typename Container::value_type>::
value>* =
nullptr)
133 value(type, std::forward<T>(val));
152 value(std::forward<T>(val));
161 noexcept(std::is_nothrow_constructible<boost::string_ref, const std::
string&>::
value) {
170 void name(std::string n) { m_name.swap(n); }
173 size_t size() const noexcept;
189 if(!detail::valid_type(new_type))
205 template <
typename T>
206 std::enable_if_t<detail::is_valid_element_value_type<container_type, T>::value, T>
value()
const {
207 static_assert(!std::is_void<T>::value,
"Cannot assign value to void.");
208 static_assert(std::is_default_constructible<T>::value,
"Return type must be default constructible.");
210 if(!valid_type<T>(
type()))
212 << detail::actual_type(
typeid(T))
213 << detail::expected_type(detail::visit<detail::typeid_visitor>(
type(), *
this)));
215 namespace mpl = boost::mpl;
216 using element_pair =
typename mpl::deref<
217 detail::find_if_second<typename detail::TypeMap<container_type>::map_type,
218 mpl::bind<detail::quote<detail::is_convertible>, T, mpl::_1>>>
::type;
219 typename mpl::second<element_pair>::type ret{};
220 detail::deserialise(m_data, ret);
221 return T(std::move(ret));
235 template <
typename T>
236 std::enable_if_t<!detail::is_valid_element_value_type<container_type, T>::value, T>
value()
const {
237 static_assert(!std::is_void<T>::value,
"Cannot assign value to void.");
238 static_assert(std::is_default_constructible<T>::value,
"Return type must be default constructible.");
240 value_get(*
this, ret);
241 return std::move(ret);
254 template <
typename T>
255 void value(T&& val, std::enable_if_t<detail::is_valid_element_set_type<container_type, T>::value>* =
nullptr) {
256 namespace mpl = boost::mpl;
257 using element_pair =
typename mpl::deref<
258 detail::find_if_second<typename detail::TypeMap<container_type, true>::map_type,
259 mpl::bind<detail::quote<detail::is_constructible>, mpl::_1, T>>>
::type;
261 value<mpl::first<element_pair>::type::value>(std::forward<T>(val));
273 template <
typename T>
274 void value(T&& val, std::enable_if_t<!detail::is_valid_element_set_type<container_type, T>::value>* =
nullptr) {
276 auto old_type = m_type;
278 swap(m_data, old_data);
281 value_set(*
this, std::forward<T>(val));
285 swap(m_data, old_data);
304 template <
typename T>
306 std::enable_if_t<detail::is_valid_element_set_type<container_type, T>::value>* =
nullptr) {
308 detail::visit<detail::set_visitor>(new_type, data, data.end(), std::forward<T>(val));
310 if(detail::detect_size(new_type, data.begin(), data.end()) != static_cast<ptrdiff_t>(boost::distance(data)))
312 << detail::actual_size(static_cast<ptrdiff_t>(boost::distance(data)))
313 << detail::expected_size(detail::detect_size(new_type, data.begin(), data.end())));
331 template <
typename T>
333 std::enable_if_t<!detail::is_valid_element_set_type<container_type, T>::value>* =
nullptr) {
334 if(!detail::valid_type(new_type))
338 auto old_type = m_type;
340 swap(m_data, old_data);
343 value_set(*
this, std::forward<T>(val));
345 if(detail::detect_size(new_type, m_data.begin(), m_data.end()) !=
346 static_cast<ptrdiff_t>(boost::distance(m_data)))
347 BOOST_THROW_EXCEPTION(
349 << detail::actual_size(static_cast<ptrdiff_t>(boost::distance(m_data)))
350 << detail::expected_size(detail::detect_size(new_type, m_data.begin(), m_data.end())));
355 swap(m_data, old_data);
372 template <element_type EType,
typename T>
378 using T2 = ElementTypeMapSet<EType>;
379 static_assert(std::is_same<std::decay_t<T>, T2>::
value,
"");
382 auto it = data.end();
383 detail::serialise(data, it, std::forward<T>(val));
385 if(detail::size_func<EType, decltype(data.begin())> {}(data.begin(), data.end()) !=
386 static_cast<ptrdiff_t>(boost::distance(data)))
388 << detail::actual_size(static_cast<ptrdiff_t>(boost::distance(data)))
389 << detail::expected_size(detail::size_func<EType, decltype(data.begin())> {}(
390 data.begin(), data.end())));
409 template <element_type EType,
typename T>
415 using T2 = ElementTypeMapSet<EType>;
416 static_assert(std::is_constructible<T2, T>::value || std::is_convertible<std::decay_t<T>, T2>::
value,
"");
418 value<EType>(
static_cast<T2
>(std::forward<T>(val)));
422 template <
typename Visitor>
423 void visit(Visitor&&, std::enable_if_t<std::is_void<decltype(std::declval<Visitor>()(
424 "", std::declval<element_type>(), std::declval<double>()))>::
value>* =
nullptr)
const;
426 template <
typename Visitor>
427 auto visit(Visitor&&, std::enable_if_t<!std::is_void<decltype(std::declval<Visitor>()(
428 "", std::declval<element_type>(), std::declval<double>()))>::
value>* =
nullptr) const
429 -> decltype(std::declval<Visitor>()("", std::declval<
element_type>(), std::declval<
double>()));
435 template <typename T>
436 static
void write_to_container(container_type&, typename container_type::const_iterator, boost::string_ref, T&&);
438 template <typename T>
440 write_to_container(container_type&, typename container_type::const_iterator, boost::string_ref, element_type, T&&,
441 std::enable_if_t<detail::is_valid_element_set_type<container_type, T>::
value>* =
nullptr);
444 template <typename T>
446 write_to_container(container_type&, typename container_type::const_iterator, boost::string_ref, element_type, T&&,
447 std::enable_if_t<!detail::is_valid_element_set_type<container_type, T>::
value>* =
nullptr);
450 template <typename OutContainer>
455 template <typename OutContainer> explicit operator OutContainer() const;
459 return m_name == other.m_name && m_type == other.m_type && boost::range::equal(m_data, other.m_data);
476 auto res =
name().compare(other.
name());
477 if(res == 0 &&
type() == other.
type()) {
479 return value<double>() < other.
value<
double>();
481 auto a_str = this->value<detail::ElementTypeMap<element_type::string_element, container_type>>();
483 return std::use_facet<std::collate<char>>({}).compare(a_str.data(), a_str.data() + a_str.size(),
484 b_str.data(), b_str.data() + b_str.size()) < 0;
486 return m_data < other.m_data;
497 swap(m_name, other.m_name);
498 swap(m_type, other.m_type);
499 swap(m_data, other.m_data);
507 template <element_type EType>
using ElementTypeMap = detail::ElementTypeMap<EType, container_type>;
508 template <element_type EType>
using ElementTypeMapSet = detail::ElementTypeMapSet<EType, container_type>;
509 template <
typename T>
static bool valid_type(
element_type);
510 template <
typename T>
static bool valid_set_type(
element_type);
516 template <
typename Container>
530 template <
class Container>
531 template <
typename OutContainer>
533 static_assert(std::is_same<typename OutContainer::value_type, char>::value,
"");
534 if(!detail::valid_type(m_type))
537 if(detail::detect_size(m_type, m_data.begin(), m_data.end()) != static_cast<ptrdiff_t>(boost::distance(m_data)))
539 << detail::actual_size(static_cast<ptrdiff_t>(boost::distance(m_data)))
540 << detail::expected_size(detail::detect_size(m_type, m_data.begin(), m_data.end())));
542 it = std::next(c.insert(it, static_cast<uint8_t>(m_type)));
543 it = c.insert(it, m_name.begin(), m_name.end());
544 std::advance(it, m_name.size());
545 it = std::next(c.insert(it,
'\0'));
547 c.insert(it, m_data.begin(), m_data.end());
560 template <
typename Container>
561 template <
typename T>
563 boost::string_ref name, T&& val) {
564 static_assert(!std::is_same<element_type, T>::value,
"");
565 static_assert(detail::is_valid_element_set_type<container_type, T>::value,
"T must be compatible for deduction");
567 namespace mpl = boost::mpl;
568 using element_pair =
typename mpl::deref<
569 detail::find_if_second<typename detail::TypeMap<container_type, true>::map_type,
570 mpl::bind<detail::quote<detail::is_constructible>, mpl::_1, T>>>::type;
572 it = std::next(c.insert(it, static_cast<uint8_t>(mpl::first<element_pair>::type::value)));
573 it = c.insert(it, name.begin(), name.end());
574 std::advance(it, name.size());
575 it = std::next(c.insert(it,
'\0'));
577 detail::serialise(c, it,
static_cast<typename mpl::second<element_pair>::type
>(std::forward<T>(val)));
592 template <
typename Container>
593 template <
typename T>
596 std::enable_if_t<detail::is_valid_element_set_type<container_type, T>::value>*) {
597 if(!detail::valid_type(type))
600 if(!valid_set_type<T>(type))
602 << detail::actual_type(
typeid(T))
603 << detail::expected_type(detail::visit<detail::typeid_visitor>(type, Container{})));
605 it = std::next(c.insert(it, static_cast<uint8_t>(type)));
606 it = c.insert(it, name.begin(), name.end());
607 std::advance(it, name.size());
608 it = std::next(c.insert(it,
'\0'));
610 detail::visit<detail::set_visitor>(type, c, it, std::forward<T>(val));
628 template <
typename Container>
629 template <
typename T>
632 std::enable_if_t<!detail::is_valid_element_set_type<container_type, T>::value>*) {
633 auto e =
basic_element{name.to_string(), type, std::forward<T>(val)};
648 template <
typename Container>
651 if(!detail::valid_type(type))
657 << detail::actual_type(
typeid(
void))
658 << detail::expected_type(detail::visit<detail::typeid_visitor>(type,
basic_element())));
660 it = std::next(c.insert(it, static_cast<uint8_t>(type)));
661 it = c.insert(it, name.begin(), name.end());
662 std::advance(it, name.size());
663 it = std::next(c.insert(it,
'\0'));
668 write_to_container(c, c.end());
676 template <
class Container>
677 template <
typename OtherContainer>
679 std::enable_if_t<std::is_constructible<container_type, OtherContainer>::value>*)
680 : m_name(elem.m_name), m_type(elem.m_type), m_data(elem.m_data) {}
687 template <
class Container>
688 template <
typename OtherContainer>
691 std::enable_if_t<!std::is_constructible<container_type, OtherContainer>::value>*,
692 std::enable_if_t<std::is_constructible<
container_type,
typename OtherContainer::const_iterator,
693 typename OtherContainer::const_iterator>::value>*)
694 : m_name(elem.m_name), m_type(elem.m_type), m_data(elem.m_data.begin(), elem.m_data.end()) {}
700 template <
class Container>
701 template <
typename OtherContainer>
704 std::enable_if_t<std::is_constructible<container_type, OtherContainer&&>::value>*)
705 : m_name(std::move(elem.m_name)), m_type(std::move(elem.m_type)), m_data(std::move(elem.m_data)) {}
713 template <
class Container>
714 template <
typename ForwardRange>
716 ForwardRange&& range, std::enable_if_t<!std::is_constructible<std::string, ForwardRange>::value>*,
718 if(boost::distance(range) < 2)
720 << detail::actual_size(boost::distance(range)));
722 auto first = std::begin(range), last = std::end(range);
724 this->type(static_cast<element_type>(*first++));
726 auto str_end = std::find(first, last,
'\0');
727 m_name.assign(first, str_end++);
729 const auto elem_size = detail::detect_size(m_type, first, last);
730 if(std::distance(first, last) < elem_size)
732 << detail::actual_size(std::distance(first, last)));
733 last = std::next(first, elem_size);
745 template <
class Container>
746 template <
typename ForwardIterator>
748 ForwardIterator last)
749 : m_name(std::move(name)), m_data(first, last) {
758 template <
class Container>
760 : m_name(std::move(name)) {
765 return sizeof(m_type) + boost::distance(m_data) + m_name.size() +
sizeof(
'\0');
776 template <element_type EType,
typename Visitor,
typename Element>
struct element_visitor {
781 elem.template value<detail::ElementTypeMap<EType, typename std::decay_t<Element>::container_type>>());
785 #ifndef DOXYGEN_SHOULD_SKIP_THIS
787 template <
typename Visitor,
typename Element>
789 auto operator()(Visitor&& visitor, Element&& elem)
const {
794 template <
typename Visitor,
typename Element>
struct element_visitor<
element_type::
null_element, Visitor, Element> {
795 auto operator()(Visitor&& visitor, Element&& elem)
const {
800 template <
typename Visitor,
typename Element>
struct element_visitor<
element_type::
min_key, Visitor, Element> {
804 template <
typename Visitor,
typename Element>
struct element_visitor<
element_type::
max_key, Visitor, Element> {
808 #endif // DOXYGEN_SHOULD_SKIP_THIS
823 template <
typename EContainer,
typename EContainer2>
829 return lhs.
name() < rhs;
833 return lhs < rhs.
name();
841 struct inner : std::integral_constant<
842 bool, mpl::or_<std::is_convertible<T, detail::ElementTypeMap<EType, Container>>,
843 std::is_constructible<detail::ElementTypeMap<EType, Container>, T>>::value> {
844 static_assert(
sizeof...(Args) == 0,
"");
849 : std::integral_constant<
850 bool, mpl::or_<std::is_convertible<T, detail::ElementTypeMapSet<EType, Container>>,
851 std::is_constructible<detail::ElementTypeMapSet<EType, Container>, T>>::value> {
852 static_assert(
sizeof...(Args) == 0,
"");
859 return detail::visit<detail::is_valid_func<T, Container>::template inner>(type);
862 template <
class Container>
template <
typename T>
bool basic_element<Container>::valid_set_type(
element_type type) {
863 return detail::visit<detail::is_valid_func<T, Container>::template set_inner>(type);
874 template <element_type EType,
typename Container>
876 if(EType != elem.type())
878 << detail::actual_element_type(elem.type()));
880 return elem.template value<detail::ElementTypeMap<EType, Container>>();
891 return elem.template value<ReturnT>();
895 template <
typename CharT,
typename TraitsT>
896 inline std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os,
element_type e) {
899 os <<
"double_element";
902 os <<
"string_element";
905 os <<
"document_element";
908 os <<
"array_element";
911 os <<
"binary_element";
914 os <<
"undefined_element";
920 os <<
"boolean_element";
923 os <<
"date_element";
926 os <<
"null_element";
929 os <<
"regex_element";
932 os <<
"db_pointer_element";
935 os <<
"javascript_element";
938 os <<
"symbol_element";
941 os <<
"scoped_javascript_element";
944 os <<
"int32_element";
947 os <<
"timestamp_element";
950 os <<
"int64_element";
959 os <<
"unknown element_type";
980 template <
class Container>
981 template <
typename Visitor>
983 std::enable_if_t<std::is_void<decltype(std::declval<Visitor>()(
984 "", std::declval<element_type>(), std::declval<double>()))>::value>*)
const {
985 detail::visit<detail::element_visitor>(m_type, std::forward<Visitor>(visitor), *
this);
1004 template <
class Container>
1005 template <
typename Visitor>
1007 std::enable_if_t<!std::is_void<decltype(std::declval<Visitor>()(
1008 "", std::declval<element_type>(), std::declval<double>()))>::value>*) const
1009 -> decltype(std::declval<Visitor>()("", std::declval<
element_type>(), std::declval<
double>())) {
1010 return detail::visit<detail::element_visitor>(m_type, std::forward<Visitor>(visitor), *
this);
1017 #endif // JBSON_ELEMENT_HPP
std::enable_if_t< detail::is_valid_element_value_type< container_type, T >::value, T > value() const
Returns the value data in the form of a specific type.
basic_element()=default
Default constructor.
Visitor for checking validity of a type for setting.
void value(T &&val, std::enable_if_t<!std::is_same< std::decay_t< T >, detail::ElementTypeMapSet< EType, container_type >>::value > *=nullptr)
Sets value. Statically ensures type compatibility.
element_type
The element_type enum represents a BSON data type.
bool operator()(const basic_element< EContainer > &lhs, const basic_element< EContainer2 > &rhs) const
Functor call operator. Normal comparison.
std::string or boost::string_ref (string_type)
basic_element(std::string name, element_type type, T &&val)
Construct an element with specified name, type and value.
void value(T &&val, std::enable_if_t<!detail::is_valid_element_set_type< container_type, T >::value > *=nullptr)
Sets value from a user-defined type.
void visit(Visitor &&, std::enable_if_t< std::is_void< decltype(std::declval< Visitor >()("", std::declval< element_type >(), std::declval< double >()))>::value > *=nullptr) const
Apply the visitor pattern with a void-return visitor.
std::true_type is_transparent
Enables heterogeneous comparison in some standard algorithms and containers.
std::enable_if_t<!detail::is_valid_element_value_type< container_type, T >::value, T > value() const
Returns the value data in the form of a specific type.
Exception thrown when an element has a value not convertible to that requested.
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.
void name(std::string n)
Sets name to n.
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.
bool operator<(const basic_element &other) const
Checks if this is less than (<) other.
typename mpl::at< typename TypeMap< Container, true >::map_type, element_type_c< EType >>::type ElementTypeMapSet
Type alias to perform boost::mpl::at on TypeMap::map_type.
Visitor for obtaining std::type_index of a mapped element_type.
void value(T &&val, std::enable_if_t< detail::is_valid_element_set_type< container_type, T >::value > *=nullptr)
Sets value from a detail::TypeMap compatible type.
void swap(basic_document< Container, EContainer > &a, basic_document< Container, EContainer > &b) noexcept(noexcept(a.swap(b)))
Non-member swap for basic_document. Calls basic_document::swap.
void type(element_type new_type)
Sets type of this element.
void value(element_type new_type, T &&val, std::enable_if_t<!detail::is_valid_element_set_type< container_type, T >::value > *=nullptr)
Sets element_type, and value from a user-defined type.
bool operator()(boost::string_ref lhs, const basic_element< EContainer > &rhs) const
Functor call operator. Heterogeneous comparison.
void value(element_type new_type, T &&val, std::enable_if_t< detail::is_valid_element_set_type< container_type, T >::value > *=nullptr)
Sets element_type, and value from a detail::TypeMap compatible type.
Type trait to determine if a type is nothrow/noexcept swappable.
bool operator()(const basic_element< EContainer > &lhs, boost::string_ref rhs) const
Functor call operator. Heterogeneous comparison.
Helper class to implement basic_element::visit with detail::visit.
size_t size() const noexcept
Returns size in bytes.
void value(T &&val, std::enable_if_t< std::is_same< std::decay_t< T >, detail::ElementTypeMapSet< EType, container_type >>::value > *=nullptr)
Sets value. Statically ensures type compatibility.
Visitor for checking validity of a type for fetching.
auto operator()(Visitor &&visitor, Element &&elem) const
Functor call operator.
Exception thrown when an element's data size differs from that reported.
Exception type thrown when a call to get() has an incorrect type parameter.
basic_document> (document_type)
Wrapper for type validity checking visitors.
element_type type() const noexcept
Returns BSON type of this element.
Exception type thrown when an element has a type value not represented by element_type.
basic_element(std::string name, T &&val)
Construct an element with specified name and value.
basic_element(ForwardIterator &&first, ForwardIterator &&last, std::enable_if_t< !std::is_constructible< boost::string_ref, ForwardIterator >::value||std::is_convertible< ForwardIterator, typename container_type::const_iterator >::value > *=nullptr, std::enable_if_t< detail::is_range_of_same_value< decltype(boost::make_iterator_range(first, last)), typename Container::value_type >::value > *=nullptr)
Construct an element from raw BSON byte sequence.
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.
typename mpl::at< typename TypeMap< Container >::map_type, element_type_c< EType >>::type ElementTypeMap
Type alias to perform boost::mpl::at on TypeMap::map_type.
bool operator!=(const basic_element &other) const
Checks if this and other are not equal.
std::decay_t< Container > container_type
Underlying storage container.
void swap(basic_element &other) noexcept
Swaps contents with other.
Functor for basic_element comparison.
boost::string_ref name() const noexcept(std::is_nothrow_constructible< boost::string_ref, const std::string & >::value)
Returns name of element.