如何用支持__LINE_和__FILE__的内联函数替换我的c++异常宏
How can I replace my c++ exception macro with an inline function with __LINE__ and __FILE__ support?
我目前正在阅读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());
}
- 为什么除非添加括号,否则构造函数上的模板替换会失败?
- 用符号版本替换对函数的所有调用
- 我可以在这里替换什么,因为我不能在 C# 中使用隐式变量的 lambda 函数?
- 用矩阵将函数中的整数值替换定义的常量
- 函数模板实例化、替换和重载解析的顺序是什么?
- 为表示一个或多个操作的C++函数的int参数寻找类型安全的替换
- 仅当一个参数中未使用 std::function 时,模板函数替换才有效
- 为什么数组中对象的析构函数在被另一个对象替换时不被调用?
- 如何使替换 c 函数的变量名成为错误?
- hstrerror() 的替换函数
- 重新声明时替换函数默认参数
- C++字符串替换函数无限循环
- 如何在c++中替换函数调用的所有实例
- 用C++模板替换函数调用的C宏
- 使用字符串替换函数将字符串的一部分替换为初始化的变量
- 字符串替换函数c++
- 使用默认类型替换c++函数引用模板错误
- 在Std::to_string替换函数中无法识别Std::stringstream对象
- c++中的替换函数
- 替换函数'operator new'不能声明'inline' [-werror,-winline-new-delete]