开发者

How should a constructor report errors? Pointers to external flags?

I am working on refactoring some of the old C-style code to bring it more into line with C++ code. I still am a bit new to C++

An example of the code I am working on is as follows

Errmsg foo{
   ErrMsg err = NoError;
   /*
    Some Processing
   */
  err = foo_cleanup(err,/* some parameters*/);
   /*
    Some More Processing
   */
  return err;
}

I was thinking of developing a class so that

class foo_class
{
 public:
   foo_class(Errmsg errmsg_init&,/*Some other arguments */ ):
      errmsg(&errmsg_init),
      /*Initialize other parameters */{}

   void foo_cleanup (/*Other parameters*/);
   // same functionality, but since the source variable is available, 
   // it can be modified without having to return any variable

  ~foo_class(){foo_cleanup(/*Parameters*/);}

   /*
   Member functions
   */

 private:
   Errmsg* errmsg;
   /*Some other parameters */
};

Errmsg foo{
   ErrMsg err = NoError; 
   foo_class foo_obj(err);
   /*
    Some Processing
   */

  // The class would be 
  //cleaned up before returning 
  // and the err variable would be 
  //modified in the destructor
  return err;
}

Although I have been able to use something similar to this approach, I do not know if this will be portable.开发者_StackOverflow

Is this the right thing to do?

If not, do I just use pointers to initialize the class instead of passing the error message variable by reference? Or is there something else I can do?

I cannot use exceptions at the current stage because there are many function calls to/from external code which use a "return error message" approach still.


Leaving aside that you should if possible fix the calling code so that an exception is OK, you could use the two-phase construction idiom:

struct foo_class {
    foo_class() { } // default constructor cannot fail
    Errmsg nothrow_init(/* other params */) throw() {
        /* initialize other parameters */
    }
    foo_class(/* other params */) {
        if (nothrow_init(/* other params */) != ERRMSG_OK) {
            throw something;
        }
    }
};

Now, exception-using code can call the multi-arg constructor (and get the object in a ready-to-use state), whereas exception-averse code can call the default constructor followed by nothrow_init (and live with the fact that if nothrow_init fails they have an unusable object on their hands, and it's their responsibility to ensure that they don't use it).

In future, as you bring other parts of your code base into an exception-using state, you will find that the code in init calls things that can themselves throw. At that point you can start moving code around, so that the nothrow_init calls an init function that can throw, but catches any exceptions and turns them into error codes. Assuming it's an internal class then eventually nothing will be using nothrow_init and you can remove it.


Your code is dangerous because it permits bad use-cases such as this:

return new FooClass (local_error_code_variable);

Don't try to signal constructor failures with return codes. You can't. Use exceptions.

You can wrap exceptions in return codes and vice-versa.

class NewAPIClass {
    NewAPIClass () {
        error_code err = old_api_function ();
        if (OLD_API_OK != err)
            throw NewAPIException (err);
    }
}

extern "C" error_code new_api_callback_function (argument arg) {
    try {
        NewAPIClass object;
        object .do_work ();
    }
    catch (...) {
        return OLD_API_ERROR;
    }
}

int main () {
    old_api_install_callback (new_api_callback_function);
}

Exceptions are important. There are lots of good GOTW articles and you should make understanding them a goal in being a C++ developer.

Write the new code to use exceptions properly. Convert error types whenever you hit a boundry between old and new code.

By the way, exceptions are the only reasonable way for a constructor to fail. It's all part of RAII which is a key to making C++ so powerful. Constructors establish your invariants and exceptions signal a failure to satisfy postconditions -- put it all together and here is the important philosophy: in C++ only valid objects should exist, if you get this right by exploiting RAII then the continued existence of an object is proof of the program's validity.


I cannot use exceptions at the current stage because there are many function calls to/from external code which use a "return error message" approach still.

Then fix that problem first. Then use exceptions.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜