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() ); } }()
Related
I am working on a RAII class that should be able to trace the code-flow of a function. It should be able to capture the line number of the function when it is constructed and do the same when it is destructed. The macros __FILE__ and __LINE__ would not work since they are substituted at the time of call. This rules out the following approach:
#define PRINT_LINES \
struct LineRecorder \
{ \
LineRecorder() { std::cout << __LINE__; } \
~LineRecorder() { std::cout << __LINE__; } \ <- Substitutes the line number at macro call
} {};
C++20 has support for source location which works at compile-time. This led me to the approach:
struct LineRecorder
{
LineRecorder(std::source_location location = std::source_location::current())
{
std::cout << location.line();
}
~LineRecorder(std::source_location location = std::source_location::current())
// destructor can't take any arguments (not even default ones)
{
std::cout << location.line();
}
};
Some other approaches that didn't pan out:
Since source_location.current is marked constexpr, it should be evaluated before any inlining is done: that rules out any lambda trickery I could think of.
I plan to use it inside performance sensitive code, so should ideally not add any run-time overhead: this rules out stacktrace parsing. The std::cout in the code above is just a (very very) expensive placeholder.
Is there any way this can be achieved?
You can’t do this: the main reason is that ~T can be called from many places other than the } of a block. Among those are various parts of the constructor of any type that contains a subobject of type T, any evaluation that might throw out of a block that contains a local T variable, a new T[n] expression (for n>1), or the end of a thread or process. C++20 adds coroutine_handle::destroy as an indirect means of causing destruction.
None of these have a good “What line is this?” answer. The fact that our syntactic trick of a default argument (which is a bit silly anyway) doesn’t work is at most a corollary.
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.
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.
I would like to be able to write a macro CONDITIONALFUNCTION so that
CONDITIONALFUNCTION( FunctionName )
{
ConditionalExpression()
}
expands to
bool FunctionName( const Arguments& args )
{
return ConditionalExpression();
}
Is this even possible?
The closest I can find on SO is this thread:
Possible to define a function-like macro with a variable body?
except unlike in that thread, I have the additional requirement that the "body" within the braces is not a complete valid C++ statement, but rather an expression to be wrapped (effectively) in an 'if' statement.
Please assume I already know this may be impossible, and is almost surely stupid and evil :)
I'm going to assume you've got a good reason for using macros in the first place...
It's not possible with the syntax you've given with the question.
The closest workable macro syntax is:
#define CONDITIONALEXPRESSION(f, c) \
bool f( const Arguments& args ) \
{ return c; }
CONDITIONALEXPRESSION(FunctionName, ConditionalExpression())
This will expand to the same as the expanded function in the question
Is there any reason why the function body must be defined in the macro? In Microsoft C++, macros like ASSERT() define the actual function separately and then just reference it from the macro.
So the function is always defined but the macro is either equal to calling the function or nothing at all.
Aside from that, for C++ I'd probably use an inline function.
je4d already provided one alternative. I over some other variation:
#define CONDITIONALEXPRESSION(f) \
bool f( const Arguments& args )
#define CONDITIONALRETURN(c) \
return (c)
CONDITIONALEXPRESSION(FunctionName)
{
CONDITIONALRETURN(ConditionalExpression())
}
I have a class that has no default constructor, but the constructor may throw. I was wanting to have a test like:
EXPECT_THROW(MyClass(param), std::runtime_error);
But the compiler, g++, complains that there is no default constructor for MyClass. However, the following...
EXPECT_THROW(MyClass foo(param), std::runtime_error);
...works, and the test passes as expected. Why though won't Googletest accept the temporary object?
class MyClass
{
public:
MyClass(std::string const& filename);
//...
};
Interestingly enough, I had refactored my test to not have the filename in a separate variable, and when asked to check I found the following works:
EXPECT_THROW(MyClass("somefilename"), std::runtime_error);
However the following doesn't:
std::string filename("somefilename");
EXPECT_THROW(MyClass(filename), std::runtime_error);
If you're hit with the "most vexing parse", the cure is often uniform initialization:
EXPECT_THROW(MyClass{param}, std::runtime_error);
(assuming your compiler understands C++11).
When dealing with macros, ultimate tool is analyzing expanded macro:
In your case (and for gtest 1.6):
EXPECT_THROW(MyClass(filename), std::runtime_error);
Expands to:
...
bool gtest_caught_expected = false; \
try { \
if (::testing::internal::AlwaysTrue()) { MyClass(filename); }; \
} \
catch (std::runtime_error const&) { \
gtest_caught_expected = true; \
} \
catch (...) { \
gtest_msg.value = \
"Expected: " "MyClass(filename)" " throws an exception of type " \
"std::runtime_error" ".\n Actual: it throws a different type."; \
goto gtest_label_testthrow_88; \
} \
if (!gtest_caught_expected) { \
gtest_msg.value = \
"Expected: " "MyClass(filename)" " throws an exception of type " \
"std::runtime_error" ".\n Actual: it throws nothing."; \
goto gtest_label_testthrow_88; \
} \
...
As you can see, argument to EXPECT_THROW is not object, but expression to be evaluated further, within GTEST provided valid try/catch block.
So anything you pass to it, must be able to evaluate as expression within nested scope of current scope. In your case:
MyClass(filename)
Is ambiguous, but according to Most vexing parse rule, declaration interpretation is preferred, so you end up with:
MyClass filename
So you are creating variable named filename of class MyClass - and hence error about missing constructor.
This mechanism is not triggered if you use literal string:
MyClass("some string")
Becouse following would be invalid (and there is no ambiguity):
MyClass "some string"
Can you give more information? I constructed an example which works fine with a class that only has a one argument constructor.
#include <iostream>
#include <stdexcept>
#include "gtest/gtest.h"
class m {
public:
m(std::string a) {std::cout << "one argument constructor" << std::endl;}
};
int main() {
EXPECT_THROW(m("hat"), std::runtime_error);
}
Output:
one argument constructor
gtt.cc:12: Failure
Expected: m("hat") throws an exception of type std::runtime_error.
Actual: it throws nothing.
EDIT
I do not claim to be an expert on the arcane inner-workings of the C/C++ pre-processor, but I think this has to do with the rules followed when evaluating expressions, in particular, in the land of the pre-processor, parentheses are king. When the pre-processor evaluates MyClass(filename) it first evaluates filename which produces a temporary value which is immediately discarded, it then evaluates MyClass(). Calling MyClass("filename") causes the pre-processor to actually copy the literal string into the expression. One way around this problem is to call EXPECT_THROW((MyClass(filename)), std::runtime_error), i.e. use a set of enclosing parentheses around your statement.