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

时间:2022-09-01 14:27:21

I currently read the book Effective C++ from Scott Meyers. It says I should prefer inline functions over #define for function-like macros.

我目前阅读Scott Meyers的“Effective C ++”一书。它说我应该优先使用#define的内联函数来实现类似函数的宏。

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:

有没有办法规避这种行为,还是应该坚持使用我的旧宏?我在这里阅读了这些帖子,我已经怀疑我的第二个例子可能无法正常工作:

3 个解决方案

#1


17  

Don't use __ (double underscore) as it's reserved. Having an inline function is better.
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());
}

#2


3  

Please consider that there is another difference between using the #define function-like macro in your case in comparison to inline functions. You could have used streaming operators and parameters in your macro's invocation to be composed as your message's text:

请考虑在内联函数中使用类似#define函数的宏与内联函数之间存在另一个区别。您可以在宏的调用中使用流操作符和参数作为消息的文本:

__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). See https://msdn.microsoft.com/en-us/library/hh694604.aspx

您可能想要了解的另一种方法是Microsoft在Microsoft :: VisualStudio :: CppUnitTestFramework命名空间(VC \ UnitTest \ Include \ CppUnitTestAssert.h)中使用其__LineInfo类。请参阅https://msdn.microsoft.com/en-us/library/hh694604.aspx

#3


3  

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. But for troubleshooting and debugging the location where it was throw'n is usually more useful.

通过这种方法,宏可以打印捕获异常的位置。但是对于故障排除和调试它所在的位置通常更有用。

To get that information, we can attach the __FILE__ and __LINE__ macros to the exception. However, we still can't get completely rid of macros, but we get at least the exact throw location:

要获取该信息,我们可以将__FILE__和__LINE__宏附加到异常。但是,我们仍然无法完全摆脱宏,但我们至少得到了精确的抛出位置:

#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. The whole program gets shorter and the locations of code execution and error handling can be nicely separated:

如果我们愿意使用boost :: exception,还有一个额外的改进:我们可以至少在我们自己的代码中摆脱宏定义。整个程序变得更短,代码执行和错误处理的位置可以很好地分开:

#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(); }
}

#1


17  

Don't use __ (double underscore) as it's reserved. Having an inline function is better.
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());
}

#2


3  

Please consider that there is another difference between using the #define function-like macro in your case in comparison to inline functions. You could have used streaming operators and parameters in your macro's invocation to be composed as your message's text:

请考虑在内联函数中使用类似#define函数的宏与内联函数之间存在另一个区别。您可以在宏的调用中使用流操作符和参数作为消息的文本:

__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). See https://msdn.microsoft.com/en-us/library/hh694604.aspx

您可能想要了解的另一种方法是Microsoft在Microsoft :: VisualStudio :: CppUnitTestFramework命名空间(VC \ UnitTest \ Include \ CppUnitTestAssert.h)中使用其__LineInfo类。请参阅https://msdn.microsoft.com/en-us/library/hh694604.aspx

#3


3  

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. But for troubleshooting and debugging the location where it was throw'n is usually more useful.

通过这种方法,宏可以打印捕获异常的位置。但是对于故障排除和调试它所在的位置通常更有用。

To get that information, we can attach the __FILE__ and __LINE__ macros to the exception. However, we still can't get completely rid of macros, but we get at least the exact throw location:

要获取该信息,我们可以将__FILE__和__LINE__宏附加到异常。但是,我们仍然无法完全摆脱宏,但我们至少得到了精确的抛出位置:

#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. The whole program gets shorter and the locations of code execution and error handling can be nicely separated:

如果我们愿意使用boost :: exception,还有一个额外的改进:我们可以至少在我们自己的代码中摆脱宏定义。整个程序变得更短,代码执行和错误处理的位置可以很好地分开:

#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(); }
}