-1

This is kind of concept question.

Lets assume that we have some code base that works with hardware from high level and whole error handling mechanism is implemented by exceptions. Lets assume that we are opening/closing some valve(s). As long this hardware operation have finalizing procedure we need to use RAII conception. So some foo() procedure could look like this:

class Valve()
{
public:
    Valve()
    {
        // open valve
    } 
    ~Valve()
    {
        // close valve
        // Potential exception here
    } 
private:
    // valve internal stuff
}

void foo()
{
   try
   {
       Valve v;       
       bar1(v); // <--- throws something
   } catch(...)
   {
        // report error and exit
        // it's guaranteed that valve destructor will be called
   }
}

This piece of code looks nice, but how we can manage errors that could happen during valve closing. Exception couldn't leave destructor. The only way I see is keeping error in some error storage, like this:

Valve::~Valve()
{
    try
    {
       // close valve
    } catch(...)
    {
        errorStorage.Add(...);
    }
} 

But this approach looks ugly. Are there any ideas how to deal in this situation? Of course one way is not using of the exceptions at all, but use return code approach and some cleanup action (with goto in case of error).

UP: Originally I wanted to avoid this kind of logic duplication:

void foo()
{
   try
   {
       Valve v;       
       v.open();    // <- could throw
       bar1(v);     // <- could throw
       v.close();   // <- could throw
   } catch(...)
   {
        if(v.opened())
             v.close(); // kind of logic duplication
   }
}
Anton_nsk
  • 21
  • 5
  • Well you have two sequential exception throws so you'll have to have minimum two try-catch constructs so the question is where you want place them. Your approach looks fine to me, as an alternative you can add separate methods `Valve::open()` and `Valve::close()` – Alexey Andronov Aug 10 '18 at 03:46
  • Thanks, this comment looks reasonable. The reason I asked was attempt to avoid code/logic duplication. – Anton_nsk Aug 13 '18 at 23:43

1 Answers1

0

Another approach would be for the destructor to handle the error directly.

Valve::~Valve()
{
    try
    {
        // close valve
    } catch(...)
    {
        // handle valve close error
    }
}

Admittedly, someone could probably come up with a case when this is inadequate, but the question lacks the details for that sort of determination. (Lacking those details may be a good thing, if the intent is to find a variety of answers. After all, it is self-described as a "concept question".) Simple logging of which valve failed to close is one case where this should work.

At a high level, I would expect this approach to simplify the code overall, since the handling of the valve close error has been localized to the Valve class. Code using the Valve class would not need to know that a valve can be closed, much less that an error could occur while closing a valve, which improves data encapsulation.

Naturally, this approach does require that no exceptions are thrown by the code that handles the valve close error. (This is probably a good goal anyway, since it is part of error handling.)


The plan to add the error to some sort of error storage looks dubious to me. I see "add" and I think "possibly allocates memory", which implies "possibly throws an exception if memory has been exhausted". So I'd wonder if steps had been taken to prevent an exception being thrown by this mechanism that is supposed to delay the throwing of an exception. Does this solve the problem or just make it less likely?

JaMiT
  • 14,422
  • 4
  • 15
  • 31
  • Actually this question has not came from real life, so I cannot answer to some of your questions. The reason I'm asking this is that I want to understand how to avoid code duplication in this situation: void foo() { try { Valve v; v.open(); // <- could throw bar1(v); // <- could throw v.close(); // <- could throw } catch(...) { if(v.opened()) v.close(); // kind of logic duplication } } Looks like RAII could help in this case. – Anton_nsk Aug 13 '18 at 23:33
  • Sorry for this mess, I updated original question message with one more code example. – Anton_nsk Aug 13 '18 at 23:41
  • @Anton_nsk Yes, think in terms of RAII. If your program must close every `Valve` it opens, then the `Valve` destructor should make sure it is closed. If `foo()` calls `close()` *solely* because every `Valve` must be closed, remove that call from `foo()` and let the destructor handle it. Similarly, you might want to move the `open()` call to the constructor. – JaMiT Aug 20 '18 at 15:31