1 // format.hpp
2 
3 // Boost Logging library
4 //
5 // Author: John Torjo, www.torjo.com
6 //
7 // Copyright (C) 2007 John Torjo (see www.torjo.com for email)
8 //
9 // Distributed under the Boost Software License, Version 1.0.
10 //    (See accompanying file LICENSE_1_0.txt or copy at
11 //          http://www.boost.org/LICENSE_1_0.txt)
12 //
13 // See http://www.boost.org for updates, documentation, and revision history.
14 // See http://www.torjo.com/log2/ for more details
15 
16 // this is fixed!
17 #ifndef JT28092007_format_HPP_DEFINED
18 #define JT28092007_format_HPP_DEFINED
19 
20 #include <hpx/util/assert.hpp>
21 #include <hpx/util/logging/detail/fwd.hpp>
22 #include <hpx/util/logging/detail/manipulator.hpp>
23 #include <hpx/util/logging/format/array.hpp>
24 #include <hpx/util/logging/format/op_equal.hpp>
25 #include <hpx/util/logging/format_fwd.hpp>
26 
27 #include <memory>
28 #include <set>
29 #include <string>
30 #include <type_traits>
31 #include <vector>
32 
33 namespace hpx { namespace util { namespace logging {
34 
35 /**
36 @file hpx/util/logging/format.hpp
37 
38 Include this file when you're using @ref manipulator "formatters and destinations",
39 and you want to define the logger classes, in a source file
40 (using HPX_DEFINE_LOG)
41 
42 */
43 
44     ///////////////////////////////////////////////////////////////////////////
45     // Format and write
46     //
47 
48     /**
49         @brief The @c %format_and_write classes know how to call
50         the formatter and destination @c objects.
51 
52         Usually you'll be happy with the
53         format_and_write::simple class - which simply calls @c
54         operator() on the formatters , and @c operator() on the destinations.
55 
56         Note that usually the formatter and destination class just have an @c operator(),
57         which when called, formats the message
58         or writes it to a destination. In case your formatters/destinations are
59         more complex than that (for instance, more than
60         a member function needs to be called),
61         you'll have to implement your own %format_and_write class.
62     */
63     namespace format_and_write {
64 
65 
66     /**
67         @brief Formats the message, and writes it to destinations
68         - calls @c operator() on the formatters , and @c operator() on the destinations.
69         Ignores @c clear_format() commands.
70 
71         @param msg_type The message to pass to the formatter. This is the
72         type that is passed to the formatter objects and to the destination objects.
73         Thus, it needs to be convertible to the argument to be sent to the
74         formatter objects and to the argument to be sent to the destination objects.
75         Usually, it's the argument you pass on to your destination classes.
76 
77         If you derive from @c destination::base, this type can be
78         @c destination::base::raw_param (see below).
79 
80         Example:
81 
82         @code
83         typedef destination::base<const std::string &> dest_base;
84         // in this case : msg_type = std::string = dest_base::raw_param
85         struct write_to_cout : dest_base {
86             void operator()(param msg) const {
87                 std::cout << msg ;
88             }
89         };
90 
91 
92         typedef destination::base<const std::string &> dest_base;
93         // in this case : msg_type = cache_string = dest_base::raw_param
94         struct write_to_file : dest_base, ... {
95             void operator()(param msg) const {
96                 context() << msg ;
97             }
98         };
99 
100         @endcode
101     */
102     struct simple {
simplehpx::util::logging::format_and_write::simple103         simple ( msg_type & msg) : m_msg(msg) {}
104 
formathpx::util::logging::format_and_write::simple105         template<class formatter_ptr> void format(const formatter_ptr & fmt) {
106             (*fmt)(m_msg);
107         }
writehpx::util::logging::format_and_write::simple108         template<class destination_ptr> void write(const destination_ptr & dest) {
109             (*dest)(m_msg);
110         }
clear_formathpx::util::logging::format_and_write::simple111         void clear_format() {}
112     protected:
113         msg_type &m_msg;
114     };
115 
116     } // namespace format_and_write
117 
118 
119 
120     ///////////////////////////////////////////////////////////////////////////
121     // Message routing
122     //
123 
124     /**
125     @brief Specifies the route : how formatting and writing to destinations take place.
126 
127     Classes in this namespace specify when formatters and destinations are to be called.
128 
129     @sa msg_route::simple, msg_route::with_route
130 
131     */
132     namespace msg_route {
133 
134     /**
135         @brief Recomended base class for message routers that
136         need access to the underlying formatter and/or destination array.
137     */
138     template<class formatter_array, class destination_array>
139     struct formatter_and_destination_array_holder {
140     protected:
formatter_and_destination_array_holderhpx::util::logging::msg_route::formatter_and_destination_array_holder141         formatter_and_destination_array_holder (const formatter_array & formats_,
142             const destination_array & destinations_)
143             : m_formats(formats_), m_destinations(destinations_) {}
144 
formatshpx::util::logging::msg_route::formatter_and_destination_array_holder145         const formatter_array & formats() const             { return m_formats; }
destinationshpx::util::logging::msg_route::formatter_and_destination_array_holder146         const destination_array & destinations() const      { return m_destinations; }
147 
148     private:
149         const formatter_array & m_formats;
150         const destination_array & m_destinations;
151     };
152 
153 /**
154 @brief Represents a simple router - first calls all formatters
155 - in the order they were added, then all destinations - in the order they were added
156 
157 Example:
158 
159 @code
160 typedef logger< format_write<...> > logger_type;
161 HPX_DEFINE_LOG_FILTER(g_log_filter, filter::no_ts )
162 HPX_DEFINE_LOG(g_l, logger_type)
163 #define L_ HPX_LOG_USE_LOG_IF_FILTER(g_l(), g_log_filter()->is_enabled() )
164 
165 // add formatters : [idx] [time] message [enter]
166 g_l()->writer().add_formatter( write_idx() );
167 g_l()->writer().add_formatter( write_time() );
168 g_l()->writer().add_formatter( append_newline() );
169 
170 // write to cout and file
171 g_l()->writer().add_destination( write_to_cout() );
172 g_l()->writer().add_destination( write_to_file("out.txt") );
173 
174 // usage
175 int i = 1;
176 L_ << "testing " << i << i+1 << i+2;
177 @endcode
178 
179 In the above case:
180 - First, the formatters are called: @c write_idx() is called, then @c write_time(),
181 then @c append_newline().
182 - Then, the destinations are called: @c write_to_cout(), and then @c write_to_file().
183 
184 
185 
186 @param format_base The base class for all formatter classes from your application.
187 See manipulator.
188 
189 @param destination_base The base class for all destination classes from your application.
190 See manipulator.
191 
192     */
193     template<
194             class formatter_base,
195             class destination_base >
196     struct simple {
197         typedef typename formatter_base::ptr_type formatter_ptr;
198         typedef typename destination_base::ptr_type destination_ptr;
199 
200         typedef std::vector<formatter_ptr> f_array;
201         typedef std::vector<destination_ptr> d_array;
202         struct write_info {
203             f_array formats;
204             d_array destinations;
205         };
206 
207         template<class formatter_array, class destination_array>
simplehpx::util::logging::msg_route::simple208         simple(const formatter_array&, const destination_array&) {}
209 
append_formatterhpx::util::logging::msg_route::simple210         void append_formatter(formatter_ptr fmt) {
211             m_to_write.formats.push_back(fmt);
212         }
del_formatterhpx::util::logging::msg_route::simple213         void del_formatter(formatter_ptr fmt) {
214             typename f_array::iterator del = std::remove(m_to_write.formats.begin(),
215                 m_to_write.formats.end(), fmt);
216             m_to_write.formats.erase(del, m_to_write.formats.end());
217         }
218 
append_destinationhpx::util::logging::msg_route::simple219         void append_destination(destination_ptr dest) {
220             m_to_write.destinations.push_back(dest);
221         }
222 
del_destinationhpx::util::logging::msg_route::simple223         void del_destination(destination_ptr dest) {
224             typename d_array::iterator del =
225                 std::remove(m_to_write.destinations.begin(),
226                     m_to_write.destinations.end(), dest);
227             m_to_write.destinations.erase(del, m_to_write.destinations.end());
228         }
229 
writehpx::util::logging::msg_route::simple230         template<class format_and_write> void write(msg_type & msg) const {
231             format_and_write m(msg);
232 
233             for ( typename f_array::const_iterator b_f = m_to_write.formats.begin(),
234                 e_f = m_to_write.formats.end(); b_f != e_f; ++b_f)
235                 m.format(*b_f);
236 
237             for ( typename d_array::const_iterator b_d = m_to_write.destinations.begin(),
238                 e_d = m_to_write.destinations.end(); b_d != e_d; ++b_d)
239                 m.write(*b_d);
240         }
241 
242     private:
243         write_info m_to_write;
244     };
245 
246 
247     /**
248     @brief. Represents a router - by default, first calls all formatters,
249     then all destinations. However you can overwrite this route
250 
251     You can append a route - with append_route(),
252     or set the route with set_route().
253 
254     Example:
255 
256     @code
257     typedef logger< default_,
258         writer::format_write< format_base, destination_base,
259         format_and_write::simple,
260             msg_route::with_route<format_base,destination_base> > > logger_type;
261     logger_type g_l();
262 
263     g_l()->writer().router().set_route()
264         .fmt( formatter::time() )
265         .fmt( formatter::append_newline() )
266         .dest( destination::dbg_window() )
267         .fmt( formatter::write_idx() )
268         .dest( destination::cout() )
269         .clear()
270         .fmt( formatter::write_idx() )
271         .fmt( formatter::append_newline() )
272         .fmt( formatter::write_to_file())
273         ;
274     @endcode
275 
276     @param format_base The base class for all
277     formatter classes from your application. See manipulator.
278 
279     @param destination_base The base class for all
280     destination classes from your application. See manipulator.
281 
282     @remarks In the router - we don't own the objects - the array holder does that
283     */
284     template<
285             class formatter_base,
286             class destination_base,
287             // note: we're counting on these defaults in format_find_writer
288             class formatter_array =
289                 hpx::util::logging::array::shared_ptr_holder<formatter_base>,
290             class destination_array =
291                 hpx::util::logging::array::shared_ptr_holder<destination_base>
292     >
293     class with_route  : protected formatter_and_destination_array_holder<formatter_array,
294         destination_array> {
295         typedef typename formatter_base::ptr_type formatter_ptr;
296         typedef typename destination_base::ptr_type destination_ptr;
297 
298         typedef formatter_and_destination_array_holder<formatter_array,
299             destination_array> holder_base_type;
300 
301         typedef with_route<formatter_base, destination_base, formatter_array,
302             destination_array> self_type;
303 
304         typedef std::vector<formatter_ptr> f_array;
305         typedef std::vector<destination_ptr> d_array;
306 
307         struct write_once {
write_oncehpx::util::logging::msg_route::with_route::write_once308             write_once() : do_clear_afterwards(false) {}
309             f_array formats;
310             d_array destinations;
311             // if true, will execute clear_format() after calling all of the above
312             bool do_clear_afterwards;
313         };
314         typedef std::vector<write_once> write_array;
315 
316     public:
with_route(const formatter_array & formatters,const destination_array & destinations)317         with_route(const formatter_array& formatters,
318             const destination_array & destinations) : holder_base_type(formatters,
319                 destinations) {}
320 
321         class route;
322         friend class route;
323         /**
324             represents a formatter/destination route to be added/set.
325         */
326         class route {
327             friend class with_route;
328             enum type {
329                 is_fmt, is_dest, is_clear
330             };
331             struct item {
itemhpx::util::logging::msg_route::with_route::route::item332                 item() : m_fmt(0), m_dest(0), m_type(is_clear) {}
fmthpx::util::logging::msg_route::with_route::route::item333                 item& fmt(formatter_ptr f) {
334                     HPX_ASSERT(f);
335                     m_fmt = f; m_type = is_fmt; return *this;
336                 }
desthpx::util::logging::msg_route::with_route::route::item337                 item &dest(destination_ptr d) {
338                     HPX_ASSERT(d);
339                     m_dest = d; m_type = is_dest; return *this;
340                 }
341                 formatter_ptr m_fmt;
342                 destination_ptr m_dest;
343                 type m_type;
344             };
345             typedef std::vector<item> array;
346 
347         protected:
route(self_type & self)348             route(self_type & self) : m_self(self) {}
349         public:
350 
fmt(formatter f)351             template<class formatter> route & fmt(formatter f) {
352                 fmt_impl(f, std::is_base_of<hpx::util::logging::manipulator
353                     ::is_generic,formatter>() );
354                 return *this;
355             }
dest(destination d)356             template<class destination> route & dest(destination d) {
357                 dest_impl(d, std::is_base_of<hpx::util::logging::manipulator
358                     ::is_generic,destination>() );
359                 return *this;
360             }
clear()361             route & clear() {
362                 m_items.push_back( item() );
363                 return *this;
364             }
365 
366         private:
367             // not generic
fmt_impl(formatter f,const std::false_type &)368             template<class formatter> void fmt_impl(formatter f,
369                 const std::false_type& ) {
370                 m_items.push_back( item().fmt( m_self.formats().get_ptr(f) )) ;
371             }
372             // not generic
dest_impl(destination d,const std::false_type &)373             template<class destination> void dest_impl(destination d,
374                 const std::false_type&) {
375                 m_items.push_back( item().dest( m_self.destinations().get_ptr(d) ));
376             }
377 
378             // generic
fmt_impl(formatter f,const std::true_type &)379             template<class formatter> void fmt_impl(formatter f,
380                 const std::true_type& ) {
381                 typedef hpx::util::logging::manipulator::detail
382                     ::generic_holder<formatter,formatter_base> holder;
383                 fmt_impl( holder(f) , std::false_type() );
384             }
385             // generic
dest_impl(destination d,const std::true_type &)386             template<class destination> void dest_impl(destination d,
387                 const std::true_type&) {
388                 typedef hpx::util::logging::manipulator::detail
389                     ::generic_holder<destination,destination_base> holder;
390                 dest_impl( holder(d) , std::false_type() );
391             }
392         protected:
393             self_type & m_self;
394             array m_items;
395         };
396 
397         struct route_do_set;
398         friend struct route_do_set;
399         struct route_do_set : route {
route_do_sethpx::util::logging::msg_route::with_route::route_do_set400             route_do_set(self_type &self) : route(self) {}
~route_do_sethpx::util::logging::msg_route::with_route::route_do_set401             ~route_do_set() {
402                 route::m_self.do_set_route( *this);
403             }
404         };
405 
406         struct route_do_append;
407         friend struct route_do_append;
408         struct route_do_append : route {
route_do_appendhpx::util::logging::msg_route::with_route::route_do_append409             route_do_append(self_type &self) : route(self) {}
~route_do_appendhpx::util::logging::msg_route::with_route::route_do_append410             ~route_do_append() {
411                 route::m_self.do_append_route( *this);
412             }
413         };
414 
415         /**
416             sets this as the route for logging
417         */
set_route()418         route_do_set set_route() { return route_do_set(*this); }
419 
420         /**
421             appends this route
422         */
append_route()423         route_do_append append_route() { return route_do_append(*this); }
424 
append_formatter(formatter_ptr fmt)425         void append_formatter(formatter_ptr fmt) {
426             if ( m_to_write.empty() )
427                 m_to_write.push_back( write_once() );
428 
429             // we need to add it at the end; if there are any destinations,
430             // we need to add it after those
431             bool can_append_to_back = m_to_write.back().destinations.empty();
432             if ( !can_append_to_back)
433                 m_to_write.push_back( write_once() );
434             m_to_write.back().formats.push_back(fmt);
435         }
del_formatter(formatter_ptr fmt)436         void del_formatter(formatter_ptr fmt) {
437             for ( typename write_array::const_iterator b = m_to_write.begin(),
438                 e = m_to_write.end(); b != e; ++b) {
439                 typename f_array::iterator del = std::remove( b->formats.begin(),
440                     b->formats.end(), fmt); //-V807
441                 b->formats.erase(del, b->formats.end());
442             }
443         }
444 
append_destination(destination_ptr dest)445         void append_destination(destination_ptr dest) {
446             if ( m_to_write.empty() )
447                 m_to_write.push_back( write_once() );
448 
449             if ( m_to_write.back().do_clear_afterwards)
450                 // after clear, always start a new write
451                 m_to_write.push_back( write_once() );
452 
453             m_to_write.back().destinations.push_back(dest);
454         }
455 
del_destination(destination_ptr dest)456         void del_destination(destination_ptr dest) {
457             for ( typename write_array::const_iterator b = m_to_write.begin(),
458                 e = m_to_write.end(); b != e; ++b) {
459                 typename d_array::iterator del = std::remove( b->destinations.begin(),
460                     b->destinations.end(), dest); //-V807
461                 b->destinations.erase(del, b->destinations.end());
462 
463                 // if from a write_once - all destinations are gone,
464                 // don't clear_afterwards
465                 if ( b->destinations.empty() )
466                     b->do_clear_afterwards = false;
467             }
468         }
469 
append_clear_format()470         void append_clear_format() {
471             if ( m_to_write.empty() )
472                 m_to_write.push_back( write_once() );
473             m_to_write.back().do_clear_afterwards = true;
474             m_to_write.push_back( write_once() );
475         }
476 
477 
478         template<class format_and_write>
write(msg_type & msg) const479         void write(msg_type & msg) const {
480             format_and_write m(msg);
481 
482             for ( typename write_array::const_iterator b = m_to_write.begin(),
483                 e = m_to_write.end(); b != e; ++b) {
484                 for ( typename f_array::const_iterator b_f = b->formats.begin(),
485                     e_f = b->formats.end(); b_f != e_f; ++b_f)
486                     m.format(*b_f);
487 
488                 for ( typename d_array::const_iterator
489                     b_d = b->destinations.begin(), e_d = b->destinations.end();
490                     b_d != e_d; ++b_d)
491                     m.write(*b_d);
492 
493                 if ( b->do_clear_afterwards)
494                     m.clear_format();
495             }
496         }
497 
498     private:
do_append_route(const route & r)499         void do_append_route(const route & r) {
500             if ( r.m_items.empty() )
501                 return; // no route to add
502 
503             typedef typename route::array array;
504             for ( typename array::const_iterator b = r.m_items.begin(),
505                 e = r.m_items.end(); b != e; ++b) {
506                 switch ( b->m_type) {
507                 case route::is_fmt:       append_formatter( b->m_fmt); break;
508                 case route::is_dest:      append_destination( b->m_dest); break;
509                 case route::is_clear:     append_clear_format(); break;
510                 }
511             }
512         }
513 
do_set_route(const route & r)514         void do_set_route(const route & r) {
515             {
516             m_to_write.clear();
517             }
518             do_append_route(r);
519         }
520 
521     private:
522         write_array m_to_write;
523     };
524 
525 
526     } // namespace msg_route
527 }}}
528 
529 #include <hpx/util/logging/detail/manipulator.hpp>
530 #include <hpx/util/logging/detail/format_write_detail.hpp>
531 
532 #include <hpx/util/logging/format/formatter/defaults.hpp>
533 #include <hpx/util/logging/format/destination/defaults.hpp>
534 #include <hpx/util/logging/gather/ostream_like.hpp>
535 
536 #endif
537