Let's do an example
class X
{
int value;
public:
X (int def = 0) : value (def) {}
void add (int i)
{
value += i;
}
};
Clearly, the function void X::add (int) will never throw any exception.
My question is, can the compiler analyze the code and decide not to generate machine code to handle exceptions, even if the function is not marked as noexcept?
If the compiler can prove that a function will never throw, it is allowed by the "As-If" rule (§1.9, "Program execution" of the C++ standard) to remove the code to handle exceptions.
However it is not possible to decide if a function will never throw in general, as it amounts to solving the Halting Problem.
Related
Consider this snippet:
static inline void g() {
throw 10;
}
int f() {
try {
g();
} catch (const int &x) {
return x;
}
return 0;
}
As you can see from the Compiler Explorer, GCC is able to inline the call to g, but still goes through the whole process of throwing and catching the expression, calling the runtime library. Is there any reason why GCC couldn't just compile f to mov eax, 10; ret? The same happens with clang, with little differences.
An optimization like this would seem when the compiler can inline the throwing site inside the catching site, which perhaps would happen frequently if you call some throwing STL method and catch just outside the call. I guess that avoiding going through the runtime could be a significant improvement in hot loops.
Consider the following code class:
class A {
public:
int number;
vector<int> powers;
A () {
number = 5;
powers.resize(100);
}
long long getPower(int x) {
return powers[x];
}
void precompute() {
powers[0] = 1;
for (int i = 1; i < 100; i++) {
powers[i] = powers[i - 1] * number;
}
}
};
In the class A, we have a vector called powers and an integer number with the property that powers[k] stores the quantity numbers^k after the precompute() function has been called. If we want to answer several queries of the form "Compute numbers^x for some integer 0 <= x < 100", it makes sense to precompute all of these powers and return them when we need them as a constant-time operation (note: this is not a problem that I am actually facing. I have made this problem up for the sake of an example. Please ignore the fact that numbers^x would exceed the maximum value of a long long).
However, there is one issue: the user must have called the precompute() function before calling the getPower() function.
This leads me to the following question: Is there some nice way to enforce the constraint that some function A can only be called after function B is called? Of course, one could just use a flag variable, but I am wondering if there is a more elegant way to do this so that it becomes a compile-time error.
Another option would be to always call the precompute() function in the constructor, but this may not be an optimal solution if we weren't always going to call precompute() in the first place. If calling precompute() is a sufficiently expensive (computationally), then this method would not be preferable.
I would prefer getting a compile-time error over a runtime error, but I am open to all approaches. Does anyone have any ideas?
One solution to your problem would be to call the precompute function in the constructor of class A.
Alternatively, as has already been suggested in the comments section, you could make the function getPower check a flag which specifies whether precompute has already been called, and if not, either perform the call itself or print an error message.
I can't think of a way to force this check to be done at compile time. However, if you want to eliminate this run-time check from release builds, you could use conditional compilation so that these checks are only included in debug builds, for example by using the assert macro or by using preprocessor directives, like this:
// note that NDEBUG is normally only defined in release builds, not debug builds
#ifdef NDEBUG
//check for flag here and print error message if flag has unexpected value
#endif
As alternative, to enforce timing dependency, you might make the dependency explicit.
For example:
class PowerGetter
{
friend class A;
const A& a;
public:
long long getPower(int x) {
return a.powers[x];
}
};
class A {
public:
int number = 5;
std::vector<int> powers = std::vector<int>(100);
A() = default;
PowerGetter precompute() {
powers[0] = 1;
for (int i = 1; i < 100; i++) {
powers[i] = powers[i - 1] * number;
}
return {*this};
}
};
Then to call getPower we need a PowerGetter which can only be obtained by calling precompute first.
For that contrived example, simpler would be to place initialization in A though.
I am learning how c++ is compiled into assembly and I found how exceptions works under the hood very interesting. If its okay to have more then one execution paths for exceptions why not for normal functions.
For example, lets say you have a function that can return a pointer to class A or something derived from A. The way your supposed to do it is with RTTI.
But why not, instead, have the called function, after computing the return value, jump back to the caller function into the specific location that matchs up with the return type. Like how exceptions, the execution flow can go normal or, if it throws, it lands in one of your catch handlers.
Here is my code:
class A
{
public:
virtual int GetValue() { return 0; }
};
class B : public A
{
public:
int VarB;
int GetValue() override { return VarB; }
};
class C : public A
{
public:
int VarC;
int GetValue() override { return VarC; }
};
A* Foo(int i)
{
if(i == 1) return new B;
if(i == 2)return new C;
return new A;
}
void main()
{
A* a = Foo(2);
if(B* b = dynamic_cast<B*>(a))
{
b->VarB = 1;
}
else if(C* c = dynamic_cast<C*>(a)) // Line 36
{
c->VarC = 2;
}
else
{
assert(a->GetValue() == 0);
}
}
So instead of doing it with RTTI and dynamic_cast checks, why not have the Foo function just jump to the appropriate location in main. So in this case Foo returns a pointer to C, Foo should instead jump to line 36 directly.
Whats wrong with this? Why aren't people doing this? Is there a performance reason? I would think this would be cheaper then RTTI.
Or is this just a language limitation, regardless if its a good idea or not?
First of all, there are million different ways of defining the language. C++ is defined as it is defined. Nice or not really does not matter. If you want to improve the language, you are free to write a proposal to C++ committee. They will review it and maybe include in future standards. Sometimes this happens.
Second, although exceptions are dispatched under the hood, there are no strong reasons to think that this is more efficient comparing your handwritten code that uses RTTI. Exception dispatch still requires CPU cycles. There is no miracle there. The real difference is that for using RTTI you need to write the code yourself, while the exception dispatch code is generated for you by compiler.
You may want to call you function 10000 times and find out what will run faster: RTTI based code or exception dispatch.
I came across an interesting secure coding rule in C++ which states:
Do not reenter a function during the initialization of a static variable declaration. If a function is reentered during the constant initialization of a static object inside that function, the behavior of the program is undefined. Infinite recursion is not required to trigger undefined behavior, the function need only recur once as part of the initialization.
The non_compliant example of the same is:
#include <stdexcept>
int fact(int i) noexcept(false) {
if (i < 0) {
// Negative factorials are undefined.
throw std::domain_error("i must be >= 0");
}
static const int cache[] = {
fact(0), fact(1), fact(2), fact(3), fact(4), fact(5),
fact(6), fact(7), fact(8), fact(9), fact(10), fact(11),
fact(12), fact(13), fact(14), fact(15), fact(16)
};
if (i < (sizeof(cache) / sizeof(int))) {
return cache[i];
}
return i > 0 ? i * fact(i - 1) : 1;
}
which according to the source gives the error:
terminate called after throwing an instance of '__gnu_cxx::recursive_init_error'
what(): std::exception
when executed in Visual Studio 2013. I tried similar code of my own and got the same error (compiled using g++ and executed, on Ubuntu).
I am doubtful if my understanding is correct with respect to this concept as I am not well-versed with C++. According to me, since the cache array is constant, which means it can be read-only and needs to be initialized only once as static, it is getting initialized again and again as the values for this array is the value returned by each of the comma-separated recursive function calls which is against the behavior of the declared array. Thus, it gives undefined behavior which is also stated in the rule.
What is a better explanation for this?
In order to execute fact(), you need to first statically initialize fact::cache[]. In order to initially fact::cache, you need to execute fact(). There's a circular dependency there, which leads to the behavior you see. cache will only be initialized once, but it requires itself to be initialized in order to initialize itself. Even typing this makes my head spin.
The right way to introduce a cache table like this is to separate it into a different function:
int fact(int i) noexcept(false) {
if (i < 0) {
// Negative factorials are undefined.
throw std::domain_error("i must be >= 0");
}
return i > 0 ? i * fact(i - 1) : 1;
}
int memo_fact(int i) noexcept(false) {
static const int cache[] = {
fact(0), fact(1), fact(2), fact(3), fact(4), fact(5),
fact(6), fact(7), fact(8), fact(9), fact(10), fact(11),
fact(12), fact(13), fact(14), fact(15), fact(16)
};
if (i < (sizeof(cache) / sizeof(int))) {
return cache[i];
}
else {
return fact(i);
}
}
Here, memo_fact::cache[] will only be initialized once - but its initialization is no longer dependent on itself. So we have no issue.
The C++ standard, §6.7/4, says the following about the initialisation of block-scope variables with static storage duration:
If control re-enters the declaration recursively while the variable is
being initialized, the behavior is undefined.
The following informative example is given:
int foo(int i) {
static int s = foo(2*i); // recursive call - undefined
return i+1;
}
This applies to your example as well. fact(0) is a recursive call, so the declaration of cache is re-entered. Undefined behaviour is invoked.
It's important to recall what undefined behaviour means. Undefined behaviour means that everything can happen, and "everything" quite naturally includes exceptions being thrown.
Undefined behaviour also means that you can no longer reason about anything else in the code, except when you really want to get down to compiler-implementation details. But then you are no longer talking about C++ in terms of using a programming language but in terms of how to implement that language.
This question provides more clarity on the problem described here. I did some more investigation and found that the stack unwinding is not happening in the following piece of code:
class One
{
public:
int x ;
};
class Wrapper
{
public:
Wrapper(CString csText):mcsText(csText)
{
CString csTempText;
csTempText.Format("Wrapper constructor :: %s\n", mcsText);
OutputDebugString(csTempText);
}
~Wrapper()
{
CString csTempText;
csTempText.Format("Wrapper destructor :: %s\n", mcsText);
OutputDebugString(csTempText);
}
CString mcsText;
};
class Test
{
public:
void notifyError()
{
try
{
int x = 10;
}
catch(...) {}
}
void OnRecvBuffer()
{
try
{
Wrapper a("AddRef");
One* p = NULL;
p->x = 10;
}
catch(...)
{
notifyError();
}
}
};
int main()
{
Test* pTest = new Test;
pTest->OnRecvBuffer();
OutputDebugString("Test");
}
I compiled this code using VC6 SP5 compiler and the output is "Wrapper constructor :: AddRef!!!" (i.e. the destructor of wrapper object which was constructed on stack is not called. Is this the expected behavior ? or is it a bug with VC compiler ? Can I use some compiler flags so that the stack unwinding happens in this case?
The C++ standard does not give anything to work with in case of Undefined Behavior. Even if MS does. That's a platform specific thing -- so beware. Some such floating point exceptions are turned to Win32 exceptions which you can attempt to catch with _set_se_translator(). The problem is you can catch Win32 exceptions but then your stack will not be unwound properly. At least that is not something you can bet your life on. Wherein lies the futility of the exercise.
Update: The exception is thrown intentionally to check the stack unwinding. The question is why Wrapper class's destructor is not getting called. – Naveen
If this is the case -- don't do it. There are better ways to throw exceptions than via Undefined Behavior.
E.g:
void OnRecvBuffer()
{
try
{
Wrapper a("AddRef");
throw 42; // this'll throw an exception without invoking UB
}
catch(...)
{
notifyError();
}
}
You cannot dereference a NULL pointer. You are invoking Undefined Behavior here:
One* p = NULL;
p->x = 10;
After that line all bets are off and you could have killed us all ;)
p is a pointer to a One object. It should contain the address of an One object. You have initialized it to 0 -- there is no object at address 0. 0 is not a valid address for any object (this is guranteed by the standard).
If you want to use SEH, you must use _set_se_translator function and /EHa compiler option.
Because C++ regular exception does not handle this kind of exception, you have to use SEH which does not know anything about the app and does not unwind.
This is undefined behavior:
One* p = NULL;
p->x = 10;
At this point the application is free to crash without unwinding the stack.
If you want to test the stack unwinding replace this with:
throw 42; // Life the Universe and Everything thrown away
You should not dynamically allocate all your objcts this is C++ not Java!
int main()
{
Test pTest; // Note the lack of new!
pTest.OnRecvBuffer();
OutputDebugString("Test");
}