From 1401b3e3bf6c857fbc19e1a1a2aa9946ccd76305 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Sun, 2 Mar 2014 18:27:36 +0400 Subject: [PATCH] Fixed parsing signatures of functions that return function pointers. - Function name parsing is only done for scopes denoted with BOOST_LOG_FUNCTION, scopes marked with BOOST_LOG_NAMED_SCOPE are output as is. - The parser has been rewritten to behave more correctly in case if function name contains template parameters and/or returns a function type. - Operator functions are not supported by the parser. - Docs corrections. --- doc/attributes.qbk | 2 + doc/changelog.qbk | 2 +- doc/expressions.qbk | 6 +- include/boost/log/attributes/named_scope.hpp | 33 ++- src/named_scope_format_parser.cpp | 153 ++++++++++-- test/run/form_named_scope.cpp | 236 +++++++++---------- 6 files changed, 267 insertions(+), 165 deletions(-) diff --git a/doc/attributes.qbk b/doc/attributes.qbk index d7e539a..6601318 100644 --- a/doc/attributes.qbk +++ b/doc/attributes.qbk @@ -264,6 +264,8 @@ Now we can mark execution scopes with the macros `BOOST_LOG_FUNCTION` and `BOOST After executing `foo` we will be able to see in the log that the `bar` function was called from `foo` and, more precisely, from the case statement that corresponds to the value of `n`. This may be very useful when tracking down subtle bugs that show up only when `bar` is called from a specific location (e.g. if `bar` is being passed invalid arguments in that particular location). +[note The `BOOST_LOG_FUNCTION` macro uses compiler-specific extensions to generate the scope name from the enclosing function. C++11 defines a standard macro `__func__` for this purpose, but it is not universally supported. Additionally, format of the string is not standardized and may vary from one compiler to another. For this reason it is generally advised to use `BOOST_LOG_NAMED_SCOPE` instead of `BOOST_LOG_FUNCTION` to ensure consistent and portable behavior.] + Another good use case is attaching the scope stack information to an exception. With the help of __boost_exception__, this is possible: void bar(int x) diff --git a/doc/changelog.qbk b/doc/changelog.qbk index f100aae..c590af0 100644 --- a/doc/changelog.qbk +++ b/doc/changelog.qbk @@ -15,7 +15,7 @@ * Added indexing operators with [class_log_attribute_name] arguments to [class_log_record] and [class_log_record_view]. The operators behave the same way as the similar operators of [class_log_attribute_value_set] (i.e. return an [class_log_attribute_value] identified by the name). * Added operators for non-const object output to [class_log_basic_formatting_ostream]. ([ticket 9389]) -* Added new format flags "%c", "%C" and "%F" to the [link log.detailed.expressions.formatters.named_scope named scope formatter]. The new flags allow putting function names and source file names of named scopes into the formatted strings. ([ticket 9263) +* Added new format flags "%c", "%C" and "%F" to the [link log.detailed.expressions.formatters.named_scope named scope formatter]. The new flags allow putting function names and source file names of named scopes into the formatted strings. ([ticket 9263]) [*Bug fixes:] diff --git a/doc/expressions.qbk b/doc/expressions.qbk index 041e04b..a571acd 100644 --- a/doc/expressions.qbk +++ b/doc/expressions.qbk @@ -363,14 +363,14 @@ The first argument names the attribute and the second is the format string. The [table Named scope format placeholders [[Placeholder][Meaning][Example]] [[%n] [Scope name]["void bar::foo()"]] - [[%c] [Function name]["bar::foo"]] - [[%C] [Function name, without the function scope]["foo"]] + [[%c] [Function name, if the scope is denoted with `BOOST_LOG_FUNCTION`, otherwise the full scope name. See the note below.]["bar::foo"]] + [[%C] [Function name, without the function scope, if the scope is denoted with `BOOST_LOG_FUNCTION`, otherwise the full scope name. See the note below.]["foo"]] [[%f] [Source file name of the scope]["/home/user/project/foo.cpp"]] [[%F] [Source file name of the scope, without the path]["foo.cpp"]] [[%l] [Line number in the source file]["45"]] ] -[note As described in the [link log.detailed.attributes.named_scope named scope] attribute description, it is possible to use `BOOST_LOG_FUNCTION` macro to automatically generate scope names from the enclosing function name. Unfortunately, the actual format of the generated strings is compiler-dependent and in many cases it includes the complete signature of the function. When "%c" or "%C" format flag is specified, the library attempts to parse the generated string to extract the function name. Depending on the string format, this may fail and in this case the library will fall back to using the whole string (i.e. behave equivalent to the "%n" flag). Note also that the library makes no distinction between scope names generated with `BOOST_LOG_FUNCTION` and explicitly specified in `BOOST_LOG_NAMED_SCOPE` and will attempt to parse either one. For this reason it is advised to avoid specifying scope names that resemble function signatures (in particular, contain parenthesis).] +[note As described in the [link log.detailed.attributes.named_scope named scope] attribute description, it is possible to use `BOOST_LOG_FUNCTION` macro to automatically generate scope names from the enclosing function name. Unfortunately, the actual format of the generated strings is compiler-dependent and in many cases it includes the complete signature of the function. When "%c" or "%C" format flag is specified, the library attempts to parse the generated string to extract the function name. Since C++ syntax is very context dependent and complex, it is not possible to parse function signature correctly in all cases, so the library is basically guessing. Depending on the string format, this may fail or produce incorrect results. In particular, operator function names are currently not supported by the parser. In case if parsing fails the library falls back to using the whole string (i.e. behave equivalent to the "%n" flag). To alleviate the problem the user can replace the problematic `BOOST_LOG_FUNCTION` usage with the `BOOST_LOG_NAMED_SCOPE` macro and explicitly write the desired scope name. Scope names denoted with `BOOST_LOG_NAMED_SCOPE` will not be interpreted by the library and will be output as is. In general, for portability and runtime performance reasons it is preferable to always use `BOOST_LOG_NAMED_SCOPE` and "%n" format flag.] While the format string describes the presentation of each named scope in the list, the following named arguments allow to customize the list traversal and formatting: diff --git a/include/boost/log/attributes/named_scope.hpp b/include/boost/log/attributes/named_scope.hpp index 7176a7f..c6a1759 100644 --- a/include/boost/log/attributes/named_scope.hpp +++ b/include/boost/log/attributes/named_scope.hpp @@ -65,6 +65,17 @@ struct named_scope_entry : public aux::named_scope_list_node //! \endcond { + /*! + * \brief Scope entry type + * + * Describes scope name specifics + */ + enum scope_name_type + { + general, //!< The scope name contains some unstructured string that should not be interpreted by the library + function //!< The scope name contains a function signature + }; + /*! * The scope name (e.g. a function signature) */ @@ -77,6 +88,10 @@ struct named_scope_entry * The line number in the source file */ unsigned int line; + /*! + * The scope name type + */ + scope_name_type type; /*! * Initializing constructor @@ -85,10 +100,11 @@ struct named_scope_entry * * \b Throws: Nothing. */ - named_scope_entry(string_literal const& sn, string_literal const& fn, unsigned int ln) BOOST_NOEXCEPT : + named_scope_entry(string_literal const& sn, string_literal const& fn, unsigned int ln, scope_name_type t = general) BOOST_NOEXCEPT : scope_name(sn), file_name(fn), - line(ln) + line(ln), + type(t) { } }; @@ -354,8 +370,8 @@ public: * \param fn File name, in which the scope is located. * \param ln Line number in the file. */ - sentry(string_literal const& sn, string_literal const& fn, unsigned int ln) BOOST_NOEXCEPT : - m_Entry(sn, fn, ln) + sentry(string_literal const& sn, string_literal const& fn, unsigned int ln, scope_entry::scope_name_type t = scope_entry::general) BOOST_NOEXCEPT : + m_Entry(sn, fn, ln, t) { named_scope::push_scope(m_Entry); } @@ -421,8 +437,8 @@ BOOST_LOG_CLOSE_NAMESPACE // namespace log #ifndef BOOST_LOG_DOXYGEN_PASS -#define BOOST_LOG_NAMED_SCOPE_INTERNAL(var, name, file, line)\ - BOOST_LOG_UNUSED_VARIABLE(::boost::log::attributes::named_scope::sentry, var, (name, file, line)); +#define BOOST_LOG_NAMED_SCOPE_INTERNAL(var, name, file, line, type)\ + BOOST_LOG_UNUSED_VARIABLE(::boost::log::attributes::named_scope::sentry, var, (name, file, line, type)); #endif // BOOST_LOG_DOXYGEN_PASS @@ -430,7 +446,7 @@ BOOST_LOG_CLOSE_NAMESPACE // namespace log * Macro for scope markup. The specified scope name is pushed to the end of the current thread scope list. */ #define BOOST_LOG_NAMED_SCOPE(name)\ - BOOST_LOG_NAMED_SCOPE_INTERNAL(BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_named_scope_sentry_), name, __FILE__, __LINE__) + BOOST_LOG_NAMED_SCOPE_INTERNAL(BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_named_scope_sentry_), name, __FILE__, __LINE__, ::boost::log::attributes::named_scope_entry::general) /*! * Macro for function scope markup. The scope name is constructed with help of compiler and contains current function name. @@ -438,7 +454,8 @@ BOOST_LOG_CLOSE_NAMESPACE // namespace log * * Not all compilers have support for this macro. The exact form of the scope name may vary from one compiler to another. */ -#define BOOST_LOG_FUNCTION() BOOST_LOG_NAMED_SCOPE(BOOST_CURRENT_FUNCTION) +#define BOOST_LOG_FUNCTION()\ + BOOST_LOG_NAMED_SCOPE_INTERNAL(BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_named_scope_sentry_), BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, ::boost::log::attributes::named_scope_entry::function) #include diff --git a/src/named_scope_format_parser.cpp b/src/named_scope_format_parser.cpp index 26f21a1..1c622a2 100644 --- a/src/named_scope_format_parser.cpp +++ b/src/named_scope_format_parser.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,119 @@ namespace aux { BOOST_LOG_ANONYMOUS_NAMESPACE { +iterator_range< const char* > parse_function_name(string_literal const& signature, bool include_scope) +{ + // The algorithm basically is: find the opening parenthesis with function arguments and extract the function name that is directly leftmost of it. + // + // Unfortunately, C++ syntax is too complex and context-dependent, there may be parenthesis in the return type and also in template parameters + // of the function class or the function itself. We cheat and ignore any template parameters at all by skipping any characters in top-level angle brackets. + // + // There is no such graceful solution to the function return types, parsing it correctly requires knowledge of types. Our trick here is to rely on the + // assumption that function name immediately preceeds the opening parenthesis, while in case of function return types there is a space between the return type + // of the function return type and the parenthesis. Technically, the space is not required by C++ grammar, so some compiler could omit it and the parser + // would get confused by it. Also, some compiler could insert a space after the function name and that would break it too. But for now I can't find + // another equivalently fast and viable solution. + // + // When the function name is detected, the algorithm searches for its beginning backwards, again skipping any template arguments. The beginning + // is detected by encountering a delimiter character, which can be a space or another punctuation character that is not allowed in function names. + // A colon served as a delimiter as well if the scope name is to be omitted. + // + // Operators pose another problem at this stage. They are curently not supported. + // + // Note that the algorithm should be tolerant to user's custom scope names which may not be function names at all. For this reason in case of any failure + // just return the original string intact. + + const char* const begin = signature.c_str(); + const char* const end = begin + signature.size(); + const char* p = begin; + while (p != end) + { + // Search for the opening parenthesis or template arguments + p += std::strcspn(p, "(<"); + if (p == begin || p == end) + break; + + char c = *p; + if (c == '(') + { + c = *(p - 1); + if (c != ' ') + { + // Assume we found the function name, find its beginning + const char* const name_end = p--; + while (p != begin) + { + c = *p; + if (c == ' ' || c == '*' || c == '&' || (!include_scope && c == ':')) + { + const char* const name_begin = p + 1; + if (name_begin < name_end) + { + // We found it + return iterator_range< const char* >(name_begin, name_end); + } + else + { + // Function name cannot be empty + goto NotFoundL; + } + } + else if (c == '>') + { + // It's template parameters, skip them + unsigned int depth = 1; + --p; + while (p != begin && depth > 0) + { + c = *p; + if (c == '<') + --depth; + else if (c == '>') + ++depth; + --p; + } + } + else + { + --p; + } + } + + // If it came to this then the supposed function name begins from the start of the signature string (i.e. no return type at all). + // This is the case for constructors, destructors and conversion operators. + return iterator_range< const char* >(p, name_end); + } + else + { + // This must be the function return type, process characters inside the parenthesis + ++p; + } + } + else if (c == '<') + { + // Template parameters opened + unsigned int depth = 1; + do + { + ++p; + p += std::strcspn(p, "><"); + + if (p == end) + break; + c = *p; + if (c == '>') + --depth; + else + ++depth; + } + while (depth > 0); + } + } + +NotFoundL: + return iterator_range< const char* >(signature.c_str(), signature.c_str() + signature.size()); +} + template< typename CharT > class named_scope_formatter { @@ -78,36 +192,29 @@ public: } }; - template< bool OmitScopeV > 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 { - const char* const begin = value.scope_name.c_str(); - const char* const paren = std::strchr(begin, '('); - if (paren) + if (value.type == attributes::named_scope_entry::function) { - const char* p = paren; - for (; p != begin; --p) - { - const char c = *(p - 1); - if (OmitScopeV && c == ':') - break; - if (c == ' ') - break; - } - - if (p != begin && p != paren) - { - strm.write(p, paren - p); - return; - } + iterator_range< const char* > function_name = parse_function_name(value.scope_name, m_include_scope); + strm.write(function_name.begin(), function_name.size()); + } + else + { + strm << value.scope_name; } - - strm << value.scope_name; } + + private: + const bool m_include_scope; }; struct full_file_name @@ -240,13 +347,13 @@ do_parse_named_scope_format(const CharT* begin, const CharT* end) case 'c': if (!literal.empty()) fmt.add_formatter(typename formatter_type::literal(literal)); - fmt.add_formatter(typename formatter_type::BOOST_NESTED_TEMPLATE function_name< false >()); + 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::BOOST_NESTED_TEMPLATE function_name< true >()); + fmt.add_formatter(typename formatter_type::function_name(false)); break; case 'f': diff --git a/test/run/form_named_scope.cpp b/test/run/form_named_scope.cpp index 23875a1..0d79799 100644 --- a/test/run/form_named_scope.cpp +++ b/test/run/form_named_scope.cpp @@ -43,16 +43,6 @@ namespace { static logging::string_literal scope1() { return logging::str_literal("scope1"); } static logging::string_literal scope2() { return logging::str_literal("scope2"); } - static logging::string_literal function_name1() { return logging::str_literal("int main(int, char *[])"); } - static logging::string_literal function_name2() { return logging::str_literal("int __cdecl main(int, char *[])"); } - static logging::string_literal function_name3() { return logging::str_literal("namespace_name::type foo()"); } - static logging::string_literal function_name4() { return logging::str_literal("namespace_name::type& foo::bar(int[], std::string const&)"); } - static logging::string_literal function_name5() { return logging::str_literal("void* namespc::foo::bar()"); } - static logging::string_literal function_name6() { return logging::str_literal("void* namespc::foo::bar(int) const"); } - static logging::string_literal function_name7() { return logging::str_literal("void (*)() namespc::foo::bar(int (my_class::*)(float*), my_class*) const volatile"); } - static logging::string_literal function_name8() { return logging::str_literal("void (*)(const int (&)[]) namespc::foo::bar(int (my_class::*)(float*), my_class*)"); } - static logging::string_literal function_name9() { return logging::str_literal("std::function namespc::foo::bar(int (my_class::*)(float*), my_class*)"); } - static logging::string_literal file() { return logging::str_literal(__FILE__); } static logging::string_literal posix_file() { return logging::str_literal("/home/user/posix_file.cpp"); } static logging::string_literal windows_file1() { return logging::str_literal("C:\\user\\windows_file1.cpp"); } @@ -304,6 +294,74 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(scopes_filename_formatting_windows, CharT, char_ty #endif // defined(BOOST_WINDOWS) +namespace { + +struct named_scope_test_case +{ + logging::string_literal scope_name; + const char* function_name; + const char* function_name_no_scope; +}; + +const named_scope_test_case named_scope_test_cases[] = +{ + // Generic signatures + { logging::str_literal("int main(int, char *[])"), "main", "main" }, + { logging::str_literal("namespace_name::type foo()"), "foo", "foo" }, + { logging::str_literal("namespace_name::type& foo::bar(int[], std::string const&)"), "foo::bar", "bar" }, + { logging::str_literal("void* namespc::foo::bar()"), "namespc::foo::bar", "bar" }, + { logging::str_literal("void* namespc::foo::bar(int) const"), "namespc::foo::bar", "bar" }, + + // MSVC-specific + { logging::str_literal("int __cdecl main(int, char *[])"), "main", "main" }, + { logging::str_literal("struct namespc::strooct __cdecl foo3(int [])"), "foo3", "foo3" }, + { logging::str_literal("void (__cdecl *__cdecl foo4(void))(void)"), "foo4", "foo4" }, // function returning pointer to function + { logging::str_literal("void (__cdecl *__cdecl foo5(void (__cdecl *)(void)))(void)"), "foo5", "foo5" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class::member1(void (__cdecl *)(void)))(void)"), "namespc::my_class::member1", "member1" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class::member2(int))(void)"), "namespc::my_class::member2", "member2" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class::member2(void (__cdecl *)(void)))(void)"), "namespc::my_class::member2", "member2" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class::member3(void))(void)"), "namespc::my_class::member3", "member3" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class::member1(void (__cdecl *)(void)))(void)"), "namespc::my_class::member1", "member1" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class::member2(int))(void)"), "namespc::my_class::member2", "member2" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class::member2(void (__cdecl *)(void)))(void)"), "namespc::my_class::member2", "member2" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class::member3(void))(void)"), "namespc::my_class::member3", "member3" }, + { logging::str_literal("void (__cdecl namespc::my_class2::* __cdecl namespc::foo6(void (__cdecl *)(void)))(void)"), "namespc::foo6", "foo6" }, + { logging::str_literal("struct namespc::my_class __cdecl namespc::foo7(void)"), "namespc::foo7", "foo7" }, + { logging::str_literal("void (__cdecl namespc::my_class2::* (&__cdecl namespc::foo8(void (__cdecl *)(void)))[2])(void)"), "namespc::foo8", "foo8" }, + + // GCC-specific + { logging::str_literal("namespc::strooct foo3(int*)"), "foo3", "foo3" }, + { logging::str_literal("void (* foo4())()"), "foo4", "foo4" }, // function returning pointer to function + { logging::str_literal("void (* foo5(pfun2_t))()"), "foo5", "foo5" }, + { logging::str_literal("static void (* namespc::my_class::member1(pfun2_t))() [with T = int; pfun1_t = void (*)(); pfun2_t = void (*)()]"), "namespc::my_class::member1", "member1" }, + { logging::str_literal("static void (* namespc::my_class::member2(U))() [with U = int; T = int; pfun2_t = void (*)()]"), "namespc::my_class::member2", "member2" }, + { logging::str_literal("static void (* namespc::my_class::member2(U))() [with U = void (*)(); T = int; pfun2_t = void (*)()]"), "namespc::my_class::member2", "member2" }, + { logging::str_literal("static void (* namespc::my_class::member3())() [with void (* Fun)() = foo1; T = int; pfun2_t = void (*)()]"), "namespc::my_class::member3", "member3" }, + { logging::str_literal("static void (* namespc::my_class::member1(pfun2_t))() [with T = void (*)(); pfun1_t = void (*)(); pfun2_t = void (*)()]"), "namespc::my_class::member1", "member1" }, + { logging::str_literal("static void (* namespc::my_class::member2(U))() [with U = int; T = void (*)(); pfun2_t = void (*)()]"), "namespc::my_class::member2", "member2" }, + { logging::str_literal("static void (* namespc::my_class::member2(U))() [with U = void (*)(); T = void (*)(); pfun2_t = void (*)()]"), "namespc::my_class::member2", "member2" }, + { logging::str_literal("static void (* namespc::my_class::member3())() [with void (* Fun)() = foo1; T = void (*)(); pfun2_t = void (*)()]"), "namespc::my_class::member3", "member3" }, + { logging::str_literal("void (namespc::my_class2::* namespc::foo6(pfun2_t))()"), "namespc::foo6", "foo6" }, + { logging::str_literal("namespc::my_class namespc::foo7()"), "namespc::foo7", "foo7" }, + { logging::str_literal("void (namespc::my_class2::* (& namespc::foo8(pfun2_t))[2])()"), "namespc::foo8", "foo8" }, + { logging::str_literal("namespc::my_class2::my_class2()"), "namespc::my_class2::my_class2", "my_class2" }, // constructor + { logging::str_literal("namespc::my_class2::~my_class2()"), "namespc::my_class2::~my_class2", "~my_class2" }, // destructor + // Operators not supported for now +// { logging::str_literal("void namespc::my_class2::operator=(const namespc::my_class2&)"), "namespc::my_class2::operator=", "operator=" }, +// { logging::str_literal("void namespc::my_class2::operator*() const"), "namespc::my_class2::operator*", "operator*" }, +// { logging::str_literal("void namespc::my_class2::operator()()"), "namespc::my_class2::operator()", "operator()" }, +// { logging::str_literal("bool namespc::my_class2::operator<(int) const"), "namespc::my_class2::operator<", "operator<" }, +// { logging::str_literal("bool namespc::my_class2::operator>(int) const"), "namespc::my_class2::operator>", "operator>" }, +// { logging::str_literal("bool namespc::my_class2::operator<=(int) const"), "namespc::my_class2::operator<=", "operator<=" }, +// { logging::str_literal("bool namespc::my_class2::operator>=(int) const"), "namespc::my_class2::operator>=", "operator>=" }, +// { logging::str_literal("namespc::my_class2::operator bool() const"), "namespc::my_class2::operator bool", "operator bool" }, + + // BOOST_CURRENT_FUNCTION fallback value + { logging::str_literal("(unknown)"), "(unknown)", "(unknown)" } +}; + +} // namespace + // Function name formatting BOOST_AUTO_TEST_CASE_TEMPLATE(scopes_scope_function_name_formatting, CharT, char_types) { @@ -328,70 +386,14 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(scopes_scope_function_name_formatting, CharT, char record_view rec = make_record_view(set1); - // File names without the full path + for (unsigned int i = 0; i < sizeof(named_scope_test_cases) / sizeof(*named_scope_test_cases); ++i) { - sentry scope1(data::function_name1(), data::file(), line1); + sentry scope1(named_scope_test_cases[i].scope_name, data::file(), line1, attrs::named_scope_entry::function); string str; osstream strm(str); - strm << "main"; - BOOST_CHECK(check_formatting(data::scope_function_name_format(), rec, strm.str())); + strm << named_scope_test_cases[i].function_name; + BOOST_CHECK_MESSAGE(check_formatting(data::scope_function_name_format(), rec, strm.str()), "Scope name: " << named_scope_test_cases[i].scope_name); } - { - sentry scope1(data::function_name2(), data::file(), line1); - string str; - osstream strm(str); - strm << "main"; - BOOST_CHECK(check_formatting(data::scope_function_name_format(), rec, strm.str())); - } - { - sentry scope1(data::function_name3(), data::file(), line1); - string str; - osstream strm(str); - strm << "foo"; - BOOST_CHECK(check_formatting(data::scope_function_name_format(), rec, strm.str())); - } - { - sentry scope1(data::function_name4(), data::file(), line1); - string str; - osstream strm(str); - strm << "foo::bar"; - BOOST_CHECK(check_formatting(data::scope_function_name_format(), rec, strm.str())); - } - { - sentry scope1(data::function_name5(), data::file(), line1); - string str; - osstream strm(str); - strm << "namespc::foo::bar"; - BOOST_CHECK(check_formatting(data::scope_function_name_format(), rec, strm.str())); - } - { - sentry scope1(data::function_name6(), data::file(), line1); - string str; - osstream strm(str); - strm << "namespc::foo::bar"; - BOOST_CHECK(check_formatting(data::scope_function_name_format(), rec, strm.str())); - } -// { -// sentry scope1(data::function_name7(), data::file(), line1); -// string str; -// osstream strm(str); -// strm << "namespc::foo::bar"; -// BOOST_CHECK(check_formatting(data::scope_function_name_format(), rec, strm.str())); -// } -// { -// sentry scope1(data::function_name8(), data::file(), line1); -// string str; -// osstream strm(str); -// strm << "namespc::foo::bar"; -// BOOST_CHECK(check_formatting(data::scope_function_name_format(), rec, strm.str())); -// } -// { -// sentry scope1(data::function_name9(), data::file(), line1); -// string str; -// osstream strm(str); -// strm << "namespc::foo::bar"; -// BOOST_CHECK(check_formatting(data::scope_function_name_format(), rec, strm.str())); -// } } // Function name without scope formatting @@ -418,69 +420,43 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(scopes_function_name_formatting, CharT, char_types record_view rec = make_record_view(set1); - // File names without the full path + for (unsigned int i = 0; i < sizeof(named_scope_test_cases) / sizeof(*named_scope_test_cases); ++i) { - sentry scope1(data::function_name1(), data::file(), line1); + sentry scope1(named_scope_test_cases[i].scope_name, data::file(), line1, attrs::named_scope_entry::function); string str; osstream strm(str); - strm << "main"; - BOOST_CHECK(check_formatting(data::function_name_format(), rec, strm.str())); + strm << named_scope_test_cases[i].function_name_no_scope; + BOOST_CHECK_MESSAGE(check_formatting(data::function_name_format(), rec, strm.str()), "Scope name: " << named_scope_test_cases[i].scope_name); } - { - sentry scope1(data::function_name2(), data::file(), line1); - string str; - osstream strm(str); - strm << "main"; - BOOST_CHECK(check_formatting(data::function_name_format(), rec, strm.str())); - } - { - sentry scope1(data::function_name3(), data::file(), line1); - string str; - osstream strm(str); - strm << "foo"; - BOOST_CHECK(check_formatting(data::function_name_format(), rec, strm.str())); - } - { - sentry scope1(data::function_name4(), data::file(), line1); - string str; - osstream strm(str); - strm << "bar"; - BOOST_CHECK(check_formatting(data::function_name_format(), rec, strm.str())); - } - { - sentry scope1(data::function_name5(), data::file(), line1); - string str; - osstream strm(str); - strm << "bar"; - BOOST_CHECK(check_formatting(data::function_name_format(), rec, strm.str())); - } - { - sentry scope1(data::function_name6(), data::file(), line1); - string str; - osstream strm(str); - strm << "bar"; - BOOST_CHECK(check_formatting(data::function_name_format(), rec, strm.str())); - } -// { -// sentry scope1(data::function_name7(), data::file(), line1); -// string str; -// osstream strm(str); -// strm << "bar"; -// BOOST_CHECK(check_formatting(data::function_name_format(), rec, strm.str())); -// } -// { -// sentry scope1(data::function_name8(), data::file(), line1); -// string str; -// osstream strm(str); -// strm << "bar"; -// BOOST_CHECK(check_formatting(data::function_name_format(), rec, strm.str())); -// } -// { -// sentry scope1(data::function_name9(), data::file(), line1); -// string str; -// osstream strm(str); -// strm << "bar"; -// BOOST_CHECK(check_formatting(data::function_name_format(), rec, strm.str())); -// } } +// The test checks that function name formatters do not affect scopes denoted with BOOST_LOG_NAMED_SCOPE +BOOST_AUTO_TEST_CASE_TEMPLATE(function_name_does_not_affect_non_function_scopes, CharT, char_types) +{ + typedef attrs::named_scope named_scope; + typedef named_scope::sentry sentry; + typedef attrs::named_scope_list scopes; + typedef attrs::named_scope_entry scope; + + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef named_scope_test_data< CharT > data; + + named_scope attr; + + attr_set set1; + set1[data::attr1()] = attr; + + record_view rec = make_record_view(set1); + + { + BOOST_LOG_NAMED_SCOPE("void foo()"); + string str; + osstream strm(str); + strm << "void foo()"; + BOOST_CHECK(check_formatting(data::scope_function_name_format(), rec, strm.str())); + BOOST_CHECK(check_formatting(data::function_name_format(), rec, strm.str())); + } +}