views:

160

answers:

3

I am looking for a way to set a warning that the caller will have to respond to. In a sense I would like to use a late exception mechaninism that occurs after the function already finished executing and returned the wanted value.

SomeObject Foo(int input)
{
   SomeObject result;      
   // do something. oh, we need to warn the caller. 

   return result;
}

void Main()
{
   SomeObject object;
   object = Foo(1); // after copy constructor is done I would like an exception to be thrown


}
A: 

Throwing "exception" and continuing to "return the normal value" contradict.

As you wrote, you can remember the problem has happened in a some member of SomeObject or some variable which is passed as a reference to the constructor.

To prevent the caller not handling the error, you can construct a "signalling member" in such a way that if the caller is not polling it, you will assert in destructor of SomeObject (or in destructor of the signalling member).

Possible example of signalling member:

template <typename Base>
class Signal
{
private:
  Base _val;
  bool _polled;

public:
  operator Base() const {_polled=true;return _val;}
  // even when we do not set the value, we force the user to poll - it makes testing easier
  Signal():_polled(false) {}
  // but once we set it, we mark he has to poll
  Signal(Base val):_val(val),_polled(false) {}
  // if he does not, we assert
  ~Signal(){assert(_polled);}
};

If you set the value and the caller will not "get" it, the destructor will assert. If the caller will get it but ignore anyway, then you are out of luck - but this is the same with exceptions: if the caller catches it and ignores it, you cannot do a thing about it. Here is how would you use it:

SomeObject Foo(int input, Signal<int> &errorCode)
{
  SomeObject result;      
  // do something. 
  if (somethingBadHappened)
    errorCode = 1234; //oh, we need to warn the caller. 

  return result;
}

void Main()
{
  SomeObject object;
  Signal<int> error;
  object = Foo(1,error);

  // error not served? => assert
  // to prevent the assert firing, uncomment the following line
  // int handle = error;
}

Note: instead of assert, you could also throw an exception in the Signal destructor. Be aware, though, that throwing from destructors is hard to get right - you need to be extra carefull about how exactly you use the Signal class (and what will be used as Base for it) then.

Suma
But what is the user will ignore that variable?
Hagai
Extended the answer. It will fire and assert then.
Suma
I would be inclined to change the return type to be a Signal<SomeObject, int>, and add a method to get the return value in addition to the method to get the error code. But then I've always disliked functions that take an error parameter.
Dennis Zickefoose
A: 

When an exception is thrown, the compiler will look at each stack frame in turn, beginning with the current, and look for a catch block that can handle the type of the exception that was thrown.

As the compiler looks in each stack frame, it cleans up all local variables in that frame -- so even if your function were to return a value and then an exception were thrown "after the function already finished executing", the return value would be cleaned up anyway as the compiler unwound the stack. So what's the point of waiting for the function to finish? Just throw an exception right there and then:

SomeObject foo(int input)
{
   SomeObject result;

   // Do stuff

   // Something goes wrong in an unrecoverable fashion
   throw BadThingHappenedException;

   // More code for the good case
   return result;
}
Nick Meyer
The exception should be thrown outside of the function after the stack was already closed. How? Maybe a wrapper object to the return value from the function that throws an exception when a warning flag was set in the wrapper. but the value still exists in the data object that it is wrapping
Hagai
My point was that I'm not sure what you need the value that "still exists in the data object" for if an exception is going to be thrown -- the compiler is going to clean that object up while looking for an exception handler before the user can do anything with it anyway.
Nick Meyer
The point I gather is that he doesn't want to throw an exception right away specifically because there is still a "valid" return value that should be saved. So he wants to pass it out, then throw, so that the object is still live, provided the caller catches the exception. Its kind of an "almost successful" state. I allocated the memory you asked for, but only half as much as you wanted, so if you don't check the size I'm going to crash now rather than let you crash later.
Dennis Zickefoose
+4  A: 

You have to make a more concrete decision, I think. It's very unorthodox to (somehow) warn the user of a function while giving them a result.

For example, you could return a std::pair<SomeObject, std::string>, where the warning is in the string, if any. But it'll be very easy for people to ignore that.

An exception isn't the solution: if you can continue execution in a meaningful way, it's not an exceptional situation. It's possible to come up with some system of letting them finish with an exception "in queue" somewhere, but when should it be thrown? And for what purpose? Surely it will end up cutting of the remainder of the work anyway.

I think what you want is an assert. assert that a condition holds true; if it doesn't, the programmer will be notified and can handle the situation accordingly. Remember, assert's are for programmers, exceptions are for the program.

Perhaps if you give us a more concrete example, we'd be able to suggest the best course of action.

GMan