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)
8 * \file named_scope_format_parser.cpp
9 * \author Andrey Semashev
12 * \brief This header is the Boost.Log library implementation, see the library documentation
13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
16 #include <boost/log/detail/config.hpp>
23 #include <boost/cstdint.hpp>
24 #include <boost/move/core.hpp>
25 #include <boost/move/utility_core.hpp>
26 #include <boost/spirit/include/karma_uint.hpp>
27 #include <boost/spirit/include/karma_generate.hpp>
28 #include <boost/log/attributes/named_scope.hpp>
29 #include <boost/log/expressions/formatters/named_scope.hpp>
30 #include <boost/log/utility/formatting_ostream.hpp>
31 #include <boost/log/detail/header.hpp>
33 namespace karma
= boost::spirit::karma
;
37 BOOST_LOG_OPEN_NAMESPACE
39 namespace expressions
{
43 BOOST_LOG_ANONYMOUS_NAMESPACE
{
45 //! The function skips any spaces from the current position
46 BOOST_FORCEINLINE
const char* skip_spaces(const char* p
, const char* end
)
48 while (p
< end
&& *p
== ' ')
53 //! The function checks if the given character can be part of a function/type/namespace name
54 BOOST_FORCEINLINE
bool is_name_character(char c
)
56 return (c
>= '0' && c
<= '9') || (c
>= 'A' && c
<= 'Z') || c
== '_' || (c
>= 'a' && c
<= 'z');
59 //! The function checks if there is 'operator' keyword at the specified position
60 BOOST_FORCEINLINE
bool is_operator_keyword(const char* p
)
62 #if defined(__i386__) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_IX86)
63 // Intel architecture allows unaligned accesses, so just compare with the whole keyword at once
64 return *reinterpret_cast< const uint64_t* >(p
) == UINT64_C(0x726f74617265706f);
66 return std::memcmp(p
, "operator", 8) == 0;
70 //! The function tries to parse operator signature
71 bool detect_operator(const char* begin
, const char* end
, const char* operator_keyword
, const char*& operator_end
)
73 if (end
- operator_keyword
< 9 || !is_operator_keyword(operator_keyword
))
75 // Check that it's not a function name ending with 'operator', like detect_operator
76 if (operator_keyword
> begin
&& is_name_character(*(operator_keyword
- 1)))
79 const char* p
= skip_spaces(operator_keyword
+ 8, end
);
83 // Check to see where the operator token ends
88 p
= skip_spaces(++p
, end
);
89 if (p
< end
&& *p
== ')')
99 p
= skip_spaces(++p
, end
);
100 if (p
< end
&& *p
== ']')
102 operator_end
= p
+ 1;
110 // Handle operator<=, operator>=, operator<<, operator>>, operator<<=, operator>>=
111 if (end
- p
>= 3 && (p
[0] == p
[1] && p
[2] == '='))
112 operator_end
= p
+ 3;
113 else if (end
- p
>= 2 && (p
[0] == p
[1] || p
[1] == '='))
114 operator_end
= p
+ 2;
116 operator_end
= p
+ 1;
121 // Handle operator->, operator->*
122 if (end
- p
>= 2 && p
[1] == '>')
124 if (end
- p
>= 3 && p
[2] == '*')
125 operator_end
= p
+ 3;
127 operator_end
= p
+ 2;
131 // Fall through to other cases involving '-'
137 // Handle operator=, operator==, operator+=, operator++, operator||, opeartor&&, etc.
138 if (end
- p
>= 2 && (p
[0] == p
[1] || p
[1] == '='))
139 operator_end
= p
+ 2;
141 operator_end
= p
+ 1;
149 // Handle operator*, operator*=, etc.
150 if (end
- p
>= 2 && p
[1] == '=')
151 operator_end
= p
+ 2;
153 operator_end
= p
+ 1;
160 // Handle operator,, operator~, etc.
161 operator_end
= p
+ 1;
166 if (end
- p
>= 2 && p
[0] == p
[1])
168 p
= skip_spaces(p
+ 2, end
);
169 // Skip through the literal suffix
170 while (p
< end
&& is_name_character(*p
))
179 // Handle type conversion operators. We can't find the end of the type reliably here.
185 //! The function skips all template parameters
186 inline const char* skip_template_parameters(const char* begin
, const char* end
)
188 unsigned int depth
= 1;
189 const char* p
= begin
;
190 while (depth
> 0 && p
!= end
)
204 // Skip operators (e.g. when an operator is a non-type template parameter)
205 const char* operator_end
;
206 if (detect_operator(begin
, end
, p
, operator_end
))
224 //! The function seeks for the opening parenthesis and also tries to find the function name beginning
225 inline const char* find_opening_parenthesis(const char* begin
, const char* end
, const char*& first_name_begin
, const char*& last_name_begin
)
229 not_started
, // no significant (non-space) characters have been encountered so far
230 started
, // some name has started; the name is a contiguous sequence of characters that may constitute a function or scope name
231 continued
, // the previous characters were the scope operator ("::"), so the name is not finished yet
232 ended
, // the name has ended; in particular, this means that there were significant characters previously in the string
233 operator_detected
// operator has been found in the string, don't parse for scopes anymore; this is needed for conversion operators
235 sequence_state state
= not_started
;
237 const char* p
= begin
;
244 if (state
== not_started
)
246 // If the opening brace is the first meaningful character in the string then this can't be a function signature.
247 // Pretend we didn't find the paranthesis to fail the parsing process.
253 if (state
== not_started
)
255 // Template parameters cannot start as the first meaningful character in the signature.
256 // Pretend we didn't find the paranthesis to fail the parsing process.
259 p
= skip_template_parameters(p
+ 1, end
);
260 if (state
!= operator_detected
)
265 if (state
== started
)
271 if (p
!= end
&& *p
== ':')
273 if (state
== not_started
)
275 // Include the starting "::" in the full name
276 first_name_begin
= p
- 1;
278 if (state
!= operator_detected
)
282 else if (state
!= operator_detected
)
284 // Weird case, a single colon. Maybe, some compilers would put things like "public:" in front of the signature.
291 const char* operator_end
;
292 if (detect_operator(begin
, end
, p
, operator_end
))
294 if (state
== not_started
|| state
== ended
)
295 first_name_begin
= p
;
298 state
= operator_detected
;
302 // Fall through to process this character as other characters
305 if (state
!= operator_detected
)
307 if (is_name_character(c
) || c
== '~') // check for '~' in case of a destructor
309 if (state
!= started
)
311 if (state
== not_started
|| state
== ended
)
312 first_name_begin
= p
;
331 //! The function seeks for the closing parenthesis
332 inline const char* find_closing_parenthesis(const char* begin
, const char* end
, char& first_char
)
334 bool found_first_meaningful_char
= false;
335 unsigned int depth
= 1;
336 const char* p
= begin
;
353 p
= skip_template_parameters(p
+ 1, end
);
358 const char* operator_end
;
359 if (detect_operator(begin
, end
, p
, operator_end
))
365 // Fall through to process this character as other characters
368 if (!found_first_meaningful_char
&& c
!= ' ')
370 found_first_meaningful_char
= true;
382 bool parse_function_name(const char*& begin
, const char*& end
, bool include_scope
)
384 // The algorithm tries to match several patterns to recognize function signatures. The most obvious is:
392 // in case of constructors, destructors and type conversion operators. The algorithm looks for the opening parenthesis and while doing that
393 // it detects the beginning of B. As a result B is the function name.
395 // The first significant complication is function and array return types, in which case the syntax becomes nested:
400 // In addition to that MSVC adds calling convention, such as __cdecl, to function types. In order to detect these cases the algorithm
401 // seeks for the closing parenthesis after the opening one. If there is an opening parenthesis or square bracket after the closing parenthesis
402 // then this is a function or array return type. The case of arrays is additionally complicated by GCC output:
406 // where D is template parameters description and is not part of the signature. To discern this special case from the array return type, the algorithm
407 // checks for the first significant character within the parenthesis. This character is '&' in case of arrays and something else otherwise.
409 // Speaking of template parameters, the parsing algorithm ignores them completely, assuming they are part of the name being parsed. This includes
410 // any possible parenthesis, nested template parameters and even operators, which may be present there as non-type template parameters.
412 // Operators pose another problem. This is especially the case for type conversion operators, and even more so for conversion operators to
413 // function types. In this latter case at least MSVC is known to produce incomprehensible strings which we cannot parse. In other cases it is
414 // too difficult to parse the type correctly. So we cheat a little. Whenever we find "operator", we know that we've found the function name
415 // already, and the name ends at the opening parenthesis. For other operators we are able to parse them correctly but that doesn't really matter.
417 // Note that the algorithm should be tolerant to different flavors of the input strings from different compilers, so we can't rely on spaces
418 // delimiting function names and other elements. Also, the algorithm should behave well in case of the fallback string generated by
419 // BOOST_CURRENT_FUNCTION (which is "(unknown)" currently). In case of any parsing failure the algorithm should return false, in which case the
420 // full original string will be used as the output.
422 const char* b
= begin
;
426 // Find the opening parenthesis. While looking for it, also find the function name.
427 // first_name_begin is the beginning of the function scope, last_name_begin is the actual function name.
428 const char* first_name_begin
= NULL
, *last_name_begin
= NULL
;
429 const char* paren_open
= find_opening_parenthesis(b
, e
, first_name_begin
, last_name_begin
);
432 // Find the closing parenthesis. Also peek at the first character in the parenthesis, which we'll use to detect array return types.
433 char first_char_in_parenthesis
= 0;
434 const char* paren_close
= find_closing_parenthesis(paren_open
+ 1, e
, first_char_in_parenthesis
);
435 if (paren_close
== e
)
438 const char* p
= skip_spaces(paren_close
+ 1, e
);
440 // Detect function and array return types
441 if (p
< e
&& (*p
== '(' || (*p
== '[' && first_char_in_parenthesis
== '&')))
443 // This is a function or array return type, the actual function name is within the parenthesis.
444 // Re-parse the string within the parenthesis as a function signature.
450 // We found something that looks like a function signature
453 if (!first_name_begin
)
456 begin
= first_name_begin
;
460 if (!last_name_begin
)
463 begin
= last_name_begin
;
474 template< typename CharT
>
475 class named_scope_formatter
477 BOOST_COPYABLE_AND_MOVABLE_ALT(named_scope_formatter
)
480 typedef void result_type
;
482 typedef CharT char_type
;
483 typedef std::basic_string
< char_type
> string_type
;
484 typedef basic_formatting_ostream
< char_type
> stream_type
;
485 typedef attributes::named_scope::value_type::value_type value_type
;
489 typedef void result_type
;
491 explicit literal(string_type
& lit
) { m_literal
.swap(lit
); }
493 result_type
operator() (stream_type
& strm
, value_type
const&) const
499 string_type m_literal
;
504 typedef void result_type
;
506 result_type
operator() (stream_type
& strm
, value_type
const& value
) const
508 strm
<< value
.scope_name
;
514 typedef void result_type
;
516 explicit function_name(bool include_scope
) : m_include_scope(include_scope
)
520 result_type
operator() (stream_type
& strm
, value_type
const& value
) const
522 if (value
.type
== attributes::named_scope_entry::function
)
524 const char* begin
= value
.scope_name
.c_str();
525 const char* end
= begin
+ value
.scope_name
.size();
526 if (parse_function_name(begin
, end
, m_include_scope
))
528 strm
.write(begin
, end
- begin
);
533 strm
<< value
.scope_name
;
537 const bool m_include_scope
;
540 struct full_file_name
542 typedef void result_type
;
544 result_type
operator() (stream_type
& strm
, value_type
const& value
) const
546 strm
<< value
.file_name
;
552 typedef void result_type
;
554 result_type
operator() (stream_type
& strm
, value_type
const& value
) const
556 std::size_t n
= value
.file_name
.size(), i
= n
;
559 const char c
= value
.file_name
[i
- 1];
560 #if defined(BOOST_WINDOWS)
567 strm
.write(value
.file_name
.c_str() + i
, n
- i
);
573 typedef void result_type
;
575 result_type
operator() (stream_type
& strm
, value_type
const& value
) const
579 char_type buf
[std::numeric_limits
< unsigned int >::digits10
+ 2];
582 typedef karma::uint_generator
< unsigned int, 10 > uint_gen
;
583 karma::generate(p
, uint_gen(), value
.line
);
585 typedef typename
stream_type::streambuf_type streambuf_type
;
586 static_cast< streambuf_type
* >(strm
.rdbuf())->append(buf
, static_cast< std::size_t >(p
- buf
));
591 typedef boost::log::aux::light_function
< void (stream_type
&, value_type
const&) > formatter_type
;
592 typedef std::vector
< formatter_type
> formatters
;
595 formatters m_formatters
;
598 BOOST_DEFAULTED_FUNCTION(named_scope_formatter(), {})
599 named_scope_formatter(named_scope_formatter
const& that
) : m_formatters(that
.m_formatters
) {}
600 named_scope_formatter(BOOST_RV_REF(named_scope_formatter
) that
) { m_formatters
.swap(that
.m_formatters
); }
602 named_scope_formatter
& operator= (named_scope_formatter that
)
608 result_type
operator() (stream_type
& strm
, value_type
const& value
) const
610 for (typename
formatters::const_iterator it
= m_formatters
.begin(), end
= m_formatters
.end(); strm
.good() && it
!= end
; ++it
)
616 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
617 template< typename FunT
>
618 void add_formatter(FunT
&& fun
)
620 m_formatters
.emplace_back(boost::forward
< FunT
>(fun
));
623 template< typename FunT
>
624 void add_formatter(FunT
const& fun
)
626 m_formatters
.push_back(formatter_type(fun
));
630 void swap(named_scope_formatter
& that
)
632 m_formatters
.swap(that
.m_formatters
);
636 //! Parses the named scope format string and constructs the formatter function
637 template< typename CharT
>
638 BOOST_FORCEINLINE
boost::log::aux::light_function
< void (basic_formatting_ostream
< CharT
>&, attributes::named_scope::value_type::value_type
const&) >
639 do_parse_named_scope_format(const CharT
* begin
, const CharT
* end
)
641 typedef CharT char_type
;
642 typedef boost::log::aux::light_function
< void (basic_formatting_ostream
< char_type
>&, attributes::named_scope::value_type::value_type
const&) > result_type
;
643 typedef named_scope_formatter
< char_type
> formatter_type
;
646 std::basic_string
< char_type
> literal
;
650 const char_type
* p
= std::find(begin
, end
, static_cast< char_type
>('%'));
651 literal
.append(begin
, p
);
658 literal
.push_back(static_cast< char_type
>('%'));
662 if (!literal
.empty())
663 fmt
.add_formatter(typename
formatter_type::literal(literal
));
664 fmt
.add_formatter(typename
formatter_type::scope_name());
668 if (!literal
.empty())
669 fmt
.add_formatter(typename
formatter_type::literal(literal
));
670 fmt
.add_formatter(typename
formatter_type::function_name(true));
674 if (!literal
.empty())
675 fmt
.add_formatter(typename
formatter_type::literal(literal
));
676 fmt
.add_formatter(typename
formatter_type::function_name(false));
680 if (!literal
.empty())
681 fmt
.add_formatter(typename
formatter_type::literal(literal
));
682 fmt
.add_formatter(typename
formatter_type::full_file_name());
686 if (!literal
.empty())
687 fmt
.add_formatter(typename
formatter_type::literal(literal
));
688 fmt
.add_formatter(typename
formatter_type::file_name());
692 if (!literal
.empty())
693 fmt
.add_formatter(typename
formatter_type::literal(literal
));
694 fmt
.add_formatter(typename
formatter_type::line_number());
698 literal
.append(p
, p
+ 2);
707 literal
.push_back(static_cast< char_type
>('%')); // a single '%' character at the end of the string
712 if (!literal
.empty())
713 fmt
.add_formatter(typename
formatter_type::literal(literal
));
715 return result_type(boost::move(fmt
));
721 #ifdef BOOST_LOG_USE_CHAR
723 //! Parses the named scope format string and constructs the formatter function
724 BOOST_LOG_API
boost::log::aux::light_function
< void (basic_formatting_ostream
< char >&, attributes::named_scope::value_type::value_type
const&) >
725 parse_named_scope_format(const char* begin
, const char* end
)
727 return do_parse_named_scope_format(begin
, end
);
730 #endif // BOOST_LOG_USE_CHAR
732 #ifdef BOOST_LOG_USE_WCHAR_T
734 //! Parses the named scope format string and constructs the formatter function
735 BOOST_LOG_API
boost::log::aux::light_function
< void (basic_formatting_ostream
< wchar_t >&, attributes::named_scope::value_type::value_type
const&) >
736 parse_named_scope_format(const wchar_t* begin
, const wchar_t* end
)
738 return do_parse_named_scope_format(begin
, end
);
741 #endif // BOOST_LOG_USE_WCHAR_T
745 } // namespace expressions
747 BOOST_LOG_CLOSE_NAMESPACE
// namespace log
751 #include <boost/log/detail/footer.hpp>