| 1 | /* |
| 2 | * Copyright Andrey Semashev 2007 - 2015. |
| 3 | * Distributed under the Boost Software License, Version 1.0. |
| 4 | * (See accompanying file LICENSE_1_0.txt or copy at |
| 5 | * http://www.boost.org/LICENSE_1_0.txt) |
| 6 | */ |
| 7 | /*! |
| 8 | * \file attribute_set.hpp |
| 9 | * \author Andrey Semashev |
| 10 | * \date 08.03.2007 |
| 11 | * |
| 12 | * This header contains definition of the attribute set container. |
| 13 | */ |
| 14 | |
| 15 | #ifndef BOOST_LOG_ATTRIBUTE_SET_HPP_INCLUDED_ |
| 16 | #define BOOST_LOG_ATTRIBUTE_SET_HPP_INCLUDED_ |
| 17 | |
| 18 | #include <cstddef> |
| 19 | #include <utility> |
| 20 | #include <iterator> |
| 21 | #include <boost/move/core.hpp> |
| 22 | #include <boost/core/enable_if.hpp> |
| 23 | #include <boost/type_traits/conditional.hpp> |
| 24 | #include <boost/log/detail/config.hpp> |
| 25 | #include <boost/log/detail/sfinae_tools.hpp> |
| 26 | #include <boost/log/attributes/attribute_name.hpp> |
| 27 | #include <boost/log/attributes/attribute.hpp> |
| 28 | #include <boost/log/detail/header.hpp> |
| 29 | |
| 30 | #ifdef BOOST_HAS_PRAGMA_ONCE |
| 31 | #pragma once |
| 32 | #endif |
| 33 | |
| 34 | namespace boost { |
| 35 | |
| 36 | BOOST_LOG_OPEN_NAMESPACE |
| 37 | |
| 38 | class attribute_set; |
| 39 | class attribute_value_set; |
| 40 | |
| 41 | namespace aux { |
| 42 | |
| 43 | //! Reference proxy object to implement \c operator[] |
| 44 | class attribute_set_reference_proxy |
| 45 | { |
| 46 | private: |
| 47 | //! Key type |
| 48 | typedef attribute_name key_type; |
| 49 | //! Mapped attribute type |
| 50 | typedef attribute mapped_type; |
| 51 | |
| 52 | private: |
| 53 | attribute_set* const m_pContainer; |
| 54 | const key_type m_key; |
| 55 | |
| 56 | public: |
| 57 | //! Constructor |
| 58 | explicit attribute_set_reference_proxy(attribute_set* pContainer, key_type const& key) BOOST_NOEXCEPT : |
| 59 | m_pContainer(pContainer), |
| 60 | m_key(key) |
| 61 | { |
| 62 | } |
| 63 | |
| 64 | //! Conversion operator (would be invoked in case of reading from the container) |
| 65 | BOOST_FORCEINLINE operator mapped_type() const BOOST_NOEXCEPT |
| 66 | { |
| 67 | return read_mapped_value(); |
| 68 | } |
| 69 | //! Assignment operator (would be invoked in case of writing to the container) |
| 70 | mapped_type& operator= (mapped_type const& val) const; |
| 71 | |
| 72 | private: |
| 73 | //! Reads the referenced mapped value from the container |
| 74 | mapped_type read_mapped_value() const BOOST_NOEXCEPT; |
| 75 | }; |
| 76 | |
| 77 | } // namespace aux |
| 78 | |
| 79 | /*! |
| 80 | * \brief An attribute set class. |
| 81 | * |
| 82 | * An attribute set is an associative container with attribute name as a key and |
| 83 | * pointer to the attribute as a mapped value. The container allows storing only one element for each distinct |
| 84 | * key value. In most regards attribute set container provides interface similar to \c std::unordered_map. |
| 85 | * However, there are differences in \c operator[] semantics and a number of optimizations with regard to iteration. |
| 86 | * Besides, attribute names are stored as a read-only <tt>attribute_name</tt>'s instead of \c std::string, |
| 87 | * which saves memory and CPU time. |
| 88 | */ |
| 89 | class attribute_set |
| 90 | { |
| 91 | BOOST_COPYABLE_AND_MOVABLE_ALT(attribute_set) |
| 92 | |
| 93 | friend class attribute_value_set; |
| 94 | friend class aux::attribute_set_reference_proxy; |
| 95 | |
| 96 | public: |
| 97 | //! Key type |
| 98 | typedef attribute_name key_type; |
| 99 | //! Mapped attribute type |
| 100 | typedef attribute mapped_type; |
| 101 | |
| 102 | //! Value type |
| 103 | typedef std::pair< const key_type, mapped_type > value_type; |
| 104 | //! Reference type |
| 105 | typedef value_type& reference; |
| 106 | //! Const reference type |
| 107 | typedef value_type const& const_reference; |
| 108 | //! Pointer type |
| 109 | typedef value_type* pointer; |
| 110 | //! Const pointer type |
| 111 | typedef value_type const* const_pointer; |
| 112 | //! Size type |
| 113 | typedef std::size_t size_type; |
| 114 | //! Difference type |
| 115 | typedef std::ptrdiff_t difference_type; |
| 116 | |
| 117 | private: |
| 118 | //! \cond |
| 119 | |
| 120 | //! Implementation |
| 121 | struct implementation; |
| 122 | friend struct implementation; |
| 123 | |
| 124 | //! A base class for the container nodes |
| 125 | struct node_base |
| 126 | { |
| 127 | node_base* m_pPrev; |
| 128 | node_base* m_pNext; |
| 129 | |
| 130 | node_base(); |
| 131 | |
| 132 | BOOST_DELETED_FUNCTION(node_base(node_base const&)) |
| 133 | BOOST_DELETED_FUNCTION(node_base& operator= (node_base const&)) |
| 134 | }; |
| 135 | |
| 136 | //! Container elements |
| 137 | struct node; |
| 138 | friend struct node; |
| 139 | struct node : |
| 140 | public node_base |
| 141 | { |
| 142 | value_type m_Value; |
| 143 | |
| 144 | node(key_type const& key, mapped_type const& data); |
| 145 | }; |
| 146 | |
| 147 | //! Iterator class |
| 148 | #ifndef BOOST_LOG_NO_MEMBER_TEMPLATE_FRIENDS |
| 149 | template< bool fConstV > class iter; |
| 150 | template< bool fConstV > friend class iter; |
| 151 | #endif |
| 152 | template< bool fConstV > |
| 153 | class iter |
| 154 | { |
| 155 | friend class iter< !fConstV >; |
| 156 | friend class attribute_set; |
| 157 | |
| 158 | public: |
| 159 | // Standard typedefs |
| 160 | typedef attribute_set::difference_type difference_type; |
| 161 | typedef attribute_set::value_type value_type; |
| 162 | typedef typename boost::conditional< |
| 163 | fConstV, |
| 164 | attribute_set::const_reference, |
| 165 | attribute_set::reference |
| 166 | >::type reference; |
| 167 | typedef typename boost::conditional< |
| 168 | fConstV, |
| 169 | attribute_set::const_pointer, |
| 170 | attribute_set::pointer |
| 171 | >::type pointer; |
| 172 | typedef std::bidirectional_iterator_tag iterator_category; |
| 173 | |
| 174 | public: |
| 175 | // Constructors |
| 176 | BOOST_CONSTEXPR iter() BOOST_NOEXCEPT : m_pNode(NULL) {} |
| 177 | explicit iter(node_base* pNode) BOOST_NOEXCEPT : m_pNode(pNode) {} |
| 178 | #if !defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS) |
| 179 | template< bool fOtherConstV, typename = typename boost::enable_if_c< !fOtherConstV && fOtherConstV != fConstV >::type > |
| 180 | iter(iter< fOtherConstV > const& that) BOOST_NOEXCEPT : m_pNode(that.m_pNode) {} |
| 181 | #else |
| 182 | template< bool fOtherConstV > |
| 183 | iter(iter< fOtherConstV > const& that, typename boost::enable_if_c< !fOtherConstV && fOtherConstV != fConstV, boost::log::aux::sfinae_dummy >::type = boost::log::aux::sfinae_dummy()) BOOST_NOEXCEPT : |
| 184 | m_pNode(that.m_pNode) |
| 185 | { |
| 186 | } |
| 187 | #endif |
| 188 | |
| 189 | //! Assignment |
| 190 | template< bool fOtherConstV > |
| 191 | typename boost::enable_if_c< !fOtherConstV && fOtherConstV != fConstV, iter& >::type operator= (iter< fOtherConstV > const& that) BOOST_NOEXCEPT |
| 192 | { |
| 193 | m_pNode = that.m_pNode; |
| 194 | return *this; |
| 195 | } |
| 196 | |
| 197 | // Comparison |
| 198 | template< bool fOtherConstV > |
| 199 | typename boost::enable_if_c< !fOtherConstV || fOtherConstV == fConstV, bool >::type operator== (iter< fOtherConstV > const& that) const BOOST_NOEXCEPT { return (m_pNode == that.m_pNode); } |
| 200 | template< bool fOtherConstV > |
| 201 | typename boost::enable_if_c< !fOtherConstV || fOtherConstV == fConstV, bool >::type operator!= (iter< fOtherConstV > const& that) const BOOST_NOEXCEPT { return (m_pNode != that.m_pNode); } |
| 202 | |
| 203 | // Modification |
| 204 | iter& operator++ () BOOST_NOEXCEPT |
| 205 | { |
| 206 | m_pNode = m_pNode->m_pNext; |
| 207 | return *this; |
| 208 | } |
| 209 | iter& operator-- () BOOST_NOEXCEPT |
| 210 | { |
| 211 | m_pNode = m_pNode->m_pPrev; |
| 212 | return *this; |
| 213 | } |
| 214 | iter operator++ (int) BOOST_NOEXCEPT |
| 215 | { |
| 216 | iter tmp(*this); |
| 217 | m_pNode = m_pNode->m_pNext; |
| 218 | return tmp; |
| 219 | } |
| 220 | iter operator-- (int) BOOST_NOEXCEPT |
| 221 | { |
| 222 | iter tmp(*this); |
| 223 | m_pNode = m_pNode->m_pPrev; |
| 224 | return tmp; |
| 225 | } |
| 226 | |
| 227 | // Dereferencing |
| 228 | pointer operator-> () const BOOST_NOEXCEPT { return &(static_cast< node* >(m_pNode)->m_Value); } |
| 229 | reference operator* () const BOOST_NOEXCEPT { return static_cast< node* >(m_pNode)->m_Value; } |
| 230 | |
| 231 | node_base* base() const BOOST_NOEXCEPT { return m_pNode; } |
| 232 | |
| 233 | private: |
| 234 | node_base* m_pNode; |
| 235 | }; |
| 236 | |
| 237 | //! \endcond |
| 238 | |
| 239 | public: |
| 240 | #ifndef BOOST_LOG_DOXYGEN_PASS |
| 241 | //! Iterator type |
| 242 | typedef iter< false > iterator; |
| 243 | //! Const iterator type |
| 244 | typedef iter< true > const_iterator; |
| 245 | #else |
| 246 | /*! |
| 247 | * Iterator type. The iterator complies to the bidirectional iterator requirements. |
| 248 | */ |
| 249 | typedef implementation_defined iterator; |
| 250 | /*! |
| 251 | * Constant iterator type. The iterator complies to the bidirectional iterator requirements with read-only capabilities. |
| 252 | */ |
| 253 | typedef implementation_defined const_iterator; |
| 254 | #endif // BOOST_LOG_DOXYGEN_PASS |
| 255 | |
| 256 | private: |
| 257 | //! Pointer to implementation |
| 258 | implementation* m_pImpl; |
| 259 | |
| 260 | public: |
| 261 | /*! |
| 262 | * Default constructor. |
| 263 | * |
| 264 | * \post <tt>empty() == true</tt> |
| 265 | */ |
| 266 | BOOST_LOG_API attribute_set(); |
| 267 | |
| 268 | /*! |
| 269 | * Copy constructor. |
| 270 | * |
| 271 | * \post <tt>size() == that.size() && std::equal(begin(), end(), that.begin()) == true</tt> |
| 272 | */ |
| 273 | BOOST_LOG_API attribute_set(attribute_set const& that); |
| 274 | |
| 275 | /*! |
| 276 | * Move constructor |
| 277 | */ |
| 278 | attribute_set(BOOST_RV_REF(attribute_set) that) BOOST_NOEXCEPT : m_pImpl(that.m_pImpl) |
| 279 | { |
| 280 | that.m_pImpl = NULL; |
| 281 | } |
| 282 | |
| 283 | /*! |
| 284 | * Destructor. All stored references to attributes are released. |
| 285 | */ |
| 286 | BOOST_LOG_API ~attribute_set() BOOST_NOEXCEPT; |
| 287 | |
| 288 | /*! |
| 289 | * Copy assignment operator. |
| 290 | * |
| 291 | * \post <tt>size() == that.size() && std::equal(begin(), end(), that.begin()) == true</tt> |
| 292 | */ |
| 293 | attribute_set& operator= (attribute_set that) BOOST_NOEXCEPT |
| 294 | { |
| 295 | this->swap(that); |
| 296 | return *this; |
| 297 | } |
| 298 | |
| 299 | /*! |
| 300 | * Swaps two instances of the container. |
| 301 | * |
| 302 | * \b Throws: Nothing. |
| 303 | */ |
| 304 | void swap(attribute_set& that) BOOST_NOEXCEPT |
| 305 | { |
| 306 | implementation* const p = m_pImpl; |
| 307 | m_pImpl = that.m_pImpl; |
| 308 | that.m_pImpl = p; |
| 309 | } |
| 310 | |
| 311 | /*! |
| 312 | * \return Iterator to the first element of the container. |
| 313 | */ |
| 314 | BOOST_LOG_API iterator begin() BOOST_NOEXCEPT; |
| 315 | /*! |
| 316 | * \return Iterator to the after-the-last element of the container. |
| 317 | */ |
| 318 | BOOST_LOG_API iterator end() BOOST_NOEXCEPT; |
| 319 | /*! |
| 320 | * \return Constant iterator to the first element of the container. |
| 321 | */ |
| 322 | BOOST_LOG_API const_iterator begin() const BOOST_NOEXCEPT; |
| 323 | /*! |
| 324 | * \return Constant iterator to the after-the-last element of the container. |
| 325 | */ |
| 326 | BOOST_LOG_API const_iterator end() const BOOST_NOEXCEPT; |
| 327 | |
| 328 | /*! |
| 329 | * \return Number of elements in the container. |
| 330 | */ |
| 331 | BOOST_LOG_API size_type size() const BOOST_NOEXCEPT; |
| 332 | /*! |
| 333 | * \return true if there are no elements in the container, false otherwise. |
| 334 | */ |
| 335 | bool empty() const BOOST_NOEXCEPT { return (this->size() == 0); } |
| 336 | |
| 337 | /*! |
| 338 | * The method finds the attribute by name. |
| 339 | * |
| 340 | * \param key Attribute name. |
| 341 | * \return Iterator to the found element or end() if the attribute with such name is not found. |
| 342 | */ |
| 343 | BOOST_LOG_API iterator find(key_type key) BOOST_NOEXCEPT; |
| 344 | /*! |
| 345 | * The method finds the attribute by name. |
| 346 | * |
| 347 | * \param key Attribute name. |
| 348 | * \return Iterator to the found element or \c end() if the attribute with such name is not found. |
| 349 | */ |
| 350 | const_iterator find(key_type key) const BOOST_NOEXCEPT |
| 351 | { |
| 352 | return const_iterator(const_cast< attribute_set* >(this)->find(key)); |
| 353 | } |
| 354 | /*! |
| 355 | * The method counts the number of the attribute occurrences in the container. Since there can be only one |
| 356 | * attribute with a particular key, the method always return 0 or 1. |
| 357 | * |
| 358 | * \param key Attribute name. |
| 359 | * \return The number of times the attribute is found in the container. |
| 360 | */ |
| 361 | size_type count(key_type key) const BOOST_NOEXCEPT { return size_type(this->find(key) != this->end()); } |
| 362 | |
| 363 | /*! |
| 364 | * Combined lookup/insertion operator. The operator semantics depends on the further usage of the returned reference. |
| 365 | * \li If the reference is used as an assignment target, the assignment expression is equivalent to element insertion, |
| 366 | * where the element is composed of the second argument of the \c operator[] as a key and the second argument of assignment |
| 367 | * as a mapped value. |
| 368 | * \li If the returned reference is used in context where a conversion to the mapped type is required, |
| 369 | * the result of the conversion is equivalent to the mapped value found with the second argument of the \c operator[] as a key, |
| 370 | * if such an element exists in the container, or a default-constructed mapped value, if an element does not exist in the |
| 371 | * container. |
| 372 | * |
| 373 | * \param key Attribute name. |
| 374 | * \return A smart reference object of unspecified type. |
| 375 | */ |
| 376 | aux::attribute_set_reference_proxy operator[] (key_type key) BOOST_NOEXCEPT |
| 377 | { |
| 378 | return aux::attribute_set_reference_proxy(this, key); |
| 379 | } |
| 380 | /*! |
| 381 | * Lookup operator |
| 382 | * |
| 383 | * \param key Attribute name. |
| 384 | * \return If an element with the corresponding attribute name is found in the container, its mapped value |
| 385 | * is returned. Otherwise a default-constructed mapped value is returned. |
| 386 | */ |
| 387 | mapped_type operator[] (key_type key) const BOOST_NOEXCEPT |
| 388 | { |
| 389 | const_iterator it = this->find(key); |
| 390 | if (it != end()) |
| 391 | return it->second; |
| 392 | else |
| 393 | return mapped_type(); |
| 394 | } |
| 395 | |
| 396 | /*! |
| 397 | * Insertion method |
| 398 | * |
| 399 | * \param key Attribute name. |
| 400 | * \param data Pointer to the attribute. Must not be NULL. |
| 401 | * \returns A pair of values. If second is true, the insertion succeeded and the first component points to the |
| 402 | * inserted element. Otherwise the first component points to the element that prevents insertion. |
| 403 | */ |
| 404 | BOOST_LOG_API std::pair< iterator, bool > insert(key_type key, mapped_type const& data); |
| 405 | |
| 406 | /*! |
| 407 | * Insertion method |
| 408 | * |
| 409 | * \param value An element to be inserted. |
| 410 | * \returns A pair of values. If second is true, the insertion succeeded and the first component points to the |
| 411 | * inserted element. Otherwise the first component points to the element that prevents insertion. |
| 412 | */ |
| 413 | std::pair< iterator, bool > insert(const_reference value) |
| 414 | { |
| 415 | return this->insert(key: value.first, data: value.second); |
| 416 | } |
| 417 | |
| 418 | /*! |
| 419 | * Mass insertion method. |
| 420 | * |
| 421 | * \param begin A forward iterator that points to the first element to be inserted. |
| 422 | * \param end A forward iterator that points to the after-the-last element to be inserted. |
| 423 | */ |
| 424 | template< typename FwdIteratorT > |
| 425 | void insert(FwdIteratorT begin, FwdIteratorT end) |
| 426 | { |
| 427 | for (; begin != end; ++begin) |
| 428 | this->insert(*begin); |
| 429 | } |
| 430 | |
| 431 | /*! |
| 432 | * Mass insertion method with ability to acquire iterators to the inserted elements. |
| 433 | * |
| 434 | * \param begin A forward iterator that points to the first element to be inserted. |
| 435 | * \param end A forward iterator that points to the after-the-last element to be inserted. |
| 436 | * \param out An output iterator that receives results of insertion of the elements |
| 437 | */ |
| 438 | template< typename FwdIteratorT, typename OutputIteratorT > |
| 439 | void insert(FwdIteratorT begin, FwdIteratorT end, OutputIteratorT out) |
| 440 | { |
| 441 | for (; begin != end; ++begin, ++out) |
| 442 | *out = this->insert(*begin); |
| 443 | } |
| 444 | |
| 445 | /*! |
| 446 | * The method erases all attributes with the specified name |
| 447 | * |
| 448 | * \post All iterators to the erased elements become invalid. |
| 449 | * \param key Attribute name. |
| 450 | * \return Tne number of erased elements |
| 451 | */ |
| 452 | BOOST_LOG_API size_type erase(key_type key) BOOST_NOEXCEPT; |
| 453 | /*! |
| 454 | * The method erases the specified attribute |
| 455 | * |
| 456 | * \post All iterators to the erased element become invalid. |
| 457 | * \param it A valid iterator to the element to be erased. |
| 458 | * \return Tne number of erased elements |
| 459 | */ |
| 460 | BOOST_LOG_API void erase(iterator it) BOOST_NOEXCEPT; |
| 461 | /*! |
| 462 | * The method erases all attributes within the specified range |
| 463 | * |
| 464 | * \pre \a end is reachable from \a begin with a finite number of increments. |
| 465 | * \post All iterators to the erased elements become invalid. |
| 466 | * \param begin An iterator that points to the first element to be erased. |
| 467 | * \param end An iterator that points to the after-the-last element to be erased. |
| 468 | */ |
| 469 | BOOST_LOG_API void erase(iterator begin, iterator end) BOOST_NOEXCEPT; |
| 470 | |
| 471 | /*! |
| 472 | * The method removes all elements from the container |
| 473 | * |
| 474 | * \post <tt>empty() == true</tt> |
| 475 | */ |
| 476 | BOOST_LOG_API void clear() BOOST_NOEXCEPT; |
| 477 | }; |
| 478 | |
| 479 | /*! |
| 480 | * Free swap overload |
| 481 | */ |
| 482 | inline void swap(attribute_set& left, attribute_set& right) BOOST_NOEXCEPT |
| 483 | { |
| 484 | left.swap(that&: right); |
| 485 | } |
| 486 | |
| 487 | namespace aux { |
| 488 | |
| 489 | //! Reads the referenced mapped value from the container |
| 490 | inline attribute_set_reference_proxy::mapped_type attribute_set_reference_proxy::read_mapped_value() const BOOST_NOEXCEPT |
| 491 | { |
| 492 | attribute_set::iterator it = m_pContainer->find(key: m_key); |
| 493 | if (it != m_pContainer->end()) |
| 494 | return it->second; |
| 495 | else |
| 496 | return mapped_type(); |
| 497 | } |
| 498 | |
| 499 | //! Assignment operator (would be invoked in case of writing to the container) |
| 500 | inline attribute_set_reference_proxy::mapped_type& attribute_set_reference_proxy::operator= (mapped_type const& val) const |
| 501 | { |
| 502 | std::pair< attribute_set::iterator, bool > res = m_pContainer->insert(key: m_key, data: val); |
| 503 | if (!res.second) |
| 504 | res.first->second = val; |
| 505 | return res.first->second; |
| 506 | } |
| 507 | |
| 508 | } // namespace aux |
| 509 | |
| 510 | #ifndef BOOST_LOG_DOXYGEN_PASS |
| 511 | inline attribute& attribute::operator= (aux::attribute_set_reference_proxy const& that) BOOST_NOEXCEPT |
| 512 | { |
| 513 | attribute attr = that; |
| 514 | this->swap(that&: attr); |
| 515 | return *this; |
| 516 | } |
| 517 | #endif |
| 518 | |
| 519 | BOOST_LOG_CLOSE_NAMESPACE // namespace log |
| 520 | |
| 521 | } // namespace boost |
| 522 | |
| 523 | #include <boost/log/detail/footer.hpp> |
| 524 | |
| 525 | #endif // BOOST_LOG_ATTRIBUTE_SET_HPP_INCLUDED_ |
| 526 | |