diff --git a/include/nestlog/function.hpp b/include/nestlog/function.hpp new file mode 100644 index 00000000..da6a3f6e --- /dev/null +++ b/include/nestlog/function.hpp @@ -0,0 +1,190 @@ +/* @file function.hpp */ + +#include +#include + +namespace xo { + enum function_style { + /* literal: print given name, no alterations */ + FS_Literal, + /* pretty: print name, surrounded by [] */ + FS_Pretty, + /* streamlined: remove extraneous detail, try to print something like class::method */ + FS_Streamlined, + /* simple: remove everything except function/method name */ + FS_Simple + }; + + /* Tag to drive header-only expression */ + template + class function_name_impl { + public: + function_name_impl(function_style style, + std::string_view pretty) + : style_{style}, pretty_{pretty} {} + + function_style style() const { return style_; } + std::string_view const & pretty() const { return pretty_; } + + /* e.g. + * std::vector xo::sometemplateclass::fib(int, char**) + * ^ ^ + * p q + */ + static void print_simple(std::ostream & os, std::string_view const & s) { + std::size_t p = exclude_return_type(s); + std::string_view s2 = s.substr(p); + std::size_t q = find_toplevel_sep(s2, true /*last_flag*/); + + print_aux(os, s2.substr(q)); + } /*print_simple*/ + + /* e.g. + * std::vector xo::sometemplateclass::fib(int, char**) + * ^ ^ + * p q + */ + static void print_streamlined(std::ostream & os, std::string_view const & s) { + std::size_t p = exclude_return_type(s); + std::string_view s2 = s.substr(p); + std::size_t q = find_toplevel_sep(s2, false /*!last_flag*/); + + print_aux(os, s2.substr(q)); + } /*print_streamlined*/ + + private: + static std::size_t exclude_return_type(std::string_view const & s) { + /* strategy: + * - scan right-to-left + * - ignore anything between matching <>, () pairs (i.e. anything nested) + * - stop at rightmost toplevel space --> return suffix following that space + */ + std::size_t nesting_level = 0; + + std::size_t z = s.size(); + for (std::size_t rp = 0; rp < z; ++rp) { + std::size_t p = z-1-rp; + char ch = s[p]; + + if (ch == '<' || ch == '(') + ++nesting_level; + + if (nesting_level == 0) { + if (ch == ' ') + return p + 1; + } + + if (ch == '>' || ch == ')') + --nesting_level; + } + + return 0; + } /*exclude_return_type*/ + + /* e.g. + * xo::ns::someclass::somemethod(xo::enum1, std::vector) + * ^ + * return this pos + * + * last_flag: return pos after last :: + * !last_flag: return pos after 2nd-last :: + */ + static std::size_t find_toplevel_sep(std::string_view const & s, bool last_flag) { + /* strategy: + * - scan left-to-right + * - ignore anything between matching <>, () pairs (i.e. anything nested) + * - count :: pairs + * - remember 2nd-last :: pair; reports pos just after it + * + * note: + * - if no :: pairs, or only one such pair, return 0 + */ + std::size_t nesting_level = 0; + + std::size_t pos_after_last_sep = 0; + std::size_t pos_after_2ndlast_sep = 0; + + for (std::size_t p = 0; p < s.size(); ++p) { + char ch = s[p]; + + if (ch == '<' || ch == '(') + ++nesting_level; + + if (nesting_level == 0) { + if ((ch == ':') + && (p+1 < s.size()) + && s[p+1] == ':') + { + pos_after_2ndlast_sep = pos_after_last_sep; + pos_after_last_sep = p+2; + ++p; /* skipping 1st : in separator */ + } + } + + if (ch == '>' || ch == ')') + --nesting_level; + } + + return last_flag ? pos_after_last_sep : pos_after_2ndlast_sep; + } /*find_toplevel_sep*/ + + /* fib(int, char **) --> fib + * quux(std::vector>) -> quux + * foo::bar>() -> foo::bar + */ + static void print_aux(std::ostream & os, std::string_view const & s) { + //std::cerr << "print_aux: s=" << s << std::endl; + + /* strategy: + * - print left-to-right, omit anything between matching <> or () pairs. + * - don't keep track of which is which, so would also match < with ) etc; + * this acceptable since pretty functions won't visit this corner case + */ + std::size_t nesting_level = 0; + + for (char ch : s) { + if (ch == '<' || ch == '(') + ++nesting_level; + + if (nesting_level == 0) + os << ch; + + if (ch == '>' || ch == ')') + --nesting_level; + } + } /*print_aux*/ + + private: + /* FS_Simple | FS_Pretty (= FS_Literal) | FS_Streamlined */ + function_style style_; + /* e.g. __PRETTY_FUNCTION__ */ + std::string_view pretty_; + }; /*function_name_impl*/ + + using function_name = function_name_impl; + + inline std::ostream & + operator<<(std::ostream & os, + function_name const & fn) + { + switch(fn.style()) { + case FS_Literal: + os << fn.pretty(); + break; + case FS_Pretty: + os << "[" << fn.pretty() << "]"; + break; + case FS_Simple: + function_name::print_simple(os, fn.pretty()); + break; + case FS_Streamlined: + /* omit namespace qualifiers and template arguments */ + function_name::print_streamlined(os, fn.pretty()); + break; + } + + return os; + } /*operator<<*/ +} /*namespace xo*/ + +/* end function.hpp */