boost.log std::exception formatter unable to find operator<< overload in own namespace

前端 未结 1 1393
栀梦
栀梦 2021-01-28 04:26

I have created a simple formatter for boost.log like shown in this example for std::exception. Now if i want to use the overloaded operator, which is defined in my

相关标签:
1条回答
  • 2021-01-28 05:11

    There are two problems in your question that I'll address separately below.

    1. Name lookup.

    In C++, unqualified function calls, such as the operator<< in your streaming expression, involve the unqualified name lookup, which basically produces a set of candidate functions that you may be calling. Out of this set the actual function is then selected according to the overload resolution rules. For the call to complete it is essential that (a) the intended function is in the candidate set and (b) that it is not ambiguous with respect to other functions in the set, considering the way the function is called (number of provided arguments and their types, explicit template parameters, etc.) In your case (a) is not fulfilled.

    Put simply and closer to your code, the operator<< lookup is performed in three stages. First, the compiler looks for a member operator<< in the right hand operand class. The operators defined in Boost.Log streams are found this way. Next, a free standing (non-member) operator is looked for in the namespaces enclosing the function call, starting from the inner namespaces and moving outwards. Names imported with using directives and declarations are also considered here. This lookup ends as soon as any operator<< is found. Note that the namespaces considered are different when you invoke the operator from your code and when the operator is invoked from Boost.Log, when you use log::add_value. In the first case you import my_space into your current namespace, so your operator is found. In the latter case Boost.Log does not import your namespace, so your operator is not found.

    Lastly, the compiler performs argument-dependent lookup (ADL) to collect additional function you might be calling. Basically, it looks for the operator in the associated namespaces which are comprised of:

    • The namespace where the type of every argument in the function call is declared. That means that namespace std is considered in both cases because std::exception is declared there. When Boost.Log is used, its internal namespace where the stream type is declared is also considered (it contains a few operators but none of them accepts std::exception).
    • If the function is a template, namespaces of its template argument types are also similarly considered.
    • If the function argument types or function template argument types are templates themselves, those template arguments' namespaces are also similarly considered. This is done recursively.

    The ADL finds operator<< in namespace std but none of them accepts std::exception. The net effect of the operator lookup here is that your operator in my_space is only found because of your using-directive, which doesn't help when the operator is called from another point of the program, such as Boost.Log code.

    When implementing operators, the best practice is to rely on ADL finding those operators. This means that the operators supporting a type must be placed in the same namespace where that type is declared. In case of std::exception, this is namespace std. Technically, adding stuff to namespace std results in undefined behavior (as per [namespace.std]/1), so the best solution would be to define your own stream manipulator in your namespace and use it to output exceptions into streams:

    namespace my_space {
    
    template< typename T >
    struct my_manip
    {
      T const& value;
    };
    template< typename T >
    my_manip< T > to_stream(T const& value) {
      my_manip< T > m = { value };
      return m;
    }
    
    template< typename CharT, typename TraitsT >
    std::basic_ostream< CharT, TraitsT >& operator<< (
      std::basic_ostream< CharT, TraitsT >& strm,
      my_manip< std::exception > const& e)
    {
      // some printout stuff here
      strm << e.value.what();
      return strm;
    }
    
    } // namespace my_space
    
    try {
      // ...
    }
    catch (std::exception& e) {
      std::cerr << my_space::to_stream(e) << std::endl;
    }
    

    You can also provide a generalized operator<< for my_manip, if you like.

    2. Adding Boost.Log attributes.

    If your original intention is to attach exceptions to a log record then I'm afraid you're not doing it the right way. The add_value manipulator does not determine the runtime type of the value you provide, which means it saves a copy of std::exception, thus losing any diagnostic information that is provided by the derived class (including the what() message). This is known as object slicing.

    You can go multiple routes to implement what you want. First, you can format the error message in the place where you caught the exception.

    catch (std::exception& e) {
      MY_LOG_ERROR(slg) << e.what();
    }
    

    You won't be able to use it as an attribute value in sinks or formatters, but this might be enough for you. You can also use your custom manipulator, of course:

    catch (std::exception& e) {
      MY_LOG_ERROR(slg) << my_space::to_stream(e);
    }
    

    If you do need it as an attribute value, you will have to choose what kind of information you want. For example, if all you need is the error message, you could attach it instead of the exception:

    catch (std::exception& e) {
      MY_LOG_ERROR(slg) << log::add_value("ErrorMessage", std::string(e.what()));
    }
    

    If the exception itself is what you need then you need C++11 exception_ptr:

    catch (std::exception& e) {
      MY_LOG_ERROR(slg) << log::add_value("Exception", std::current_exception());
    }
    

    In C++03 you can use Boost.Exception or implement your custom mechanism of determining the dynamic type of the exception. Note that the standard library or Boost.Exception also don't provide operator<< for exception_ptr, so you'll have to implement custom formatters for that.

    0 讨论(0)
提交回复
热议问题