构造函数应该如何报告错误?指向外部标志的指针?

发布于 2024-12-04 02:06:59 字数 1215 浏览 1 评论 0原文

我正在努力重构一些旧的 C 风格代码,使其更符合 C++ 代码。我对 C++ 还是有点陌生

​​ 我正在处理的代码示例如下

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

我正在考虑开发一个类,以便

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;
}

虽然我已经能够使用与此方法类似的方法,但我不知道这是否会是便携的。

这是正确的做法吗?

如果不是,我是否只使用指针来初始化类,而不是通过引用传递错误消息变量?或者还有什么我可以做的吗?

我在当前阶段无法使用异常,因为有许多对外部代码的函数调用仍然使用“返回错误消息”方法。

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

陈独秀 2024-12-11 02:06:59

暂且不说,如果可能的话,您应该修复调用代码,以便异常正常,您可以使用两阶段构造惯用法:

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;
        }
    }
};

现在,使用异常的代码可以调用多参数构造函数(并在准备就绪的情况下获取对象) to-use 状态),而异常厌恶代码可以调用默认构造函数,后跟 nothrow_init (并且接受这样的事实:如果 nothrow_init 失败,它们的对象上就会有一个不可用的对象手,这是他们的确保他们不使用它的责任)。

将来,当您将代码库的其他部分带入异常使用状态时,您会发现 init 中的代码调用本身可以抛出的东西。此时,您可以开始移动代码,以便 nothrow_init 调用可以抛出异常的 init 函数,但捕获任何异常并将其转换为错误代码。假设它是一个内部类,那么最终什么都不会使用 nothrow_init 并且您可以将其删除。

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.

甜心小果奶 2024-12-11 02:06:59

您的代码很危险,因为它允许出现如下不良用例:

return new FooClass (local_error_code_variable);

不要尝试使用返回代码来表示构造函数失败。 你不能。使用异常。

您可以将异常包装在返回代码中,反之亦然。

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);
}

例外很重要。有很多好的 GOTW 文章,您应该将理解它们作为成为 C++ 的目标开发商。

编写新代码以正确使用异常。每当遇到新旧代码之间的界限时,请转换错误类型。

顺便说一句,异常是构造函数失败的唯一合理方式。这些都是 RAII 的一部分,而 RAII 是使 C++ 如此强大的关键。构造函数建立不变量和异常,表明无法满足后置条件 - 将它们放在一起,这里是重要的哲学:在 C++ 中只有有效对象应该存在,如果你得到通过利用 RAII 的这种权利,那么对象的持续存在就是程序有效性的证明。

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.

吻泪 2024-12-11 02:06:59

现阶段我还不能使用异常,因为有很多异常
使用“返回错误”的外部代码的函数调用
消息”方法仍然如此。

然后先解决该问题。然后使用异常。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文