Googletest does not accept temporary object in EXPECT_THROW - c++

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.

Related

Understanding macro code statement with lambda and __attribute__

My program(C++) is defining a macro for throw statements. It is something like:
#define FOO_THROW(some-exception-type, some-error-message) \
do \
{ \
[&]() __attribute__((cold, noinline)) \
{ \
using exception_type = some-exception-type; \
std::ostringstream ss; \
ss << some-error-message << ""; \
throw exception_type(ss.str()); \
} \
(); \
} \
while (0);
Then, it is getting called in the program, something like:
FOO_THROW(std::runtime_error, "error specfic message here")
I can see that we use do...while(0) structure with the macro, what I want to understand is the use of lambda and __attribute__ here, can someone explain that in simple words?
Basically, my focus is what is the need of lambda function here, when it can be done without it, is there can be any specific advantage of it here?
I am a beginner in C++, so any hint in the right direction will do as well!
do...while(0) allows the macro to by called with semicolon at the end, i.e. macro(x,y,z); (note the semicolon at the end), that's an old trick back from C, you can look it up separately.
As for the rest... it defines a lambda (immediate function object) capturing everything by reference (not sure why) that throws exception of a given type with a given message and calls it immediately, effectively throwing an exception.
The attributes signify the lambda is unlikely to be called (cold) and prevent from inlining (noinline), see: https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html

Capture caller line number at scope exit

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 detect whether a local variable declared?

Is it possible to detect whether a local variable declared?
The reason is that I'd like to have a macro (LOG), which has a logic like this:
if (<logContext is declared>) {
log(logContext, <parameters>);
} else {
DefaultLogContext logContext(*this);
log(logContext, <parameters>);
}
So, if the macro sees that there is a local variable named logContext, then it passes this variable to log. If logContext doesn't exist, then it creates a default logContext, and passes that to log.
Note: DefaultLogContext is a dependent type. It means that this logging macro can only be used in a class member function, and this type is a typedef to an actual type-dependent LogContext class:
class File {
typedef FileLogContext DefaultLogContext;
void open() {
LOG("open"); // here, I'd like LOG to create a new local logContext
}
void complexFunction() {
FileLogContext logContext(...);
logContext.setup();
LOG("pass1"); // here, I'd like LOG to use my declared logContext
pass1();
LOG("pass2"); // same comment as at pass1
pass2();
// other LOG and pass function calls here
};
Somewhat ugly solution that checks for a presence of local variable named local_context:
#include <iostream>
#include <type_traits>
constexpr class t_Decoy{} const local_context; // should be declared in global scope
class t_Context{};
#define LOG(text) \
[&](char const * const psz_text) -> void \
{ \
if constexpr(::std::is_same_v<t_Context, decltype(local_context)>) \
{ \ // we can use captured local_context here
::std::cout << psz_text << " has local_context" << ::std::endl; \
} \
else \
{ \ // we can create default context using captured this
::std::cout << psz_text << " no local_context" << ::std::endl; \
}\
}(text)
int main()
{
LOG("first");
t_Context local_context{};
LOG("second");
}
online compiler
No. Without reflection you cannot do such a thing autonomously. Then again, it would be of little benefit here.
Instead you should simply restructure your macros. You know, as the programmer, whether a variable is in scope at the point where you write your macro, and can instead use a different macro that does not require an extant variable.
You won't be able to do this via local variables. If you need an RAII style scoped logging facility, you have no choice but to create a local object explicitly.
LogScope logscope("some log msg");
If you don't need RAII, then you could have a global log object that maps __FUNCTION__ (or so) to a log context.
#define LOG(msg) { \
auto& logScope = g_logManager[__FUNCTION__]; \
}

Custom `assert` macro that supports commas and error message

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.

Throwing function-like variadic macro wrapping, replacing thrown exception

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() ); } }()