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:
有没有办法规避这种行为,还是应该坚持使用我的旧宏?我在这里阅读了这些帖子,我已经怀疑我的第二个例子可能无法正常工作:
- Behavior of __LINE__ in inline functions
- __FILE__, __LINE__, and __FUNCTION__ usage in C++
_LINE__在内联函数中的行为
在C ++中__FILE __,_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
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(); }
}