开发者

Alternative to C++ exception

开发者 https://www.devze.com 2023-01-01 14:00 出处:网络
I\'m writing a reactive software, which repeatedly recieves input, processes it and emits relevant output. The main loop looks something like:

I'm writing a reactive software, which repeatedly recieves input, processes it and emits relevant output. The main loop looks something like:

initialize();
while (true) {
    Message msg,out;
    recieve(msg);
    process(msg,out);
    //no global state is saved between loop iterations!
    send(out);
}

I want that whatever error occured during the proccess phase, whetehr it is out of memory error, logical error, invalid assertion etc, the program will clean up whatever it did, and keep running. I'll assume it is invalid input, and simply ignore it.

C++'s exception are exceptionally good for that situation, I could surround process with try/catch clause, and throw exception whenever something goes wrog. The only thing I need to make sure that I clean up all my resources before throwing an exception. This could be verified by RAII, or by writing a global resource allocator (for instance, if your destructor might throw an exception), and use it exclusively for all resources.

Socket s = GlobalResourceHandler.manageSocket(new Socket());
...
try {
    process(msg,out);
catch (...) {
    GlobalResourceHandler.cleanUp();
}

However, using exception is forbidden in our coding standard (also in Google's C++ standard BTW), as a result all the code is compiled with exceptions off, and I believe nobody's going to change the way everything work just for my design problem.

Also, this is code for embedded platform, so the less 开发者_StackOverflow社区C++ extra feature we use, the faster the code becomes, and the more portable it is.

Is there an alternative design I can consider?

update: I appreciate everyones answer about idiotic code standard. The only thing I can say is, in big organizations you have to have strict and sometimes illogical rules, to make sure no idiot would come and make your good code unmaintainable. The standard is more about people than about technicalities. Yes, bad man can make every code a mess, but it's much worse if you give him extra tools for the task.

I'm still looking for a technical answer.


Coding these kind of services all day long I understand your problem. Although we do have exceptions within our code, we don't return them to the external libraries that invoke it, instead we have a simple 'tribool'.

enum ReturnCode
{
  OK = 0,  // OK (there is a reason for it to be 0)
  KO,      // An error occurred, wait for next message
  FATAL    // A critical error occurred, reboot
};

I must say FATAL is... exceptional. There isn't any code path in the application that returns it, apart from the initialization (can't do much if you're not initialized properly).

C++ here brings much with RAII, since it laughs multiple paths of return off and guarantees deterministic release of the objects it holds.

For the actual code checking, you can simply use some macros:

// Here is the reason for OK being 0 and KO and Fatal being something else

#define CHECK_RETURN(Expr) if (ReturnCode code = (Expr)) return code;

#define CHECK_BREAK(Expr) if (ReturnCode code = (Expr)) \
    if (KO == code) break; else return code;

Then you can use them like so:

CHECK_RETURN( initialize() )
while(true)
{
  Message msg,out;
  CHECK_BREAK( receive(msg) )
  CHECK_BREAK( process(msg,out) )
  CHECK_BREAK( send(out) )
}

As noted, the real bummer is about constructors. You can't have "normal" constructors with such a situation.

Perhaps can you use boost::optional, if you can't, I would really suggest duplicating the functionality. Combine that with systemic factory functions in lieu of constructors and you're off to go:

boost::optional<MyObject> obj = MyObject::Build(1, 2, 3);
if (!obj) return KO;

obj->foo();

Looks much like a pointer, except that it's stack allocated and thus involves near zero overhead.


If you can't throw an exception, then the alternative is to return (or to return false or similar error code).

Whether you throw or return, you still use C++ deterministic destructors to release resources.

The one thing that you can't easily just 'return' from is a constructor. If you have an unrecoverable error in a constructor, then it's a good time to throw; but if you're not allowed to throw, then instead you must return, and in that case you need some other way to signal construction failure:

  • Have private constructors and static factory methods; have the factory method return null on construction failure; don't forget to check for a null return when you call a factory method
  • Have a "get_isConstructedOk()" property which you invoke after each constructor (and don't forget to invoke/check it on every newly-constructed object)
  • Implement 'two-stage' construction: in which you say that any code which might fail mustn't be in a constructor, and must instead be in a separate bool initialize() method that's called after the constructor (and don't forget to call initialize and don't forget to check its return value).


However, using exception is forbidden in our coding standard (also in Google's C++ standard BTW). Is there an alternative design I can consider?

Short answer is no.

Long answer yes :). You can make all functions return an error code (similar to the implementation of Microsoft's COM platform.

The main disadvantages of this approach are:

  • you have to handle all exceptional cases explicitly

  • your code size increases dramatically

  • the code becomes more difficult to read.

Instead of:

initialize();
while (true) {
    Message msg,out;
    recieve(msg);
    process(msg,out);
    //no global state is saved between loop iterations!
    send(out);
}

you have:

if( !succeedded( initialize() ) )
    return SOME_ERROR;

while (true) {
    Message msg,out;
    if( !succeeded( RetVal rv = recieve(msg) ) )
    {
         SomeErrorHandler(rv);
         break;
    }
    if( !succeeded( RetVal rv = process(msg,out) ) )
    {
         SomeErrorHandler(rv);
         break;
    }
    //no global state is saved between loop iterations!
    if( !succeeded( RetVal rv = send(out) ) )
    {
         SomeErrorHandler(rv);
         break;
    }
}

furthermore, the implementation all your functions will have to do the same: surround each function call with an if.

In the example above, you also have to decide if the rv value on each iteration constitutes an error for the current function and (eventually) return it directly from the while, or break the while on any error, and return the value.

In short, except for possibly using RAII in your code and templates (are you allowed to use them?), you end up close to "C code, using the C++ compiler".

Your code transforms each function from a two-liner into an eight-liner and so on. You can improve this with use of extra functions and #defined macros but macros have their own idiosyncrasies that you have to be really careful about.

In short, your coding standards are making your code unnecessarily longer, more error prone, harder to understand and more difficult to maintain.

This is a good case to present to whoever is in charge of the coding standards in your company :(

Edit: You can also implement this with signals but they are a bad replacement for exceptions: they do the same thing as exceptions, only they also disable RAII completely and make your code even less elegant and more error prone.


Just because using exceptions is forbidden in your current coding standards this does not mean that you should dismiss them out of hand for future problems you encounter such as this. It may the case that your current coding standards did not envisage such a scenario arising. If they did they would probably give you help as to what the alternative implementation would be.

This sounds to me like a good time to challenge your current coding standards. If the people that wrote them are still there then speak to them directly as they will either be able to answer your question as to alternative strategies or they will accept that this is a valid use-case for exceptions.


However, using exception is forbidden in our coding standard (also in Google's C++ standard BTW). Is there an alternative design I can consider?

Coding standards like that are nuts.

I suggest that you ask the person / people who developed and mandated that standard how to solve your problem. If they have no good answer, use this as justification for ignoring that part of the coding standard ... with your bosses permission of course.


If you are running under windows, you could use SEH exceptions. They also have the advantage of pre-stack-unwind handler which can stop unwind (EXCEPTION_CONTINUE_EXECUTION).


Off the top of my head, you might be able to achieve something similar with signals.

Set up a signal handler to catch appropriate signals and have it clean things up. For example, if your code generates a SIGSEGV as a result of something that would otherwise have thrown an exception a little earlier, you can try catching it with the signal handler.

There may be more to it than this as I have not thought it through.

Hope this helps.


Do you call any libraries that could raise exceptions? If it's the case, you will need a try catch anyway. For your internal errors, each method will need to return an error code (use return only for error code, use reference parameters to return the actual values). If you want to make memory cleanup 100% reliable, you could start your application using a monitor application. If your application crash, the monitor start it again. You still need to close files and DB connection, tho.


Another approach is, instead of throwing exception, set a global error indicator, and return a legal but arbitary input. Then checking in every loop iteration whether or not the global error indicator is set, and if it is - return.

If you're careful enough, you can make sure that returning legal data will never cause you to crash or to cause undefined behaviour. Thus you shouldn't care that the software will keep running a bit until it reaches to the nearest error checking condition.

For example

#define WHILE_R(cond,return_value) while (cond) {\
    if (exception_thrown) return return_value
#define ENDWHILE() }

bool isPolyLegal(Poly p) {
    PolyIter it(p);
    WHILE_R(it.next(),true) //return value is arbitary
    ...
        if (not_enough_memory) exception_thrown = true;
    ...
    ENDWHILE()
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号