mirror of
https://github.com/boostorg/log.git
synced 2026-02-10 23:42:22 +00:00
Include winapi/config.hpp as soon as possible; avoid including even
Boost.Config before it so that it doesn't set Windows API version
accidentally by including some third party header. In all source files,
include detail/config.hpp or detail/setup_config.hpp first thing.
Moved all WinAPI config macros to the Jamfile.v2 and removed
windows_version.hpp as it was no longer needed. Also enabled inclusion of
windows.h while compiling the library.
Removed auto-linking with psapi.lib and advapi32.lib as it was no longer
working after ecf3114. Added linking with advapi32.lib to Jamfile.v2.
752 lines
23 KiB
C++
752 lines
23 KiB
C++
/*
|
|
* Copyright Andrey Semashev 2007 - 2015.
|
|
* Distributed under the Boost Software License, Version 1.0.
|
|
* (See accompanying file LICENSE_1_0.txt or copy at
|
|
* http://www.boost.org/LICENSE_1_0.txt)
|
|
*/
|
|
/*!
|
|
* \file named_scope_format_parser.cpp
|
|
* \author Andrey Semashev
|
|
* \date 14.11.2012
|
|
*
|
|
* \brief This header is the Boost.Log library implementation, see the library documentation
|
|
* at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
|
|
*/
|
|
|
|
#include <boost/log/detail/config.hpp>
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <limits>
|
|
#include <algorithm>
|
|
#include <boost/cstdint.hpp>
|
|
#include <boost/move/core.hpp>
|
|
#include <boost/move/utility.hpp>
|
|
#include <boost/spirit/include/karma_uint.hpp>
|
|
#include <boost/spirit/include/karma_generate.hpp>
|
|
#include <boost/log/attributes/named_scope.hpp>
|
|
#include <boost/log/expressions/formatters/named_scope.hpp>
|
|
#include <boost/log/utility/formatting_ostream.hpp>
|
|
#include <boost/log/detail/header.hpp>
|
|
|
|
namespace karma = boost::spirit::karma;
|
|
|
|
namespace boost {
|
|
|
|
BOOST_LOG_OPEN_NAMESPACE
|
|
|
|
namespace expressions {
|
|
|
|
namespace aux {
|
|
|
|
BOOST_LOG_ANONYMOUS_NAMESPACE {
|
|
|
|
//! The function skips any spaces from the current position
|
|
BOOST_FORCEINLINE const char* skip_spaces(const char* p, const char* end)
|
|
{
|
|
while (p < end && *p == ' ')
|
|
++p;
|
|
return p;
|
|
}
|
|
|
|
//! The function checks if the given character can be part of a function/type/namespace name
|
|
BOOST_FORCEINLINE bool is_name_character(char c)
|
|
{
|
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z');
|
|
}
|
|
|
|
//! The function checks if there is 'operator' keyword at the specified position
|
|
BOOST_FORCEINLINE bool is_operator_keyword(const char* p)
|
|
{
|
|
#if defined(__i386__) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_IX86)
|
|
// Intel architecture allows unaligned accesses, so just compare with the whole keyword at once
|
|
return *reinterpret_cast< const uint64_t* >(p) == UINT64_C(0x726f74617265706f);
|
|
#else
|
|
return std::memcmp(p, "operator", 8) == 0;
|
|
#endif
|
|
}
|
|
|
|
//! The function tries to parse operator signature
|
|
bool detect_operator(const char* begin, const char* end, const char* operator_keyword, const char*& operator_end)
|
|
{
|
|
if (end - operator_keyword < 9 || !is_operator_keyword(operator_keyword))
|
|
return false;
|
|
// Check that it's not a function name ending with 'operator', like detect_operator
|
|
if (operator_keyword > begin && is_name_character(*(operator_keyword - 1)))
|
|
return false;
|
|
|
|
const char* p = skip_spaces(operator_keyword + 8, end);
|
|
if (p == end)
|
|
return false;
|
|
|
|
// Check to see where the operator token ends
|
|
switch (*p)
|
|
{
|
|
case '(':
|
|
// Handle operator()
|
|
p = skip_spaces(++p, end);
|
|
if (p < end && *p == ')')
|
|
{
|
|
operator_end = p + 1;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
case '[':
|
|
// Handle operator[]
|
|
p = skip_spaces(++p, end);
|
|
if (p < end && *p == ']')
|
|
{
|
|
operator_end = p + 1;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
case '>':
|
|
case '<':
|
|
// Handle operator<=, operator>=, operator<<, operator>>, operator<<=, operator>>=
|
|
if (end - p >= 3 && (p[0] == p[1] && p[2] == '='))
|
|
operator_end = p + 3;
|
|
else if (end - p >= 2 && (p[0] == p[1] || p[1] == '='))
|
|
operator_end = p + 2;
|
|
else
|
|
operator_end = p + 1;
|
|
|
|
return true;
|
|
|
|
case '-':
|
|
// Handle operator->, operator->*
|
|
if (end - p >= 2 && p[1] == '>')
|
|
{
|
|
if (end - p >= 3 && p[2] == '*')
|
|
operator_end = p + 3;
|
|
else
|
|
operator_end = p + 2;
|
|
|
|
return true;
|
|
}
|
|
// Fall through to other cases involving '-'
|
|
|
|
case '=':
|
|
case '|':
|
|
case '&':
|
|
case '+':
|
|
// Handle operator=, operator==, operator+=, operator++, operator||, opeartor&&, etc.
|
|
if (end - p >= 2 && (p[0] == p[1] || p[1] == '='))
|
|
operator_end = p + 2;
|
|
else
|
|
operator_end = p + 1;
|
|
|
|
return true;
|
|
|
|
case '*':
|
|
case '/':
|
|
case '%':
|
|
case '^':
|
|
// Handle operator*, operator*=, etc.
|
|
if (end - p >= 2 && p[1] == '=')
|
|
operator_end = p + 2;
|
|
else
|
|
operator_end = p + 1;
|
|
|
|
return true;
|
|
|
|
case ',':
|
|
case '~':
|
|
case '!':
|
|
// Handle operator,, operator~, etc.
|
|
operator_end = p + 1;
|
|
return true;
|
|
|
|
case '"':
|
|
// Handle operator""
|
|
if (end - p >= 2 && p[0] == p[1])
|
|
{
|
|
p = skip_spaces(p + 2, end);
|
|
// Skip through the literal suffix
|
|
while (p < end && is_name_character(*p))
|
|
++p;
|
|
operator_end = p;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
default:
|
|
// Handle type conversion operators. We can't find the end of the type reliably here.
|
|
operator_end = p;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//! The function skips all template parameters
|
|
inline const char* skip_template_parameters(const char* begin, const char* end)
|
|
{
|
|
unsigned int depth = 1;
|
|
const char* p = begin;
|
|
while (depth > 0 && p != end)
|
|
{
|
|
switch (*p)
|
|
{
|
|
case '>':
|
|
--depth;
|
|
break;
|
|
|
|
case '<':
|
|
++depth;
|
|
break;
|
|
|
|
case 'o':
|
|
{
|
|
// Skip operators (e.g. when an operator is a non-type template parameter)
|
|
const char* operator_end;
|
|
if (detect_operator(begin, end, p, operator_end))
|
|
{
|
|
p = operator_end;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
++p;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
//! The function seeks for the opening parenthesis and also tries to find the function name beginning
|
|
inline const char* find_opening_parenthesis(const char* begin, const char* end, const char*& first_name_begin, const char*& last_name_begin)
|
|
{
|
|
enum sequence_state
|
|
{
|
|
not_started, // no significant (non-space) characters have been encountered so far
|
|
started, // some name has started; the name is a contiguous sequence of characters that may constitute a function or scope name
|
|
continued, // the previous characters were the scope operator ("::"), so the name is not finished yet
|
|
ended, // the name has ended; in particular, this means that there were significant characters previously in the string
|
|
operator_detected // operator has been found in the string, don't parse for scopes anymore; this is needed for conversion operators
|
|
};
|
|
sequence_state state = not_started;
|
|
|
|
const char* p = begin;
|
|
while (p != end)
|
|
{
|
|
char c = *p;
|
|
switch (c)
|
|
{
|
|
case '(':
|
|
if (state == not_started)
|
|
{
|
|
// If the opening brace is the first meaningful character in the string then this can't be a function signature.
|
|
// Pretend we didn't find the paranthesis to fail the parsing process.
|
|
return end;
|
|
}
|
|
return p;
|
|
|
|
case '<':
|
|
if (state == not_started)
|
|
{
|
|
// Template parameters cannot start as the first meaningful character in the signature.
|
|
// Pretend we didn't find the paranthesis to fail the parsing process.
|
|
return end;
|
|
}
|
|
p = skip_template_parameters(p + 1, end);
|
|
if (state != operator_detected)
|
|
state = ended;
|
|
continue;
|
|
|
|
case ' ':
|
|
if (state == started)
|
|
state = ended;
|
|
break;
|
|
|
|
case ':':
|
|
++p;
|
|
if (p != end && *p == ':')
|
|
{
|
|
if (state == not_started)
|
|
{
|
|
// Include the starting "::" in the full name
|
|
first_name_begin = p - 1;
|
|
}
|
|
if (state != operator_detected)
|
|
state = continued;
|
|
++p;
|
|
}
|
|
else if (state != operator_detected)
|
|
{
|
|
// Weird case, a single colon. Maybe, some compilers would put things like "public:" in front of the signature.
|
|
state = ended;
|
|
}
|
|
continue;
|
|
|
|
case 'o':
|
|
{
|
|
const char* operator_end;
|
|
if (detect_operator(begin, end, p, operator_end))
|
|
{
|
|
if (state == not_started || state == ended)
|
|
first_name_begin = p;
|
|
last_name_begin = p;
|
|
p = operator_end;
|
|
state = operator_detected;
|
|
continue;
|
|
}
|
|
}
|
|
// Fall through to process this character as other characters
|
|
|
|
default:
|
|
if (state != operator_detected)
|
|
{
|
|
if (is_name_character(c) || c == '~') // check for '~' in case of a destructor
|
|
{
|
|
if (state != started)
|
|
{
|
|
if (state == not_started || state == ended)
|
|
first_name_begin = p;
|
|
last_name_begin = p;
|
|
state = started;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state = ended;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
++p;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
//! The function seeks for the closing parenthesis
|
|
inline const char* find_closing_parenthesis(const char* begin, const char* end, char& first_char)
|
|
{
|
|
bool found_first_meaningful_char = false;
|
|
unsigned int depth = 1;
|
|
const char* p = begin;
|
|
while (p != end)
|
|
{
|
|
char c = *p;
|
|
switch (c)
|
|
{
|
|
case ')':
|
|
--depth;
|
|
if (depth == 0)
|
|
return p;
|
|
break;
|
|
|
|
case '(':
|
|
++depth;
|
|
break;
|
|
|
|
case '<':
|
|
p = skip_template_parameters(p + 1, end);
|
|
continue;
|
|
|
|
case 'o':
|
|
{
|
|
const char* operator_end;
|
|
if (detect_operator(begin, end, p, operator_end))
|
|
{
|
|
p = operator_end;
|
|
continue;
|
|
}
|
|
}
|
|
// Fall through to process this character as other characters
|
|
|
|
default:
|
|
if (!found_first_meaningful_char && c != ' ')
|
|
{
|
|
found_first_meaningful_char = true;
|
|
first_char = c;
|
|
}
|
|
break;
|
|
}
|
|
|
|
++p;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
bool parse_function_name(const char*& begin, const char*& end, bool include_scope)
|
|
{
|
|
// The algorithm tries to match several patterns to recognize function signatures. The most obvious is:
|
|
//
|
|
// A B(C)
|
|
//
|
|
// or just:
|
|
//
|
|
// B(C)
|
|
//
|
|
// in case of constructors, destructors and type conversion operators. The algorithm looks for the opening parenthesis and while doing that
|
|
// it detects the beginning of B. As a result B is the function name.
|
|
//
|
|
// The first significant complication is function and array return types, in which case the syntax becomes nested:
|
|
//
|
|
// A (*B(C))(D)
|
|
// A (&B(C))[D]
|
|
//
|
|
// In addition to that MSVC adds calling convention, such as __cdecl, to function types. In order to detect these cases the algorithm
|
|
// seeks for the closing parenthesis after the opening one. If there is an opening parenthesis or square bracket after the closing parenthesis
|
|
// then this is a function or array return type. The case of arrays is additionally complicated by GCC output:
|
|
//
|
|
// A B(C) [D]
|
|
//
|
|
// 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
|
|
// checks for the first significant character within the parenthesis. This character is '&' in case of arrays and something else otherwise.
|
|
//
|
|
// Speaking of template parameters, the parsing algorithm ignores them completely, assuming they are part of the name being parsed. This includes
|
|
// any possible parenthesis, nested template parameters and even operators, which may be present there as non-type template parameters.
|
|
//
|
|
// Operators pose another problem. This is especially the case for type conversion operators, and even more so for conversion operators to
|
|
// function types. In this latter case at least MSVC is known to produce incomprehensible strings which we cannot parse. In other cases it is
|
|
// 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
|
|
// 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.
|
|
//
|
|
// Note that the algorithm should be tolerant to different flavors of the input strings from different compilers, so we can't rely on spaces
|
|
// delimiting function names and other elements. Also, the algorithm should behave well in case of the fallback string generated by
|
|
// BOOST_CURRENT_FUNCTION (which is "(unknown)" currently). In case of any parsing failure the algorithm should return false, in which case the
|
|
// full original string will be used as the output.
|
|
|
|
const char* b = begin;
|
|
const char* e = end;
|
|
while (b != e)
|
|
{
|
|
// Find the opening parenthesis. While looking for it, also find the function name.
|
|
// first_name_begin is the beginning of the function scope, last_name_begin is the actual function name.
|
|
const char* first_name_begin = NULL, *last_name_begin = NULL;
|
|
const char* paren_open = find_opening_parenthesis(b, e, first_name_begin, last_name_begin);
|
|
if (paren_open == e)
|
|
return false;
|
|
// Find the closing parenthesis. Also peek at the first character in the parenthesis, which we'll use to detect array return types.
|
|
char first_char_in_parenthesis = 0;
|
|
const char* paren_close = find_closing_parenthesis(paren_open + 1, e, first_char_in_parenthesis);
|
|
if (paren_close == e)
|
|
return false;
|
|
|
|
const char* p = skip_spaces(paren_close + 1, e);
|
|
|
|
// Detect function and array return types
|
|
if (p < e && (*p == '(' || (*p == '[' && first_char_in_parenthesis == '&')))
|
|
{
|
|
// This is a function or array return type, the actual function name is within the parenthesis.
|
|
// Re-parse the string within the parenthesis as a function signature.
|
|
b = paren_open + 1;
|
|
e = paren_close;
|
|
continue;
|
|
}
|
|
|
|
// We found something that looks like a function signature
|
|
if (include_scope)
|
|
{
|
|
if (!first_name_begin)
|
|
return false;
|
|
|
|
begin = first_name_begin;
|
|
}
|
|
else
|
|
{
|
|
if (!last_name_begin)
|
|
return false;
|
|
|
|
begin = last_name_begin;
|
|
}
|
|
|
|
end = paren_open;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template< typename CharT >
|
|
class named_scope_formatter
|
|
{
|
|
BOOST_COPYABLE_AND_MOVABLE_ALT(named_scope_formatter)
|
|
|
|
public:
|
|
typedef void result_type;
|
|
|
|
typedef CharT char_type;
|
|
typedef std::basic_string< char_type > string_type;
|
|
typedef basic_formatting_ostream< char_type > stream_type;
|
|
typedef attributes::named_scope::value_type::value_type value_type;
|
|
|
|
struct literal
|
|
{
|
|
typedef void result_type;
|
|
|
|
explicit literal(string_type& lit) { m_literal.swap(lit); }
|
|
|
|
result_type operator() (stream_type& strm, value_type const&) const
|
|
{
|
|
strm << m_literal;
|
|
}
|
|
|
|
private:
|
|
string_type m_literal;
|
|
};
|
|
|
|
struct scope_name
|
|
{
|
|
typedef void result_type;
|
|
|
|
result_type operator() (stream_type& strm, value_type const& value) const
|
|
{
|
|
strm << value.scope_name;
|
|
}
|
|
};
|
|
|
|
struct function_name
|
|
{
|
|
typedef void result_type;
|
|
|
|
explicit function_name(bool include_scope) : m_include_scope(include_scope)
|
|
{
|
|
}
|
|
|
|
result_type operator() (stream_type& strm, value_type const& value) const
|
|
{
|
|
if (value.type == attributes::named_scope_entry::function)
|
|
{
|
|
const char* begin = value.scope_name.c_str();
|
|
const char* end = begin + value.scope_name.size();
|
|
if (parse_function_name(begin, end, m_include_scope))
|
|
{
|
|
strm.write(begin, end - begin);
|
|
return;
|
|
}
|
|
}
|
|
|
|
strm << value.scope_name;
|
|
}
|
|
|
|
private:
|
|
const bool m_include_scope;
|
|
};
|
|
|
|
struct full_file_name
|
|
{
|
|
typedef void result_type;
|
|
|
|
result_type operator() (stream_type& strm, value_type const& value) const
|
|
{
|
|
strm << value.file_name;
|
|
}
|
|
};
|
|
|
|
struct file_name
|
|
{
|
|
typedef void result_type;
|
|
|
|
result_type operator() (stream_type& strm, value_type const& value) const
|
|
{
|
|
std::size_t n = value.file_name.size(), i = n;
|
|
for (; i > 0; --i)
|
|
{
|
|
const char c = value.file_name[i - 1];
|
|
#if defined(BOOST_WINDOWS)
|
|
if (c == '\\')
|
|
break;
|
|
#endif
|
|
if (c == '/')
|
|
break;
|
|
}
|
|
strm.write(value.file_name.c_str() + i, n - i);
|
|
}
|
|
};
|
|
|
|
struct line_number
|
|
{
|
|
typedef void result_type;
|
|
|
|
result_type operator() (stream_type& strm, value_type const& value) const
|
|
{
|
|
strm.flush();
|
|
typedef typename stream_type::streambuf_type streambuf_type;
|
|
string_type& str = *static_cast< streambuf_type* >(strm.rdbuf())->storage();
|
|
|
|
char_type buf[std::numeric_limits< unsigned int >::digits10 + 2];
|
|
char_type* p = buf;
|
|
|
|
typedef karma::uint_generator< unsigned int, 10 > uint_gen;
|
|
karma::generate(p, uint_gen(), value.line);
|
|
str.append(buf, p);
|
|
}
|
|
};
|
|
|
|
private:
|
|
typedef boost::log::aux::light_function< void (stream_type&, value_type const&) > formatter_type;
|
|
typedef std::vector< formatter_type > formatters;
|
|
|
|
private:
|
|
formatters m_formatters;
|
|
|
|
public:
|
|
BOOST_DEFAULTED_FUNCTION(named_scope_formatter(), {})
|
|
named_scope_formatter(named_scope_formatter const& that) : m_formatters(that.m_formatters) {}
|
|
named_scope_formatter(BOOST_RV_REF(named_scope_formatter) that) { m_formatters.swap(that.m_formatters); }
|
|
|
|
named_scope_formatter& operator= (named_scope_formatter that)
|
|
{
|
|
this->swap(that);
|
|
return *this;
|
|
}
|
|
|
|
result_type operator() (stream_type& strm, value_type const& value) const
|
|
{
|
|
for (typename formatters::const_iterator it = m_formatters.begin(), end = m_formatters.end(); strm.good() && it != end; ++it)
|
|
{
|
|
(*it)(strm, value);
|
|
}
|
|
}
|
|
|
|
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
|
template< typename FunT >
|
|
void add_formatter(FunT&& fun)
|
|
{
|
|
m_formatters.emplace_back(boost::forward< FunT >(fun));
|
|
}
|
|
#else
|
|
template< typename FunT >
|
|
void add_formatter(FunT const& fun)
|
|
{
|
|
m_formatters.push_back(formatter_type(fun));
|
|
}
|
|
#endif
|
|
|
|
void swap(named_scope_formatter& that)
|
|
{
|
|
m_formatters.swap(that.m_formatters);
|
|
}
|
|
};
|
|
|
|
//! Parses the named scope format string and constructs the formatter function
|
|
template< typename CharT >
|
|
BOOST_FORCEINLINE boost::log::aux::light_function< void (basic_formatting_ostream< CharT >&, attributes::named_scope::value_type::value_type const&) >
|
|
do_parse_named_scope_format(const CharT* begin, const CharT* end)
|
|
{
|
|
typedef CharT char_type;
|
|
typedef boost::log::aux::light_function< void (basic_formatting_ostream< char_type >&, attributes::named_scope::value_type::value_type const&) > result_type;
|
|
typedef named_scope_formatter< char_type > formatter_type;
|
|
formatter_type fmt;
|
|
|
|
std::basic_string< char_type > literal;
|
|
|
|
while (begin != end)
|
|
{
|
|
const char_type* p = std::find(begin, end, static_cast< char_type >('%'));
|
|
literal.append(begin, p);
|
|
|
|
if ((end - p) >= 2)
|
|
{
|
|
switch (p[1])
|
|
{
|
|
case '%':
|
|
literal.push_back(static_cast< char_type >('%'));
|
|
break;
|
|
|
|
case 'n':
|
|
if (!literal.empty())
|
|
fmt.add_formatter(typename formatter_type::literal(literal));
|
|
fmt.add_formatter(typename formatter_type::scope_name());
|
|
break;
|
|
|
|
case 'c':
|
|
if (!literal.empty())
|
|
fmt.add_formatter(typename formatter_type::literal(literal));
|
|
fmt.add_formatter(typename formatter_type::function_name(true));
|
|
break;
|
|
|
|
case 'C':
|
|
if (!literal.empty())
|
|
fmt.add_formatter(typename formatter_type::literal(literal));
|
|
fmt.add_formatter(typename formatter_type::function_name(false));
|
|
break;
|
|
|
|
case 'f':
|
|
if (!literal.empty())
|
|
fmt.add_formatter(typename formatter_type::literal(literal));
|
|
fmt.add_formatter(typename formatter_type::full_file_name());
|
|
break;
|
|
|
|
case 'F':
|
|
if (!literal.empty())
|
|
fmt.add_formatter(typename formatter_type::literal(literal));
|
|
fmt.add_formatter(typename formatter_type::file_name());
|
|
break;
|
|
|
|
case 'l':
|
|
if (!literal.empty())
|
|
fmt.add_formatter(typename formatter_type::literal(literal));
|
|
fmt.add_formatter(typename formatter_type::line_number());
|
|
break;
|
|
|
|
default:
|
|
literal.append(p, p + 2);
|
|
break;
|
|
}
|
|
|
|
begin = p + 2;
|
|
}
|
|
else
|
|
{
|
|
if (p != end)
|
|
literal.push_back(static_cast< char_type >('%')); // a single '%' character at the end of the string
|
|
begin = end;
|
|
}
|
|
}
|
|
|
|
if (!literal.empty())
|
|
fmt.add_formatter(typename formatter_type::literal(literal));
|
|
|
|
return result_type(boost::move(fmt));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
#ifdef BOOST_LOG_USE_CHAR
|
|
|
|
//! Parses the named scope format string and constructs the formatter function
|
|
BOOST_LOG_API boost::log::aux::light_function< void (basic_formatting_ostream< char >&, attributes::named_scope::value_type::value_type const&) >
|
|
parse_named_scope_format(const char* begin, const char* end)
|
|
{
|
|
return do_parse_named_scope_format(begin, end);
|
|
}
|
|
|
|
#endif // BOOST_LOG_USE_CHAR
|
|
|
|
#ifdef BOOST_LOG_USE_WCHAR_T
|
|
|
|
//! Parses the named scope format string and constructs the formatter function
|
|
BOOST_LOG_API boost::log::aux::light_function< void (basic_formatting_ostream< wchar_t >&, attributes::named_scope::value_type::value_type const&) >
|
|
parse_named_scope_format(const wchar_t* begin, const wchar_t* end)
|
|
{
|
|
return do_parse_named_scope_format(begin, end);
|
|
}
|
|
|
|
#endif // BOOST_LOG_USE_WCHAR_T
|
|
|
|
} // namespace aux
|
|
|
|
} // namespace expressions
|
|
|
|
BOOST_LOG_CLOSE_NAMESPACE // namespace log
|
|
|
|
} // namespace boost
|
|
|
|
#include <boost/log/detail/footer.hpp>
|