Forcible error return codeGuy Peleg Summary: how to force the function caller to accept and use the error code returned by the function. Andrei Alexandrescu describes a framework in his article "three ideas" (http://www.ddj.com/dept/cpp/184401917) that enforces error return codes, the caller used to force the function to accept and use the error code returned by the function. This code will be called "forcing methods" in this article )". The idea is simple and powerful: Define a class template errorcode and hold the error code returned from the forced method. When the returned value leaves the scope, it throws an exception unless the caller has used the error code (using the type conversion operator ). The caller can also explicitly ignore error codes by converting errorcode to ignoreerror. The following code list 1 shows the classes that make up the framework, and routine 1 shows how to use it.
Code List 1: struct ignoreerror {}; template <class T> class errorcode {mutable bool read _; T code _; public: errorcode (const T & code ): read _ (false), Code _ (CODE) {} errorcode (const errorcode & RHs): Read _ (RHS. read _), Code _ (RHS. code _) {RHS. read _ = true;} operator T () {// eliminate the exception this-> Read _ = true; return this-> code _;} operator ignoreerror () {// clear the exception this-> Read _ = true; return ignoreerror ();}~ Errorcode () {If (! This-> Read _) {Throw mandatoryerrorcodeexception (....);}}}; |
Example 1: errorcode <int> fallablefunction (); // function declaration errorcode <int> result = fallablefunction (); // okif (fallablefunction ()){...} // okfallablefunction (); // throw an exception (ignoreerror) fallablefunction (); // OK |
However, the problem with this framework is that an exception is thrown in the destructor of errorcode. Let's look at an example. The following example Example 2 looks normal. You didn't ignore the return code. You accept and use them. There is even a specific recover method to capture all exceptions. Yes, almost all exceptions.
Example 2: void Aclass: somemethod () {try {errorcode <int> EC1 = This-> fallablemethod_1 (); errorcode <int> EC2 = This-> fallablemethod_2 (); if (EC1 & EC2) {This-> dosomething () ;}} catch (...) {This-> recover ();}} |
Here is a problem: If this-> fallablemethod_2 () throws an exception, the destructor of EC1 will be called. But at this time, EC1 is not used, so another exception is thrown (from the destructor of EC1), which will abort this program (set_terminate is called but no completion ). This is a big surprise for you who have followed all the rules.
SolutionIf you want to use this framework effectively, you must make some adjustments to it. First, you need to change the assumptions that errorcode must be used. In fact, there is no real way to ensure that the user actually uses an error return code. You should be more concerned about whether the caller accepts the return code, rather than whether the caller uses it. After all, the caller can accept the return code and assign it to a variable that is never used. Second, make sure that the thrown exception does not stop the program. To do this, first Add a new class throwalbeerrorcode (Code List 2 ). This class is returned from the methods to force the error code check, replacing the previous errorcode. Throwalbeerrorcode does not provide access to the error code it holds.
Code List 2: Template <class code> class throwableerrorcode {template <class code> friend class errorcode; code _; bool throw _; public: // constructor-Accept the error code and prepare the exception throwableerrorcode (Code I _code): code _ (I _code), throw _ (true) {}// explicitly ignore the error code and eliminate the exception operator ignoreerror () {This-> throw _ = false; return ignoreerror ();}~ Throwableerrorcode () {// throw an exception unless errorcode <code> or ignoreerror if (this-> throw _) {Throw mandatoryerrorcodeexception (...);}}}; |
Throwableerrorcode can throw an exception. In fact, it will throw an exception when it leaves the scope. The only way to prevent it from throwing an exception is to explicitly use the ignoreerror transformation operator of throwableerrorcode or assign it to an errorcode object. The new errorcode (Code List 3) does not throw an exception. It is responsible for eliminating exceptions in throwableerrorcode and providing access to error codes.
Code List 3: Template <class code> class errorcode {code _; public: // explicit constructor, make sure that the class user knows what she/he is doing. Explicit errorcode (throwableerrorcode <code> & code): code _ (code. code _) {// eliminate the exception in throwableerrorcode and throw the code. throw _ = false;} operator code () {return this-> code _;}~ Errorcode (){}}; |
The caller using this method has the following options: l accepts the returned throwableerrorcode object and assigns it to an errorcode object (assuming that the caller will use it ). This is a good habit. L The error return code is explicitly ignored by using the ignoreerror operator. This is also a good habit. L ignore the returned throwableerrorcode object. A temporary throwableerrorcode instance will be created and destroyed in the scope of a statement. This will cause an exception but will not endanger the entire program. This is a bad habit. L use the throwableerrorcode object to accept the return code, which means that the exception is not eliminated. This indicates that the call is willing to hold an object that may throw an exception. In this case, it is better for the caller to know what they are doing. This is also a bad habit. Example 3 shows how you use this new framework. Let's take a look at the problem in example 2. Now the code works well, because errorcode EC1 and EC2 eliminate exceptions in throwableerrorcode returned from fallablefunction. Even if an exception is thrown in fallablefunction, the code can reach the catch statement as you wish.
Example 3: // function declaration throwableerrorcode <int> fallablefunction (); // okerrorcode <int> result = fallablefunction (); If (0 = Result ){...} // throw an exception-but does not stop the program if (fallablefunction ()){...} // throw an exception-but the program fallablefunction () will not be aborted; // OK-explicitly ignore the error return code (ignoreerror) fallablefunction (); // an exception may be thrown-the caller is taking an adventure! Throwableerrorcode <int> mightthrow = fallablefunction (); |
ThanksThank you Andrei Alexandrescu!