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_value_set.hpp
9  * \author Andrey Semashev
10  * \date   21.04.2007
11  *
12  * This header file contains definition of attribute value set. The set is constructed from
13  * three attribute sets (global, thread-specific and source-specific) and contains attribute
14  * values.
15  */
16 
17 #ifndef BOOST_LOG_ATTRIBUTE_VALUE_SET_HPP_INCLUDED_
18 #define BOOST_LOG_ATTRIBUTE_VALUE_SET_HPP_INCLUDED_
19 
20 #include <cstddef>
21 #include <utility>
22 #include <iterator>
23 #include <boost/move/core.hpp>
24 #include <boost/log/detail/config.hpp>
25 #include <boost/log/attributes/attribute_name.hpp>
26 #include <boost/log/attributes/attribute.hpp>
27 #include <boost/log/attributes/attribute_value.hpp>
28 #include <boost/log/attributes/attribute_set.hpp>
29 #include <boost/log/expressions/keyword_fwd.hpp>
30 #include <boost/log/detail/header.hpp>
31 
32 #ifdef BOOST_HAS_PRAGMA_ONCE
33 #pragma once
34 #endif
35 
36 namespace boost {
37 
38 BOOST_LOG_OPEN_NAMESPACE
39 
40 /*!
41  * \brief A set of attribute values
42  *
43  * The set of attribute values is an associative container with attribute name as a key and
44  * a pointer to attribute value object as a mapped type. This is a collection of elements with unique
45  * keys, that is, there can be only one attribute value with a given name in the set. With respect to
46  * read-only capabilities, the set interface is close to \c std::unordered_map.
47  *
48  * The set is designed to be only capable of adding elements to it. Once added, the attribute value
49  * cannot be removed from the set.
50  *
51  * An instance of attribute value set can be constructed from three attribute sets. The constructor attempts to
52  * accommodate values of all attributes from the sets. The situation when a same-named attribute is found
53  * in more than one attribute set is possible. This problem is solved on construction of the value set: the three
54  * attribute sets have different priorities when it comes to solving conflicts.
55  *
56  * From the library perspective the three source attribute sets are global, thread-specific and source-specific
57  * attributes, with the latter having the highest priority. This feature allows to override attributes of wider scopes
58  * with the more specific ones.
59  *
60  * For sake of performance, the attribute values are not immediately acquired from attribute sets at construction.
61  * Instead, on-demand acquisition is performed either on iterator dereferencing or on call to the \c freeze method.
62  * Once acquired, the attribute value stays within the set until its destruction. This nuance does not affect
63  * other set properties, such as size or lookup ability. The logging core automatically freezes the set
64  * at the right point, so users should not be bothered unless they manually create attribute value sets.
65  *
66  * \note The attribute sets that were used for the value set construction must not be modified or destroyed
67  *       until the value set is frozen. Otherwise the behavior is undefined.
68  */
69 class attribute_value_set
70 {
71     BOOST_COPYABLE_AND_MOVABLE_ALT(attribute_value_set)
72 
73 public:
74     //! Key type
75     typedef attribute_name key_type;
76     //! Mapped attribute type
77     typedef attribute_value mapped_type;
78 
79     //! Value type
80     typedef std::pair< const key_type, mapped_type > value_type;
81     //! Reference type
82     typedef value_type& reference;
83     //! Const reference type
84     typedef value_type const& const_reference;
85     //! Pointer type
86     typedef value_type* pointer;
87     //! Const pointer type
88     typedef value_type const* const_pointer;
89     //! Size type
90     typedef std::size_t size_type;
91     //! Pointer difference type
92     typedef std::ptrdiff_t difference_type;
93 
94 #ifndef BOOST_LOG_DOXYGEN_PASS
95 
96 private:
97     struct implementation;
98     friend struct implementation;
99 
100     //! A base class for the container nodes
101     struct node_base
102     {
103         node_base* m_pPrev;
104         node_base* m_pNext;
105 
106         node_base();
107 
108         BOOST_DELETED_FUNCTION(node_base(node_base const&))
109         BOOST_DELETED_FUNCTION(node_base& operator= (node_base const&))
110     };
111 
112     //! Container elements
113     struct node;
114     friend struct node;
115     struct node :
116         public node_base
117     {
118         value_type m_Value;
119         bool m_DynamicallyAllocated;
120 
121         node(key_type const& key, mapped_type& data, bool dynamic);
122     };
123 
124 public:
125     class const_iterator;
126     friend class const_iterator;
127     class const_iterator
128     {
129     public:
130         //  Standard typedefs
131         typedef attribute_value_set::difference_type difference_type;
132         typedef attribute_value_set::value_type value_type;
133         typedef attribute_value_set::const_reference reference;
134         typedef attribute_value_set::const_pointer pointer;
135         typedef std::bidirectional_iterator_tag iterator_category;
136 
137     public:
138         //  Constructors
const_iterator()139         BOOST_CONSTEXPR const_iterator() : m_pNode(NULL), m_pContainer(NULL) {}
const_iterator(node_base * n,attribute_value_set * cont)140         explicit const_iterator(node_base* n, attribute_value_set* cont) BOOST_NOEXCEPT :
141             m_pNode(n),
142             m_pContainer(cont)
143         {
144         }
145 
146         //  Comparison
operator ==(const_iterator const & that) const147         bool operator== (const_iterator const& that) const BOOST_NOEXCEPT
148         {
149             return (m_pNode == that.m_pNode);
150         }
operator !=(const_iterator const & that) const151         bool operator!= (const_iterator const& that) const BOOST_NOEXCEPT
152         {
153             return (m_pNode != that.m_pNode);
154         }
155 
156         //  Modification
operator ++()157         const_iterator& operator++ ()
158         {
159             m_pContainer->freeze();
160             m_pNode = m_pNode->m_pNext;
161             return *this;
162         }
operator --()163         const_iterator& operator-- ()
164         {
165             m_pContainer->freeze();
166             m_pNode = m_pNode->m_pPrev;
167             return *this;
168         }
operator ++(int)169         const_iterator operator++ (int)
170         {
171             const_iterator tmp(*this);
172             m_pContainer->freeze();
173             m_pNode = m_pNode->m_pNext;
174             return tmp;
175         }
operator --(int)176         const_iterator operator-- (int)
177         {
178             const_iterator tmp(*this);
179             m_pContainer->freeze();
180             m_pNode = m_pNode->m_pPrev;
181             return tmp;
182         }
183 
184         //  Dereferencing
operator ->() const185         pointer operator-> () const BOOST_NOEXCEPT { return &(static_cast< node* >(m_pNode)->m_Value); }
operator *() const186         reference operator* () const BOOST_NOEXCEPT { return static_cast< node* >(m_pNode)->m_Value; }
187 
188     private:
189         node_base* m_pNode;
190         attribute_value_set* m_pContainer;
191     };
192 
193 #else
194 
195     /*!
196      * Constant iterator type with bidirectional capabilities.
197      */
198     typedef implementation_defined const_iterator;
199 
200 #endif // BOOST_LOG_DOXYGEN_PASS
201 
202 private:
203     //! Pointer to the container implementation
204     implementation* m_pImpl;
205 
206 public:
207     /*!
208      * Default constructor
209      *
210      * The constructor creates an empty set which can be filled later by subsequent
211      * calls of \c insert method. Optionally, the amount of storage reserved for elements
212      * to be inserted may be passed to the constructor.
213      * The constructed set is frozen.
214      *
215      * \param reserve_count Number of elements to reserve space for.
216      */
217     BOOST_LOG_API explicit attribute_value_set(size_type reserve_count = 8);
218 
219     /*!
220      * Move constructor
221      */
attribute_value_set(BOOST_RV_REF (attribute_value_set)that)222     attribute_value_set(BOOST_RV_REF(attribute_value_set) that) BOOST_NOEXCEPT : m_pImpl(that.m_pImpl)
223     {
224         that.m_pImpl = NULL;
225     }
226 
227     /*!
228      * The constructor adopts three attribute sets into the value set.
229      * The \a source_attrs attributes have the greatest preference when a same-named
230      * attribute is found in several sets, \a global_attrs has the least.
231      * The constructed set is not frozen.
232      *
233      * \param source_attrs A set of source-specific attributes.
234      * \param thread_attrs A set of thread-specific attributes.
235      * \param global_attrs A set of global attributes.
236      * \param reserve_count Amount of elements to reserve space for, in addition to the elements in the three attribute sets provided.
237      */
238     BOOST_LOG_API attribute_value_set(
239         attribute_set const& source_attrs,
240         attribute_set const& thread_attrs,
241         attribute_set const& global_attrs,
242         size_type reserve_count = 8);
243 
244     /*!
245      * The constructor adopts three attribute sets into the value set.
246      * The \a source_attrs attributes have the greatest preference when a same-named
247      * attribute is found in several sets, \a global_attrs has the least.
248      * The constructed set is not frozen.
249      *
250      * \pre The \a source_attrs set is frozen.
251      *
252      * \param source_attrs A set of source-specific attributes.
253      * \param thread_attrs A set of thread-specific attributes.
254      * \param global_attrs A set of global attributes.
255      * \param reserve_count Amount of elements to reserve space for, in addition to the elements in the three attribute sets provided.
256      */
257     BOOST_LOG_API attribute_value_set(
258         attribute_value_set const& source_attrs,
259         attribute_set const& thread_attrs,
260         attribute_set const& global_attrs,
261         size_type reserve_count = 8);
262 
263     /*!
264      * The constructor adopts three attribute sets into the value set.
265      * The \a source_attrs attributes have the greatest preference when a same-named
266      * attribute is found in several sets, \a global_attrs has the least.
267      * The constructed set is not frozen.
268      *
269      * \pre The \a source_attrs set is frozen.
270      *
271      * \param source_attrs A set of source-specific attributes.
272      * \param thread_attrs A set of thread-specific attributes.
273      * \param global_attrs A set of global attributes.
274      * \param reserve_count Amount of elements to reserve space for, in addition to the elements in the three attribute sets provided.
275      */
attribute_value_set(BOOST_RV_REF (attribute_value_set)source_attrs,attribute_set const & thread_attrs,attribute_set const & global_attrs,size_type reserve_count=8)276     attribute_value_set(
277         BOOST_RV_REF(attribute_value_set) source_attrs,
278         attribute_set const& thread_attrs,
279         attribute_set const& global_attrs,
280         size_type reserve_count = 8) : m_pImpl(NULL)
281     {
282         construct(static_cast< attribute_value_set& >(source_attrs), thread_attrs, global_attrs, reserve_count);
283     }
284 
285     /*!
286      * Copy constructor.
287      *
288      * \pre The original set is frozen.
289      * \post The constructed set is frozen, <tt>std::equal(begin(), end(), that.begin()) == true</tt>
290      */
291     BOOST_LOG_API attribute_value_set(attribute_value_set const& that);
292 
293     /*!
294      * Destructor. Releases all referenced attribute values.
295      */
296     BOOST_LOG_API ~attribute_value_set() BOOST_NOEXCEPT;
297 
298     /*!
299      * Assignment operator
300      */
operator =(attribute_value_set that)301     attribute_value_set& operator= (attribute_value_set that) BOOST_NOEXCEPT
302     {
303         this->swap(that);
304         return *this;
305     }
306 
307     /*!
308      * Swaps two sets
309      *
310      * \b Throws: Nothing.
311      */
swap(attribute_value_set & that)312     void swap(attribute_value_set& that) BOOST_NOEXCEPT
313     {
314         implementation* const p = m_pImpl;
315         m_pImpl = that.m_pImpl;
316         that.m_pImpl = p;
317     }
318 
319     /*!
320      * \return Iterator to the first element of the set.
321      */
322     BOOST_LOG_API const_iterator begin() const;
323     /*!
324      * \return Iterator to the after-the-last element of the set.
325      */
326     BOOST_LOG_API const_iterator end() const;
327 
328     /*!
329      * \return Number of elements in the set.
330      */
331     BOOST_LOG_API size_type size() const;
332     /*!
333      * \return \c true if there are no elements in the container, \c false otherwise.
334      */
empty() const335     bool empty() const { return (this->size() == 0); }
336 
337     /*!
338      * The method finds the attribute value by name.
339      *
340      * \param key Attribute name.
341      * \return Iterator to the found element or \c end() if the attribute with such name is not found.
342      */
343     BOOST_LOG_API const_iterator find(key_type key) const;
344 
345     /*!
346      * Alternative lookup syntax.
347      *
348      * \param key Attribute name.
349      * \return A pointer to the attribute value if it is found with \a key, default-constructed mapped value otherwise.
350      */
operator [](key_type key) const351     mapped_type operator[] (key_type key) const
352     {
353         const_iterator it = this->find(key);
354         if (it != this->end())
355             return it->second;
356         else
357             return mapped_type();
358     }
359 
360     /*!
361      * Alternative lookup syntax.
362      *
363      * \param keyword Attribute keyword.
364      * \return A \c value_ref with extracted attribute value if it is found, empty \c value_ref otherwise.
365      */
366     template< typename DescriptorT, template< typename > class ActorT >
367     typename result_of::extract< typename expressions::attribute_keyword< DescriptorT, ActorT >::value_type, DescriptorT >::type
operator [](expressions::attribute_keyword<DescriptorT,ActorT> const & keyword) const368     operator[] (expressions::attribute_keyword< DescriptorT, ActorT > const& keyword) const
369     {
370         typedef typename expressions::attribute_keyword< DescriptorT, ActorT >::value_type attr_value_type;
371         typedef typename result_of::extract< attr_value_type, DescriptorT >::type result_type;
372         const_iterator it = this->find(keyword.get_name());
373         if (it != this->end())
374             return it->second.extract< attr_value_type, DescriptorT >();
375         else
376             return result_type();
377     }
378 
379     /*!
380      * The method counts the number of the attribute value occurrences in the set. Since there can be only one
381      * attribute value with a particular key, the method always return 0 or 1.
382      *
383      * \param key Attribute name.
384      * \return The number of times the attribute value is found in the container.
385      */
count(key_type key) const386     size_type count(key_type key) const { return size_type(this->find(key) != this->end()); }
387 
388     /*!
389      * The method acquires values of all adopted attributes.
390      *
391      * \post The set is frozen.
392      */
393     BOOST_LOG_API void freeze();
394 
395     /*!
396      * Inserts an element into the set. The complexity of the operation is amortized constant.
397      *
398      * \pre The set is frozen.
399      *
400      * \param key The attribute name.
401      * \param mapped The attribute value.
402      *
403      * \returns An iterator to the inserted element and \c true if insertion succeeded. Otherwise,
404      *          if the set already contains a same-named attribute value, iterator to the
405      *          existing element and \c false.
406      */
407     BOOST_LOG_API std::pair< const_iterator, bool > insert(key_type key, mapped_type const& mapped);
408 
409     /*!
410      * Inserts an element into the set. The complexity of the operation is amortized constant.
411      *
412      * \pre The set is frozen.
413      *
414      * \param value The attribute name and value.
415      *
416      * \returns An iterator to the inserted element and \c true if insertion succeeded. Otherwise,
417      *          if the set already contains a same-named attribute value, iterator to the
418      *          existing element and \c false.
419      */
insert(const_reference value)420     std::pair< const_iterator, bool > insert(const_reference value) { return this->insert(value.first, value.second); }
421 
422     /*!
423      * Mass insertion method. The complexity of the operation is linear to the number of elements inserted.
424      *
425      * \pre The set is frozen.
426      *
427      * \param begin A forward iterator that points to the first element to be inserted.
428      * \param end A forward iterator that points to the after-the-last element to be inserted.
429      */
430     template< typename FwdIteratorT >
insert(FwdIteratorT begin,FwdIteratorT end)431     void insert(FwdIteratorT begin, FwdIteratorT end)
432     {
433         for (; begin != end; ++begin)
434             this->insert(*begin);
435     }
436 
437     /*!
438      * Mass insertion method with ability to acquire iterators to the inserted elements.
439      * The complexity of the operation is linear to the number of elements inserted times the complexity
440      * of filling the \a out iterator.
441      *
442      * \pre The set is frozen.
443      *
444      * \param begin A forward iterator that points to the first element to be inserted.
445      * \param end A forward iterator that points to the after-the-last element to be inserted.
446      * \param out An output iterator that receives results of insertion of the elements.
447      */
448     template< typename FwdIteratorT, typename OutputIteratorT >
insert(FwdIteratorT begin,FwdIteratorT end,OutputIteratorT out)449     void insert(FwdIteratorT begin, FwdIteratorT end, OutputIteratorT out)
450     {
451         for (; begin != end; ++begin, ++out)
452             *out = this->insert(*begin);
453     }
454 
455 #ifndef BOOST_LOG_DOXYGEN_PASS
456 private:
457     //! Constructs the object by moving from \a source_attrs. This function is mostly needed to maintain ABI stable between C++03 and C++11.
458     BOOST_LOG_API void construct(
459         attribute_value_set& source_attrs,
460         attribute_set const& thread_attrs,
461         attribute_set const& global_attrs,
462         size_type reserve_count);
463 #endif // BOOST_LOG_DOXYGEN_PASS
464 };
465 
466 /*!
467  * Free swap overload
468  */
swap(attribute_value_set & left,attribute_value_set & right)469 inline void swap(attribute_value_set& left, attribute_value_set& right) BOOST_NOEXCEPT
470 {
471     left.swap(right);
472 }
473 
474 BOOST_LOG_CLOSE_NAMESPACE // namespace log
475 
476 } // namespace boost
477 
478 #include <boost/log/detail/footer.hpp>
479 
480 #endif // BOOST_LOG_ATTRIBUTE_VALUE_SET_HPP_INCLUDED_
481