简体   繁体   English

如何用支持__LINE__和__FILE__的内联函数替换我的c ++异常宏?

[英]How can I replace my c++ exception macro with an inline function with __LINE__ and __FILE__ support?

I currently read the book Effective C++ from Scott Meyers. 我目前阅读Scott Meyers的“Effective C ++”一书。 It says I should prefer inline functions over #define for function-like macros. 它说我应该#define inline函数来实现类似函数的宏。

Now I try to code an inline function to replace my exception macro. 现在我尝试编写内联函数来替换我的异常宏。 My old macro looks like this: 我的旧宏看起来像这样:

#define __EXCEPTION(aMessage) \
{ \
    std::ostringstream stream; \
    stream << "EXCEPTION: " << aMessage << ", file " <<__FILE__ << " line " << __LINE__; \
    throw ExceptionImpl(stream.str()); \
}

My new inline function is this: 我的新内联函数是这样的:

inline void __EXCEPTION(const std::string aMessage)
{
   std::ostringstream stream;
   stream << "EXCEPTION: " << aMessage << ", file " <<__FILE__ << " line " << __LINE__;
   throw ExceptionImpl(stream.str());
}

As probably some people already expect, now the __FILE__ and __LINE__ macros are useless, because they refer always to the C++-file with the definition of the inline function. 正如一些人已经预料的那样,现在__FILE____LINE__宏是无用的,因为它们总是引用带有内联函数定义的C ++文件。

Is there any way to circumvent this behaviour or should I stick with my old macro? 有没有办法规避这种行为,还是应该坚持我的旧宏? I read this threads here, and I already suspect that there is probably no way of my second example to work fine: 我在这里阅读了这些帖子,我已经怀疑我的第二个例子可能无法正常工作:

Don't use __ (double underscore) as it's reserved. 不要使用__ (双下划线),因为它是保留的。 Having an inline function is better. inline函数更好。
However, here you need a mix of macro and the function, hence you can do following: 但是,这里需要混合使用宏和函数,因此您可以执行以下操作:

#define MY_EXCEPTION(aMessage) MyException(aMessage, __FILE__, __LINE__) 

inline void MyException(const std::string aMessage,
                        const char* fileName,
                        const std::size_t lineNumber)
{
   std::ostringstream stream;
   stream << "EXCEPTION: " << aMessage << ", file " << fileName << " line " << lineNumber;
   throw ExceptionImpl(stream.str());
}

Please consider that there is another difference between using the #define function-like macro in your case in comparison to inline functions. 请注意,在内inline函数中使用类似#define函数的宏与inline函数之间存在另一个区别。 You could have used streaming operators and parameters in your macro's invocation to be composed as your message's text: 您可以在宏的调用中使用流操作符和参数作为消息的文本:

__EXCEPTION( "My message with a value " << val )

But most times I've needed something like this, it was to check on a certain condition (like an assertion). 但是大多数时候我需要这样的东西,它是检查某种条件(如断言)。 So you could extend @iammilind's example with something like: 所以你可以扩展@ iammilind的例子:

#define MY_EXCEPTION_COND( cond )                  \
    if (bool(cond) == false)                       \
    {                                              \
        std::string _s( #cond " == false" );       \
        MyException(_s, __FILE__, __LINE__);       \
    }

Or something a little more specialized where the values are also printed: 或者在打印值时更专业的东西:

template <typename T>
inline void MyExceptionValueCompare(const T&          a,
                                    const T&          b,
                                    const char*       fileName,
                                    const std::size_t lineNumber)
{
    if (a != b)
    {
        std::ostringstream stream;
        stream << "EXCEPTION: " << a << " != " << b << ", file " << fileName << " line " << lineNumber;
        throw ExceptionImpl(stream.str());
    }
}

#define MY_EXCEPTION_COMP( a, b )  MyExceptionValueCompare(a, b, __FILE__, __LINE__)

Another approach you may want to take a look at is Microsoft's usage of their __LineInfo class in the Microsoft::VisualStudio::CppUnitTestFramework namespace (VC\\UnitTest\\Include\\CppUnitTestAssert.h). 您可能想要了解的另一种方法是Microsoft在Microsoft::VisualStudio::CppUnitTestFramework命名空间(VC \\ UnitTest \\ Include \\ CppUnitTestAssert.h)中使用其__LineInfo类。 See https://msdn.microsoft.com/en-us/library/hh694604.aspx 请参阅https://msdn.microsoft.com/en-us/library/hh694604.aspx

I see this is an old question but I think that the approach of printing the line in the exception macro is fundamentally flawed and I think I have a better alternative. 我认为这是一个老问题,但我认为在异常宏中打印行的方法存在根本缺陷,我认为我有更好的选择。 I assume that the macro is used similar to the following code: 我假设宏的使用类似于以下代码:

try {
    /// code 
    throw;
} 
catch (...) { __EXCEPTION(aMessage); }

With this approach the macro prints the location where the exception was catch'ed . 通过这种方法,宏可以打印catch'ed异常的位置 But for troubleshooting and debugging the location where it was throw'n is usually more useful. 但对于故障排除和调试它哪里的位置throw'n通常更加有用。

To get that information, we can attach the __FILE__ and __LINE__ macros to the exception. 要获取该信息,我们可以将__FILE____LINE__宏附加到异常。 However, we still can't get completely rid of macros, but we get at least the exact throw location: 但是,我们仍然无法完全摆脱宏,但我们至少得到了精确的throw位置:

#include <iostream>
#include <exception>
#include <string>

#define MY_THROW(msg) throw my_error(__FILE__, __LINE__, msg)

struct my_error : std::exception
{
    my_error(const std::string & f, int l, const std::string & m)
        :   file(f)
        ,   line(l)
        ,   message(m)
    {}
    std::string file;
    int line;
    std::string message;

    char const * what() const throw() { return message.c_str(); }
};

void my_exceptionhandler()
{
    try {
        throw; // re-throw the exception and capture the correct type
    } 
    catch (my_error & e)
    {
        std::cout << "Exception: " << e.what() << " in line: " << e.line << std::endl;
    }
}

int main()
{
    try {

        MY_THROW("error1");

    } catch(...) { my_exceptionhandler(); }
}

There is one additional improvement possible if we are willing to use boost::exception : We can get rid of macro definitons at least in our own code. 如果我们愿意使用boost::exception还有一个额外的改进:我们可以至少在我们自己的代码中摆脱宏定义。 The whole program gets shorter and the locations of code execution and error handling can be nicely separated: 整个程序变得更短,代码执行和错误处理的位置可以很好地分开:

#include <iostream>
#include <boost/exception/all.hpp>

typedef boost::error_info<struct tag_error_msg, std::string> error_message;
struct error : virtual std::exception, virtual boost::exception { };
struct my_error:            virtual error { };

void my_exceptionhandler()
{
    using boost::get_error_info;

    try {
        throw;
    }
    catch(boost::exception & e)
    {
        char const * const * file = get_error_info<boost::throw_file>(e);
        int const * line = get_error_info<boost::throw_line>(e);
        char const * const * throw_func = get_error_info<boost::throw_function>(e);
        std::cout << diagnostic_information(e, false) 
                  << " in File: " << *file << "(" << *line << ")"
                     " in Function: " << *throw_func;
    }
}

int main()
{
    try {

        BOOST_THROW_EXCEPTION(my_error() << error_message("Test error"));

    } catch(...) { my_exceptionhandler(); }
}

With std::experimental::source_location , you might do: 使用std :: experimental :: source_location ,您可以执行以下操作:

#include <experimental/source_location>

void THROW_EX(const std::string_view& message,
              const std::experimental::source_location& location
                  = std::experimental::source_location::current())
{
    std::ostringstream stream;
    stream << "EXCEPTION: " << message
           << ", file " << location.file_name()
           << " line " << location.line();
    throw ExceptionImpl(stream.str());
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM