清理代码的正确方法是什么?

时间:2021-11-07 10:49:33

When you have several lines of codes that initiate other objects, is there a cleaner way to clean up the objects than what is shown below? I initiate several objects in one function and check to see if they fail- but I have a bunch of redundant code that I have to keep typing. Is what shown below the correct method of doing this? Or are there cleaner ways? I am aware of the do { } while(false) method and the goto method- but they are not clean and feel messy.

当你有多行代码启动其他对象时,是否有一种更清洁的方法来清理对象而不是下面显示的内容?我在一个函数中启动了几个对象并检查它们是否失败 - 但我有一堆冗余代码,我必须继续输入。下面显示的是正确的方法吗?还是有更干净的方式?我知道do {} while(false)方法和goto方法 - 但它们不干净并且感觉很乱。

    if( bind(s, (sockaddr *)&saddr, sizeof(sockaddr)) == SOCKET_ERROR ) {
        printf("bind() failed.\n");
        closesocket(s);
        CloseHandle(g_hIOCompletionPort);
        CloseHandle(g_hShutdownEvent);
        WSACleanup();
    }

    if( listen(s, 60) == SOCKET_ERROR ) {
        printf("listen() failed.\n");
        closesocket(s);
        CloseHandle(g_hIOCompletionPort);
        CloseHandle(g_hShutdownEvent);
        WSACleanup();
    }

    g_hAcceptEvent = WSACreateEvent();
    if( g_hAcceptEvent == WSA_INVALID_EVENT ) {
        printf("WSACreateEvent() failed.\n");
        closesocket(s);
        CloseHandle(g_hIOCompletionPort);
        CloseHandle(g_hShutdownEvent);
        WSACleanup();
    }

3 个解决方案

#1


3  

Most often, RAII would be used in C++, in which the code is organized with objects that have constructors and destructors, and the cleanup is performed in the destructor. Thus, destruction of the object is sufficient, which limits the amount of replicated code.

大多数情况下,RAII将在C ++中使用,其中代码使用具有构造函数和析构函数的对象进行组织,并且在析构函数中执行清理。因此,对象的破坏就足够了,这限制了复制代码的数量。

class Server {
    SOCKET s;
    HANDLE iocp;
    HANDLE shutdown;
    std::string err_str;
public:
    ~Server () {
        if (!err_str.epmty()) std::cerr << err_str << '\n';
        closesocket(s);
        CloseHandle(iocp);
        CloseHandle(shutdown);
        WSACleanup();
    }
    //...
};

In addition to RAII, C++ also provides exception handling, which could also be made to work in your case. The try block would have the socket code. When the socket code could throw an exception on error, and the catch block could take care of the clean up.

除了RAII之外,C ++还提供了异常处理,也可以在您的情况下使用。 try块将具有套接字代码。当套接字代码可能在出错时抛出异常,并且catch块可以处理清理。

try {
    if (bind(...) == SOCKET_ERROR) {
        throw ...something...;
    }
    if (listen(...) == SOCKET_ERROR) {
        throw ...something...;
    }
    ...
}
catch (...something...) {
    ...cleanup code;
}

In C, there is no RAII equivalent. Nor are there exceptions. However, exception handling can be emulated with setjmp() and longjmp(), as is done with cexcept. Although there is no direct support for RAII, there is nothing preventing you from organizing your C code into objects with associated clean up functions.

在C中,没有RAII等价物。也没有例外。但是,可以使用setjmp()和longjmp()模拟异常处理,就像使用cexcept一样。虽然没有直接支持RAII,但没有什么可以阻止您将C代码组织到具有相关清理功能的对象中。

struct Server {
    SOCKET s;
    HANDLE iocp;
    HANDLE shutdown;
    const char *err_str;
};

void destroy_server (Server *server) {
    /* ... */
}

If you have a lot of code that follows a sequence, but any of them might need to do the cleanup, you can organize the code in a state machine like fashion.

如果你有许多代码遵循一个序列,但是他们中的任何一个可能需要进行清理,你可以像时尚一样在状态机中组织代码。

enum { STATE_INIT, STATE_SUCCESS, STATE_ERROR, STATE_STOP } state = STATE_INIT;

while (state != STATE_STOP) {
    switch (state) {
    case STATE_INIT:    state = do_server_init(); break;
    case STATE_SUCCESS: state = do_server(); break;
    case STATE_ERROR:   state = do_server_cleanup(); break;
    case STATE_STOP:    break;
    default:            fprintf(stderr, "unexpected state: %d\n", state);
                        state = STATE_ERROR;
    }
}

#2


3  

If you're using C++ then RAII (Resource Acquisition Is Initialization) is the preferred way. Basically you acquire or attach a resource to the class (typically during construction) and free it up during destruction.

如果您正在使用C ++,那么RAII(资源获取是初始化)是首选方式。基本上,您获取或附加资源(通常在施工期间)并在销毁期间将其释放。

For example, in your code you'd have some sort of Handle class which held onto the g_hIOCompletionPort handle and called CloseHandle in its destructor.

例如,在你的代码中,你有一些Handle类,它保存在g_hIOCompletionPort句柄中,并在其析构函数中调用CloseHandle。

#3


0  

In C++ RAII is a very common pattern for such a case.

在C ++中,RAII是这种情况的一种非常常见的模式。

For plain C: if I write goto I will be killed, so I don't. But look for example here: Valid use of goto for error management in C?

对于普通C:如果我写goto我将被杀死,所以我没有。但是请看这里的例子:在C中有效使用goto进行错误管理?

#1


3  

Most often, RAII would be used in C++, in which the code is organized with objects that have constructors and destructors, and the cleanup is performed in the destructor. Thus, destruction of the object is sufficient, which limits the amount of replicated code.

大多数情况下,RAII将在C ++中使用,其中代码使用具有构造函数和析构函数的对象进行组织,并且在析构函数中执行清理。因此,对象的破坏就足够了,这限制了复制代码的数量。

class Server {
    SOCKET s;
    HANDLE iocp;
    HANDLE shutdown;
    std::string err_str;
public:
    ~Server () {
        if (!err_str.epmty()) std::cerr << err_str << '\n';
        closesocket(s);
        CloseHandle(iocp);
        CloseHandle(shutdown);
        WSACleanup();
    }
    //...
};

In addition to RAII, C++ also provides exception handling, which could also be made to work in your case. The try block would have the socket code. When the socket code could throw an exception on error, and the catch block could take care of the clean up.

除了RAII之外,C ++还提供了异常处理,也可以在您的情况下使用。 try块将具有套接字代码。当套接字代码可能在出错时抛出异常,并且catch块可以处理清理。

try {
    if (bind(...) == SOCKET_ERROR) {
        throw ...something...;
    }
    if (listen(...) == SOCKET_ERROR) {
        throw ...something...;
    }
    ...
}
catch (...something...) {
    ...cleanup code;
}

In C, there is no RAII equivalent. Nor are there exceptions. However, exception handling can be emulated with setjmp() and longjmp(), as is done with cexcept. Although there is no direct support for RAII, there is nothing preventing you from organizing your C code into objects with associated clean up functions.

在C中,没有RAII等价物。也没有例外。但是,可以使用setjmp()和longjmp()模拟异常处理,就像使用cexcept一样。虽然没有直接支持RAII,但没有什么可以阻止您将C代码组织到具有相关清理功能的对象中。

struct Server {
    SOCKET s;
    HANDLE iocp;
    HANDLE shutdown;
    const char *err_str;
};

void destroy_server (Server *server) {
    /* ... */
}

If you have a lot of code that follows a sequence, but any of them might need to do the cleanup, you can organize the code in a state machine like fashion.

如果你有许多代码遵循一个序列,但是他们中的任何一个可能需要进行清理,你可以像时尚一样在状态机中组织代码。

enum { STATE_INIT, STATE_SUCCESS, STATE_ERROR, STATE_STOP } state = STATE_INIT;

while (state != STATE_STOP) {
    switch (state) {
    case STATE_INIT:    state = do_server_init(); break;
    case STATE_SUCCESS: state = do_server(); break;
    case STATE_ERROR:   state = do_server_cleanup(); break;
    case STATE_STOP:    break;
    default:            fprintf(stderr, "unexpected state: %d\n", state);
                        state = STATE_ERROR;
    }
}

#2


3  

If you're using C++ then RAII (Resource Acquisition Is Initialization) is the preferred way. Basically you acquire or attach a resource to the class (typically during construction) and free it up during destruction.

如果您正在使用C ++,那么RAII(资源获取是初始化)是首选方式。基本上,您获取或附加资源(通常在施工期间)并在销毁期间将其释放。

For example, in your code you'd have some sort of Handle class which held onto the g_hIOCompletionPort handle and called CloseHandle in its destructor.

例如,在你的代码中,你有一些Handle类,它保存在g_hIOCompletionPort句柄中,并在其析构函数中调用CloseHandle。

#3


0  

In C++ RAII is a very common pattern for such a case.

在C ++中,RAII是这种情况的一种非常常见的模式。

For plain C: if I write goto I will be killed, so I don't. But look for example here: Valid use of goto for error management in C?

对于普通C:如果我写goto我将被杀死,所以我没有。但是请看这里的例子:在C中有效使用goto进行错误管理?