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.
Related
I'd like to use the built-in compiler checks to verify format strings of a custom logging framework to catch the odd runtime crash due to mismatching format string <-> parameters in advance.
Arguments of the custom C++ logging methods are identical to the printf() family so I was attempting to replace all calls to
MyLogger::Error(
with
fprintf(stderr,
Though unfortunately the (clang) preprocessor chokes on the scope resolution operator (::), i.e. instead of ULog::Warn( only the ULog substring is recognized:
#define MyLogger::Error( fprintf(stderr,
Any suggestions on how to make this work much appreciated.
Have you tried a variadic template? found here.
#include <iostream>
namespace MyLogger
{
template <typename... T>
auto Error(const char * _Format, T &&... args)
{
return printf(_Format, std::forward<T>(args)...);
};
}
#define printf(...) MyLogger::Error(__VA_ARGS__)
int main()
{
MyLogger::Error("Non-Macro Print \n");
printf("Macro Print \n");
return 0;
}
Elaborating on the approach suggested by #Someprogrammerdude I've extended the custom logging class to use the clang/gcc format attribute to enable compiler format checking.
The declaration simply becomes
static void Error(const char *format,...) __attribute__ ((format (printf, 1, 2)));
It's even better than the original idea to use the preprocessor to temp. enable checks by replacing calls to the custom formatter with calls to printf() as it's enabled all the time catching argument mismatches immediately!
(FWIW - already fixed dozens of issues and couple potential crashes on our 120+ LOC code base)
What if you modify MyLogger::Error to
MyLogger::Error(args){
if (0) {
fprintf(stderr,args)
}
//actual function
}
This way you get the built-in warnings and it does not effect the efficiency of your code. (You can obviosly actually use the print if you wan to write to stderr, but I think if you wanted that you used that already)
I am using the boost logging framework, which might be irrelevant for this question, but I want to have a macro in the form of LOG(sev) where sev is one of the log levels and I can standardize the format of the output.
#define LOG_LOCATION \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<int>>(boost::log::core::get()->get_global_attributes()["Line"]).set(__LINE__); \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<std::string>>(boost::log::core::get()->get_global_attributes()["File"]).set(__FILE__); \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<std::string>>(boost::log::core::get()->get_global_attributes()["Function"]).set(__func__);
#define LOG(sev) LOG_LOCATION BOOST_LOG_SEV(slg, sev)
extern boost::log::sources::severity_logger<boost::log::trivial::severity_level > slg;
This code snippet works in most cases where the log is on a single line, however, if I use an if in the format.
if(false) LOG(debug) << "Don't print this";
It always prints the message. The reason is obvious, the if applies to the first statement in the macro and the rest executed, so the statement will show up (without the line number).
I am unsure of how to format this macro to work correctly.
Replace the semicolons at the end of the three function calls in your LOG_LOCATION macro with commas. This turns three statements into one partial one, that continues after the end of the expanded macro.
#define LOG_LOCATION \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<int>>(boost::log::core::get()->get_global_attributes()["Line"]).set(__LINE__), \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<std::string>>(boost::log::core::get()->get_global_attributes()["File"]).set(__FILE__), \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<std::string>>(boost::log::core::get()->get_global_attributes()["Function"]).set(__func__),
When used as you have it above, the if line will become
if(false) a, b, c, BOOST_LOG_SEV(slg, sev) << "Don't print this";
(Replacing those three calls to set with letters for brevity.)
This would work if BOOST_LOG_SEV expands to a single expression. However, BOOST_LOG_SEV expands to a for statement:
for (::boost::log::record rec_var = (logger).open_record((BOOST_PP_SEQ_ENUM(params_seq))); !!rec_var;)
::boost::log::aux::make_record_pump((logger), rec_var).stream()
So a different approach is necessary.
You can define 'LOG' as a class (instead of a macro) to encapsulate all the stuff in those macros.
class LOG {
public:
LOG(int sev): sev(sev) { LOG_LOCATION; }
LOG &operator<<(const char *msg) {
BOOST_LOG_SEV(slg, sev) << msg;
return *this;
}
}
Depending on what your needs are, you can add other overloads of operator<< to handle std::string, int, or just make it a template class.
This may not be quite as efficient as the original when sending multiple items (LOG(debug) << "Number " << x << " found.") but will work as a single statement.
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.
Is it possible to replace this preprocessor macro:
#define AL_CALL(a) do { a; \
ALenum e = alGetError(); \
if(e != AL_NO_ERROR) \
UtilitySoundNode::printALError(e,__FILE__, __LINE__); \
} while(0)
with a C++ template? If it is possible, will make any sense to do it (pros/cons - overhead/debugging)?
Note:
Basically I am wondering if there is an elegant way to handle this kind of error handling in C++.
EDIT:
Of course I made a mistake a is a function call. As one may guess it is a function call with parameters of a OpenAL function.
AL_CALL(someAlFunction(param1, param2))
NOTE:
Somebody decided to edit the macro and make it nicer but I'd prefer to keep the original one too. So here it is:
#define AL_CALL(a) {a; ALenum e = alGetError();if(e != AL_NO_ERROR)PUtilitySoundNode::printALError(e,__FILE__, __LINE__);}
One problem here seems to be that the "a" can be some arbitrary function (with parameters) which sets the error code returned by alGetError().
That can be rewritten to C++ by using a functor object. To pass the arguments (and object instance if necessary) std::bind or boost::bind can be used (note that to bind reference args the std::ref/boost::ref is necessary).
However, if you'd want to still have the __FILE__ and __LINE__ passed the the printError() that C++ template still would need to be called by a macro which will pass those to the template. __FILE__ and __LINE__ are only expanded by the preprocessor, so there is no way around using a macro for them.
But the macro could be much simpler then and most of the work can be done in the C++ template (which has many advantages e.g. for debugging, because in most debuggers you cannot step into a macro).
EDIT: adding the code as an example:
template<typename T>
void ALcallAndCheck(T c, const char *file, size_t line)
{
c();
ALenum e = alGetError();
if(e != AL_NO_ERROR)
UtilitySoundNode::printALError(e, file, line); \
}
#define AL_CALL(a) ALcallAndCheck(a, __FILE__, __LINE__)
Then, instead of
AL_CALL(SomeFunction(2, refAttr));
the call will become:
AL_CALL(std::bind(SomeFunction, 2, std::ref(refAttr)));
EDIT 2:
The previous indeed does not work with expressions, which the original macro allows. To work also for expressions, the macro can be altered to:
#define AL_CALL(a) ALcallAndCheck([&]{ (a); }, __FILE__, __LINE__)
That will create a lambda which will evaluate anything that comes into the macro. Then even the std::bind is not necessary and it can be called directly as:
AL_CALL(SomeFunction(2, refAttr));
AL_CALL(SomeOtherFunction1()+SomeOtherFunction2(8));
No, the use of __FILE__ and __LINE__ pretty well require the preprocessor.
Note that using a template instead of a macro does not produce an exact analog. The macro defined in your question allows a to represent a statement as well as an expression. A template does not have that kind of flexibility. The template defined below assumes a is a non-void expression.
There is no standard way to implicitly inject a function caller's file name and line number without the caller passing in that information to the called function. A preprocessor macro allows a means to make the syntax appear to be implicit injection, when in fact the information is being passed.
template <typename T>
void AL_CALL (T a, const char *file, int line) {
ALenum e = alGetError();
if(e != AL_NO_ERROR)
UtilitySoundNode::printALError(e, file, line);
}
#define AL_CALL(X) AL_CALL((X), __FILE__, __LINE__)
You may be able to use system specific facilities (e.g., CaptureStackBackTrace + SymFromAddr or backtrace + backtrace_symbols) to get approximately the same information implicitly, but it may require debugging symbols to be present, and inline functions may not produce the expected output.
template<class A>
void al_call(A&&a){
ALenum e = a();
if(e != AL_NO_ERROR)
UtilitySoundNode::printALError(e,__FILE__, __LINE__);
}
Use:
al_call( [&]{ return bob(); });
Instead of:
AL_CALL( bob() )
The line/file info is not useful above.
So
template<class A>
void al_call(A&&a, char const*file, unsigned line){
ALenum e = a();
if(e != AL_NO_ERROR)
UtilitySoundNode::printALError(e,file, line);
}
#define AL_CALL(...) al_call([&]()mutable{return __VA_ARGS__;}, __FILE__, __LINE__)
and it is almost a drop in replacement.
Suppose I have a third party library that provides a function-like ThirdPartyMacro macro that is:
Variadic and accepts arbitrary tokens, not just well formed c++ expressions. After parsing the arguments ThirdPartyMacro extracts some tokens that it assumes to be identifiers denoting variables in the scope of it's invocation, and uses them as such.
Evaluates to some value of a known type.
May throw an exception of type ThirdPartyException
I want to wrap this up by writing a macro MyMacro that will behave exactly like ThirdPartyMacro but throw MyException whenever ThirdPartyMacro would throw ThirdPartyException.
Is it possible? If so, how?
Not that due to (1) MyMacro cannot be a function, as it's arguments are not something that can be passed as function parameters.
A lambda will help:
#define MyMacro(...) \
[&]{ try { return ThirdPartyMacro(__VA_ARGS__); } \
catch( const ThirdPartyException& e ) \
{ throw MyException( e.what() ); } }()