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

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

本文关键字:替换 函数 我的 c++ 异常 支持 何用 LINE FILE      更新时间:2023-10-16

我目前正在阅读Scott Meyers的《高效C++》一书。它说,对于类似函数的宏,我应该更喜欢inline函数而不是#define函数。

现在我尝试编写一个内联函数来替换我的异常宏。我的旧宏是这样的:

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

我的新内联函数是:

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

可能有些人已经预料到了,现在__FILE____LINE__宏是无用的,因为它们总是引用带有内联函数定义的C++文件。

有没有办法规避这种行为,或者我应该坚持我以前的宏?我在这里阅读了这些线程,我已经怀疑我的第二个示例可能无法正常工作:

  • __LINE_在内联函数中的行为
  • __FILE__、__LINE_和__FUNCTION_在C中的用法++

不要使用__(双下划线),因为它是保留的。具有inline功能更好
然而,在这里您需要宏和函数的混合,因此您可以执行以下操作:

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

我知道这是一个老问题,但我认为在异常宏中打印行的方法存在根本缺陷,我认为我有更好的选择。我假设宏的使用类似于以下代码:

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

使用这种方法,宏将打印异常catch'ed的位置但对于故障排除和调试,位于throw'n的位置通常更有用。

要获得该信息,我们可以将__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(); }
}

如果我们愿意使用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(); }
}

请注意,与inline函数相比,在您的情况下使用类似#define函数的宏还有另一个区别。您可以在宏的调用中使用流式操作符和参数,将其组合为消息的文本:

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

但大多数时候,我需要这样的东西,那就是检查某个条件(比如断言)。因此,你可以用以下内容来扩展@iammilind的例子:

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

或者一些更专业的东西,也打印值:

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__)

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

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