How to implement final conditions properly?
This is what I'm trying to do (this is a simplification of a real project):
int param;
int result;
void isolated(int p) {
param = p;
try {
// make calculations with "param"开发者_运维技巧 and place the
// result into "result"
process();
} catch (...) {
throw "problems..";
}
}
I can't change the way process()
works, since this function is not created in the project and is a third-party function. It works with global variables param
and result
and we can't change this.
The problem appears when isolated()
is called back from process()
with another parameter. I want to catch this situation, but don't know how to do it, since finally
is absent in C++. I feel that I should use RAII technique, but can't figure out how to do it in this case properly.
This is how I can make it with code duplication:
int param;
int result;
void isolated(int p) {
static bool running;
if (running) {
throw "you can't call isolated() from itself!";
}
running = true;
param = p;
try {
// make calculations with "param" and place the
// result into "result"
process();
running = false;
} catch (...) {
running = false; // duplication!
throw "problems..";
}
}
"finally" like situations are handled in C++ using guard objects, that do their finally thing in the destructor. This is IMHO much more powerful approach, because you have to analyze the situation to finalize in order to create a reuseable object. In this case, we need to make process rentrant, because parameters and returns are passed in globals. The solution is to save their values on entry and restore them on exit:
template<class T>
class restorer
{
T &var; // this is the variable we want to save/restore
T old_value; // the old value
restorer(const restorer&);
void operator=(const restorer&);
public:
restorer(T &v) : var(v), old_value(v) {}
~restorer() { var=old_value; }
};
int param;
int result;
int isolated(int p) {
restorer<int> rest_param(param);
restorer<int> rest_result(result);
param = p;
try {
// make calculations with "param" and place the
// result into "result"
process();
return result;
} catch (...) {
return 0;
}
}
Maybe I didn't get it right, but why don't you use a flag? You want to know when the isolated() is called from the process(), right?
int isolated(int p) {
static int execDeep = 0;
execDeep++;
// your code here
execDeep--;
}
Now you can check 'execDeep' value, > 1 means it is called from the process() while still being executed.
I still don't quite sure how finally
is related here, but you could try Boost.ScopeExit if you want to avoid creating a scope guard structure yourself.
Example:
#include <boost/scope_exit.hpp>
#include <cstdio>
int isolated(int p) {
static bool running = false;
if (running) {
printf("Throwing %d\n", p);
throw p;
}
printf("Starting %d\n", p);
running = true;
BOOST_SCOPE_EXIT( (p)(&running) ) { // <--
printf("Stopping %d\n", p); // <--
running = false; // <--
} BOOST_SCOPE_EXIT_END // <--
// ...
if (p)
isolated(p*10);
// ...
printf("Returing %d\n", p);
return 4;
}
int main() {
printf(">> first\n");
isolated(0);
printf(">> second\n");
try {
isolated(1);
printf(">> third (should not be printed.)\n");
} catch(int p) {
printf("Caught %d\n", p);
}
isolated(0);
printf(">> fourth\n");
return 0;
}
Result:
>> first Starting 0 Returing 0 Stopping 0 >> second Starting 1 Throwing 10 Stopping 1 Caught 10 Starting 0 Returing 0 Stopping 0 >> fourth
Could this work?
int save = -10000000000;
int param;
int result;
int isolated(int p) {
if (save != -10000000000)
{
// run the other condition
}
else
{
save = p;
param = p;
try {
// make calculations with "param" and place the
// result into "result"
process();
return result;
} catch (...) {
return 0;
}
}
}
If I understand correctly, you want to automatically set the running flag to false at the end of function. If that is the requirement then you can use the ScopeGuard approarch mentioned in the link.
精彩评论