I am trying to write my own custom assert for my own project. This project will be written with c++11.
The assert must have the following qualities:
It must be kept as an expression and is assignable.
E.g. I should be able to write code like this int x = custom_assert(1/y);
It must be overloaded to accept an assert with a message and without one.
E.g int x = custom_assert(1/y, "Error divide by zero"); This code and the above are both compilable and acceptable.
It must have no side-effects in release mode
E.g. int x = custom_assert(1/y); will become int x = 1/y; in release mode.
And most importantly, it must break at the specific point where the assert was made. Which will make use of __debugbreak() as part of its evaluating expression.
The following is my attempt:
#include <string>
bool DoLog(std::string msg, std::string file, int line); //Prints to std::cerr and returns HALT macro
#if defined(_DEBUG) || defined(DEBUG)
#define HALT true
#define NT_ASSERT_BASE(x, msg) (!(x) && DoLog((msg), __FILE__, __LINE__) && (__debugbreak(),1))
#else
#define HALT false
#define NT_ASSERT_BASE(x,msg) (x)
#endif//debug/release defines
//--- Can't implement these until I can return the expression ---
//#define GET_MACRO(_1,_2,_3,NAME,...) NAME
//#define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2)(__VA_ARGS__)
#define NT_ASSERT(expression, msg) NT_ASSERT_BASE(expression,msg)
As you can see my custom assert fails on 2 fronts, namely being kept as expression and assignable, and on overloading (Which I cannot implement until I figure out how to keep it as an expression.
All in all, I may be chasing stars and this macro may in fact be impossible to make. (Which I hope isn't the case)
Many thanks.
As far as I can tell, this can't be done in standard C++.
There is no way to get the __debugbreak() into the expanded code and at the same time pass the result of the expression unmodified, because you need the result twice: once for testing it, which will implicitly cast it to bool, and once to return it at the end.
There are two options:
Use gcc's and clang's ({}) construct with auto variable to hold the result. That will exclude MSC++, but I suppose you want that, because __debugbreak() is a MSC++ misfeature.
Give up on requiring the __debugbreak() on the call site, accept having to go one level up when it stops and make the thing as a template function.
A lambda expression will fit slightly better than a template function. It will make the break appear at the macro site, but it will still appear as a separate stack frame in the call stack. It also requires C++11 support (it was published over 5 years ago, but some platforms may not have it).
I don't think you should be mixing the validation with the assignment. From your example, it looks like you want to assign to an integer but an assertion, by nature, is a boolean expression. Further, your example is asserting on the wrong expression. It looks like you want to assert that y is not equal to zero (preventing division by zero), but you are asserting against something that will also be one or false or undefined.
If you are willing to be a bit flexible with your assignment requirements, then we can work around the problem of maintaining the expression and other useful info with some macro magic. Further, we can execute the __debugbreak() at the call site.
#include <iostream>
#include <string>
#include <type_traits>
template<class Fun>
inline bool DoLog(Fun f, std::string message, const char *expression, const char *filename, int line) {
static_assert(std::is_same<bool, decltype(f())>::value, "Predicate must return a bool.");
if (!(f())) {
std::cerr << filename << '#' << line << ": '" << expression << "' is false.";
if (!message.empty()) {
std::cerr << ' ' << message;
}
std::cerr << std::endl;
return false;
}
return true;
}
#if defined(_DEBUG) || defined(DEBUG)
#define HALT true
#define WITH_MESSAGE_(expr, x) [&](){return (expr);}, x, #expr
#define WITHOUT_MESSAGE_(expr) [&](){return (expr);}, std::string{}, #expr
#define PICK_ASSERTION_ARGS_(_1, _2, WHICH_, ...) WHICH_
#define CREATE_ASSERTION_ARGS_(...) PICK_ASSERTION_ARGS_(__VA_ARGS__, WITH_MESSAGE_, WITHOUT_MESSAGE_)(__VA_ARGS__)
#define NT_ASSERT(...) if (!DoLog(CREATE_ASSERTION_ARGS_(__VA_ARGS__), __FILE__, __LINE__)) __debugbreak()
#else
#define HALT false
#define NT_ASSERT(...)
#endif
int main() {
NT_ASSERT(true);
NT_ASSERT(false);
NT_ASSERT(1 == 1, "1 is 1");
NT_ASSERT(1 == 0, "1 is not 0");
return 0;
}
NOTE: The above snippet works on GCC using -std=c++11 (with a placeholder for the __debugbreak() statement). I'm making an assumption that VC++ would work also when it fully supports C++11.
Related
Let's say I have a project with many functions. For debugging purpopses, I want each one of them print out a diagnostic message (or a few) when called:
int f(int arg) {
cerr << "f() called with argument" << arg << endl;
int ret = 42;
cerr << "f() returned " << ret << endl;
return ret;
}
and so on. In one function, there may be as many as five to six messages printed out on cerr. I want to be able to disable them unless a debug flag (like NDEBUG) is set. One thing I could do is wrap each cerr statement with an if-statement.
#ifdef NDEBUG
const bool DEBUG_ON = true;
#else
const bool DEBUG_ON = false;
#endif
int f(int arg) {
if (DEBUG_ON) {
cerr << "f() called with argument" << arg << endl;
}
int ret = 42;
if (DEBUG_ON) {
cerr << "f() returned " << ret << endl;
}
return ret;
}
But it can get really tedious and, in the end, virtually unreadable with a couple dozens of such statements. My idea was to replace cerr with a custom object with an overloaded << operator which would send any arguments passed to it "into the void" like this:
class NullOutStream {
public:
NullOutStream operator<<(...) {
return *this;
}
};
NullOutStream debug_out;
#ifdef NDEBUG
#define debug_out cerr
#else
#define debug_out debug_out
#endif
Although it works perfectly, it doesn't seem very elegant. Is there a standard/nicer way to accomplish this?
There is no "standard" way to do this. There are many logging/assertion libraries that deal with similar issues, and there are different approaches to solve this. A few things to keep in mind:
It may or may not be desirable to keep the side effects of your log statement (as yours does). For example, if you did something like this:
debug_out << ++i;
This would increment i no matter whether you are in debug mode or not. This is probably desirable in this case. Conversely, if you do this:
debug_out << some_object.expensive_to_string_operation();
This would call expensive_to_string_operation() even when you are not in debug mode. This is probably not desirable in this case.
This would not happen if you used printf-style debug macro, e.g. something like this:
#ifdef NDEBUG
#define logf(...)
#else
#define logf(...) printf(__VA_ARGS__)
#endif
The reason being that when in non-debug mode, the arguments would be removed by the preprocessor.
You may want to do other things before or after each log statement, e.g. record a time stamp, flush a log file etc. It is possible to do this with streams by using destructor tricks, but it is more complicated to implement. It is much easier to do this using a function call, e.g. something like this:
#define log(msg) printf("%s: %s\n", timestamp(), msg)
You may want to record file name / line number with your logs. Again, this is easier to do with a function call than with a stream.
Streams may be better if you want custom formatting by object type - the printf interface doesn't lend itself to that very well.
I would recommend to have a look at some existing logging libraries to get an idea of different approaches. I suggest looking at Google's glog library because it has an interesting combination of using streams while retaining the ability to do 'per call' things (e.g. record time stamps, line numbers etc).
This question already has answers here:
How to put assert into release builds in C/C++
(7 answers)
Closed 1 year ago.
Where can I find an ASSERT similar to the assert(...) macro from the standard C++ library (the one defined in <cassert>), but that works on Release mode too? Or how should I write one?
I like assert(...) because it automatically prints the line my source file where the assertion failed, as well as the assertion expression. I expect those features (if possible) in the Release mode ASSERT as well.
Basically assert is a macro that evaluates the expression and if it fails prints something and then aborts. It's not that hard to write something similar, something like.
#define ASSERT(x) do { if( !(x) ) { printfunc( #x ); abort(); } while(0)
Then you can modify that in order to suit your requirements. For example you might want not to abort in release mode. You could also adjust your printouts (to include just the information that you think is useful), in order to get file and line information you would use __FILE__ and __LINE__ macros (btw the #x in the define expands to a string literal containing the expression x).
In your release build, don't define NDEBUG, and assert will work.
You can undefine NDEBUG
#undef NDEBUG
near the assert statement
or you can define your own assert
#define assert(x) printf(...)
I've rolled out my own assertions that always fire in release mode too, based on this article:
This is the GIST of it, with me not providing the actual implementation (of AbstractAsserter, which is based on the article), but you get the idea:
#include <iostream>
struct AbstractAsserter
{
virtual void doAssert(const char* expr, const char* file, int line, const char* function) const
{
std::cout << "Asserting now at " << expr << ", " << file << ", " << line << ", " << function << std::endl;
}
};
struct Local
{
const char* function_;
const char* expr_;
const char* file_;
int line_;
Local( const char* f, const char* ex, const char* file, int line )
: function_( f ), expr_( ex ), file_( file ), line_( line )
{ }
Local operator << ( const AbstractAsserter& impl )
{
impl.doAssert( expr_, file_, line_, function_ );
return *this;
}
};
// More to be added as required...
#if defined( __GNUC__ )
# define WE_FUNCTION __PRETTY_FUNCTION__
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)
# define WE_FUNCTION __func__
#else
# define WE_FUNCTION "null_func()"
#endif
#define WE_ASSERT( expr )\
if( expr );\
else Local( WE_FUNCTION, #expr, __FILE__, __LINE__ )\
<< AbstractAsserter();
int main() {
WE_ASSERT(!"bad BAD BOY!");
return 0;
}
With this structure, your implementation may also do some critical saving of state etc (if you deem your program state trustworthy...debatable).
Using the std assert in release mode (where, by the comments, you would like compiler optimizations enabled), isn't a problem per se, as is established by the other answers. Writing a custom one has been done, so there's not much problem there.
However, to quote the Dr. Dobb's article:
Do use assertions liberally throughout your code; they are watchful, reliable guards that protect you (your program) from insanity
If you are worried to have assertion + compiler optimizations, then these two thing are a bit add odds conceptually:
Asserts should and can be used liberally, with the intention of finding bugs early, and mostly so that it doesn't matter whether the assert code hurts performance.
If you want/need to run with optimizations, you may not like the performance hit the additional (now optimized, but still) assert code gives you.
So, in summary, I see the use of a relase mode assert, but make sure to keep it separate from the "normal" assert, where most people would assume it to be off in release mode.
I would like to create a custom version of the assert macro defined in <cassert>, that displays an error message when the assertion fails.
Desired usage:
custom_assert(AClass<T1, T2>::aBoolMethod(), "aBoolMethod must be true");
Flawed test implementations:
#define custom_assert(mCondition, mMessage) ...
// This fails because mCondition may have commas in it
#define custom_assert(..., mMessage)
// Not sure about this either - mMessage may be an expression containing commas
// as well
How can I correctly implement a custom assert that takes a boolean expression (with possible commas) as the first argument and a string expression (with possible commas) as the second argument?
Or is there a way to implement assertions without the use of macros?
You were quite close, what you need to use is simply this:
#define myAssert(message, ...) do { \
if(!(__VA_ARGS__)) { \
/*error code*/ \
} \
} while(0)
The special preprocessor variable __VA_ARGS__ will expand to whatever was passed in the place of the three dots, including all comas.
Note that the preprocessor will not interprete commas in the condition in the very least, it will just paste them as is into the if() statement. Which is precisely what you want if you want to pass templated conditions, as hinted by the comments.
Commas in the message string are not a problem either, since the preprocessor understands about string literals and does not interprete anything within the double quotes.
The straightforward
assert(AClass<T1, T2>::aBoolMethod() && "aBoolMethod must be true");
fails:
error: macro "assert" passed 2 arguments, but takes just 1
but if you add an extra pair of parenthesis around the first argument, it works. Like this:
#include <cassert>
template <typename A, typename B>
struct C {
bool f() { return false; }
};
int main() {
assert((C<int,int>().f()) && "some message, with comma");
// ^ ^
}
Note: it was also pointed out by Adam Rosenfield in a comment.
Perhaps the only benefit over the __VA_ARGS__ approach is that it doesn't dump yet another macro on the user. If you forget the parenthesis, you can get a compile time error, so I see it as a safe solution.
For the sake of completeness, I published a drop-in 2 files assert macro implementation in C++:
#include <pempek_assert.h>
int main()
{
float min = 0.0f;
float max = 1.0f;
float v = 2.0f;
PEMPEK_ASSERT(v > min && v < max,
"invalid value: %f, must be between %f and %f", v, min, max);
return 0;
}
Will prompt you with:
Assertion 'v > min && v < max' failed (DEBUG)
in file e.cpp, line 8
function: int main()
with message: invalid value: 2.000000, must be between 0.000000 and 1.000000
Press (I)gnore / Ignore (F)orever / Ignore (A)ll / (D)ebug / A(b)ort:
Where
(I)gnore: ignore the current assertion
Ignore (F)orever: remember the file and line where the assertion fired and
ignore it for the remaining execution of the program
Ignore (A)ll: ignore all remaining assertions (all files and lines)
(D)ebug: break into the debugger if attached, otherwise abort() (on Windows,
the system will prompt the user to attach a debugger)
A(b)ort: call abort() immediately
You can find out more about it there:
blog post
GitHub project
Hope that helps.
I'm not entirely sure what you mean by a "boolean expression with commas." Simply wrapping macro expansions in commas (as seen in the samples below) protects against parse errors if you happen to use the default comma operator in your conditions, but the default comma operator does not do the same thing as &&. If you mean you want something like:
my_assert(condition1, condition2, message1, message2);
You're out of luck. How should any API tell where the conditions stop and the messages start. Just use &&. You can use the same tricks below for handling the message portion to also create a my_condition_set macro that allows you to write something like:
my_asssert(my_condition_set(condition1, condition2), message1, message2);
Getting to the message part, which is the tricky part and the most useful part that the standard assert macros tend to lack, the trick will come down to either using a custom message output type that overrides operator, (the comma operator) or to use a variadic template.
The macro uses variadic macro support and some tricks to deal with commas. Note that none of the code I'm posting here is tested directly; it's all from memory from custom assert macros I've written in the past.
The version using comma operator overloading, which works in C++98 compilers:
struct logger {
template <typename T>
logger& operator,(const T& value) {
std::cerr << value;
return *this;
}
};
#define my_assert(condition, ...) do{ \
if (!(condition)) { \
(logger() , __VA_ARGS__); \
std::terminate(); \
} \
}while(false)
The logger() expression creates a new instance of the logger type. The list of arguments from __VA_ARGS__ is then pasted with commas separating each. These commas each invoke the comma operator left-to-right, which forwards the expression on to std::cerr. I usually use a custom log stream that handles writing to files, cerr, Windows' OutputDebugStringA, or whatever.
Using variadic templates in a C++11 compiler, this would be more like:
template <typename ...Ts>
void logger(Ts&&... argv) {
std::cerr << your_string_format_function(argv...);
}
void logger(const char* fmt) {
std::cerr << fmt;
}
void logger() {}
#define my_assert(condition, ...) do{ \
if (!(condition)) { \
logger(__VA_ARGS__); \
std::terminate(); \
} \
}while(false)
You'd need a format string function that actually works with variadic arguments or write an adapter for something like boost::format.
You could also use printf if you don't mind the lack of type-safety.
Recently I have been reading Effective C++ Second Edition by Scott Meyers to improve on C++ best practices. One of his listed items encourages C++ programmers to avoid pre-processor macros and 'prefer the compiler'. He went as far as saying there are almost no reasons for macro in C++ aside from #include and #ifdef/#ifndef.
I agree with his reasoning, as you can accomplish the following macro
#define min(a,b) ((a) < (b) ? (a) : (b))
with the following C++ language features
template<class T>
inline const T & min(const T & a, const T & b) {
return a < b ? a : b;
}
where inline gives the compiler the option to remove the function call and insert inline code and template which can handle multiple data types who have an overloaded or built in > operator.
EDIT-- This template declaration will not completely match the stated macro if the data type of a and b differ. See Pete's comment for an example.
However, I am curious to know if using macros for debug logging is a valid use in C++. If the method I present below is not good practice, would someone be kind to suggest an alternative way?
I have been coding in Objective-C for the last year and one of my favorite 2D engines (cocos2d) utilized a macro to create logging statements. The macro is as follows:
/*
* if COCOS2D_DEBUG is not defined, or if it is 0 then
* all CCLOGXXX macros will be disabled
*
* if COCOS2D_DEBUG==1 then:
* CCLOG() will be enabled
* CCLOGERROR() will be enabled
* CCLOGINFO() will be disabled
*
* if COCOS2D_DEBUG==2 or higher then:
* CCLOG() will be enabled
* CCLOGERROR() will be enabled
* CCLOGINFO() will be enabled
*/
#define __CCLOGWITHFUNCTION(s, ...) \
NSLog(#"%s : %#",__FUNCTION__,[NSString stringWithFormat:(s), ##__VA_ARGS__])
#define __CCLOG(s, ...) \
NSLog(#"%#",[NSString stringWithFormat:(s), ##__VA_ARGS__])
#if !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0
#define CCLOG(...) do {} while (0)
#define CCLOGWARN(...) do {} while (0)
#define CCLOGINFO(...) do {} while (0)
#elif COCOS2D_DEBUG == 1
#define CCLOG(...) __CCLOG(__VA_ARGS__)
#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__)
#define CCLOGINFO(...) do {} while (0)
#elif COCOS2D_DEBUG > 1
#define CCLOG(...) __CCLOG(__VA_ARGS__)
#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__)
#define CCLOGINFO(...) __CCLOG(__VA_ARGS__)
#endif // COCOS2D_DEBUG
This macro provides for incredible utility which I will want to incorporate in my C++ programs. Writing a useful log statement is as simple as
CCLOG(#"Error in x due to y");
What is even better, is that if the COCOS2D_DEBUG is set to 0, then these statements never see the light of day. There is no overhead for checking a conditional statement to see if logging statements should be used. This is convenient when making the transition from development to production. How could one recreate this same effect in C++?
So does this type of macro belong in a C++ program? Is there a better, more C++ way of doing this?
First, Scott's statement was made at a time when macros were
considerably overused, for historical reasons. While it is
generally true, there are a few cases where macros make sense.
Of of these is logging, because only a macro can automatically
insert __FILE__ and __LINE__. Also, only a macro can
resolve to absolutely nothing (although based on my experience,
this isn't a big deal).
Macros such as you show aren't very frequent in C++. There are
two usual variants for logging:
#define LOG( message ) ... << message ...
which allows messages in the form " x = " << x, and can be
completely suppressed by redefining the macro, and
#define LOG() logFile( __FILE__, __LINE__ )
where logFile returns a wrapper for an std::ostream, which
defines operator<<, and permits such things as:
LOG() << "x = " << x;
Done this way, all of the expressions to the right of LOG()
will always be evaluated, but done correctly, no formatting will
be done unless the log is active.
There are "right" things to use macros for and there are bad uses of macros. Using macros where functions work is a bad idea. Using macros where functions DON'T do the same thing is perfectly good in my book.
I quite often use constructs like this:
#defien my_assert(x) do { if (!x) assert_failed(x, #x, __FILE__, __LINE__); } while(0)
template<typename T>
void assert_failed(T x, const char *x_str, const char *file, int line)
{
std::cerr << "Assertion failed: " << x_str << "(" << x << ") at " << file << ":" << line << std::endl;
std::terminate();
}
Another trick using the stringizing "operator" is something like this:
enum E
{
a,
b,
c,
d
};
struct enum_string
{
E v;
const char *str;
};
#define TO_STR(x) { x, #x }
enum_string enum_to_str[] =
{
TO_STR(a),
TO_STR(b),
TO_STR(c),
TO_STR(d),
};
Saves quite a bit of repeating stuff...
So, yes, it's useful in some cases.
After reading another question about the use of macros, I wondered: What are they good for?
One thing I don't see replaced by any other language construct very soon is in diminishing the number of related words you need to type in the following:
void log_type( const bool value ) { std::cout << "bool: " << value; }
void log_type( const int value ) { std::cout << "int: " << value; }
...
void log_type( const char value ) { std::cout << "char: " << value; }
void log_type( const double value ) { std::cout << "int: " << value; }
void log_type( const float value ) { std::cout << "float: " << value; }
as opposed to
#define LOGFN( T ) void log_type( const T value ) { std::cout << #T ## ": " << value; }
LOGFN( int )
LOGFN( bool )
...
LOGFN( char )
LOGFN( double )
LOGFN( float )
Any other 'irreplaceables'?
EDIT:
trying to summarize the reasons-why encountered in the answers; since that's what I was interested in. Mainly because I have a feeling that most of them are due to us still programming in raw text files in, still, poorly supporting environments.
flexibility of code-to-be compiled (e.g. #ifdef DEBUG, platform issues) (SadSido, Catalin, Goz)
debug purposes (e.g. __LINE__, __TIME__); I also put 'stringifying' under this reason (SadSido, Jla3ep, Jason S)
replacing e.g. PHP's require vs. include feature (#pragma once) (SadSido, Catalin)
readability enhancement by replacing complicated code (e.g. MESSAGEMAP, BOOST_FOREACH) (SadSido, fnieto)
DRY principle (Jason S)
an inline replacement (Matthias Wandel, Robert S. Barnes)
stringifying (Jason S)
compile different code under different conditions ( #ifdef __DEBUG__ );
guards to include each header once for each translation unit ( #pragma once );
__FILE__ and __LINE__ - replaced by the current file name and current line;
structuring the code to make it more readable (ex: BEGIN_MESSAGE_MAP() );
See interesting macro discussion at gotw here:
http://www.gotw.ca/gotw/032.htm
http://www.gotw.ca/gotw/077.htm
Most useful - header file guarding:
#ifndef MY_HEADER_GUARD
#define MY_HEADER_GUARD
// Header file content.
#endif
Later add [Windows only]
Exporting classes to DLL:
#ifdef EXPORT_MY_LIB
#define MY_API __declspec( dllexport)
#else
#define MY_API __declspec( dllimport)
#endif
Sample class:
class MY_API MyClass { ... };
platform specific sections.
ie
#ifdef WINDOWS
#include "WindowsImplementation.h"
#elif defined( LINUX )
#include "LinuxImplementation.h"
#else
#error Platform undefined.
#endif
I've posted this before, but of course cannot now find it. If you want to access the __FILE__ and __LINE__ macros, then another macro is by far the most convenient way to go - for example:
#define ATHROW(msg) \
{ \
std::ostringstream os; \
os << msg; \
throw ALib::Exception( os.str(), __LINE__, __FILE__ ); \
}
For doing cool magic tricks like in BOOST_FOREACH, injecting variables into an ambit.
BOOST_FOREACH( char c, "Hello, world!" )
{
... use char variable c here ...
} // c's scope ends here
// if there's an outer c defined, its scope resumes here
For don't-repeat-yourself (DRY) reasons. Things that involve repeated constructs at compile-time which cannot be abstracted away in other methods (templates or what have you). If you are finding you're repeating the same code constructs 20 times, that's a potential source of human error -- which hopefully can be abstracted away using templates but sometimes not. It's always a balance between the advantages of seeing raw code that can be type-checked and reviewed clearly, vs. the advantages of using macros for arbitrary substitution patterns (that generally can't be checked by automatic programming tools).
Stringifying and concatenation (the # and ## preprocessor patterns) can't be performed by templates.
Of course, at some point you may be better off using a tool (whether custom or off-the-shelf) for automatic code generation.
Modern languages take the philosophy that needing a proeprocessor ins a sign of a missing language feature, so they define all kinds of language features that the preprocessor took care of very simply back in the old K&R style C.
Your code example above could be simplified via an inline function, for example.
Personally, the most indispensable aspect of a preprocessor is to make it clear that some things are done compile time right in the source code. The java approach of eliminating dead code paths at compile time is just not as obvious when reading the code.
One of their uses is basically as a poor mans ( inlined ) template function.
For example:
#define MIN(X,Y) ((X) < (Y) ? : (X) : (Y))
This allows you to generate a custom MIN function for any types supporting these operators which is effectively inline at the point of useage. Of course there is no type checking and it's easy to end up with weird syntax errors or incorrect behavior if you don't get the parens just right.