需求
解决
利用 RAII 特性,封装个 ScopeGuard!或者直接用 Boost.ScopeExit。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #pragma once
#include <functional>
namespace umu { class ScopeGuard { public: explicit ScopeGuard(std::function<void()> on_exit_scope) : on_exit_scope_(on_exit_scope), dismissed_(false) {}
~ScopeGuard() noexcept { if (!dismissed_) { on_exit_scope_(); } }
void Dismiss() { dismissed_ = true; }
private: std::function<void()> on_exit_scope_; bool dismissed_;
ScopeGuard(ScopeGuard const&) = delete; ScopeGuard& operator=(ScopeGuard const&) = delete; }; }
#define SCOPEGUARD_LINENAME_CAT(name, line) name##line #define SCOPEGUARD_LINENAME(name, line) SCOPEGUARD_LINENAME_CAT(name, line) #define ON_SCOPE_EXIT(callback) \ umu::ScopeGuard SCOPEGUARD_LINENAME(EXIT, __LINE__)(callback)
|
范例
Windows 上使用 socket 必须先调用 WSAStartup 初始化 WinSock 环境。
常规封装版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| #include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
class WinSock { public: WinSock() : error_code_(WSAEFAULT) {}
int Initialize(WORD version_requested = WINSOCK_VERSION) { assert(WSAEFAULT == error_code_); return error_code_ = ::WSAStartup(version_requested, &wsa_data_); }
bool GetWsaData(WSADATA& wsa_data) { if (NO_ERROR == error_code_) { wsa_data = wsa_data_; return true; } return false; }
int GetErrorCode() { return error_code_; }
~WinSock() { if (NO_ERROR == error_code_) { ::WSACleanup(); } }
private: int error_code_; WSADATA wsa_data_; }
int main(int argc, char* argv[]) { WinSock winsock; int error_code = winsock.Initialize(); if (NO_ERROR != error_code) { std::cerr << "Initialize() failed with " << error_code << '\n'; return error_code; }
SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (INVALID_SOCKET != s) { closesocket(s); std::cout << "OK\n"; } else { error_code = WSAGetLastError(); if (WSANOTINITIALISED == error_code) { std::cerr << "WSANOTINITIALISED\n"; } else { std::cerr << "socket() failed with " << error_code << '\n'; } }
return error_code; }
|
Boost.ScopeExit 版
现在是 2020 年 9 月,建议使用 cpp17,所以抛弃 BOOST_SCOPE_EXIT + BOOST_SCOPE_EXIT_END,使用 cpp11 的 BOOST_SCOPE_EXIT_ALL。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <winsock2.h>
#include <boost/scope_exit.hpp>
#pragma comment(lib, "ws2_32.lib")
int main(int argc, char* argv[]) { WSADATA wsa_data = {}; int error_code = ::WSAStartup(WINSOCK_VERSION, &wsa_data); if (NO_ERROR != error_code) { std::cerr << "WSAStartup() failed with " << error_code << '\n'; return error_code; } BOOST_SCOPE_EXIT_ALL(&) { ::WSACleanup(); };
SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (INVALID_SOCKET != s) { closesocket(s); std::cout << "OK\n"; } else { error_code = WSAGetLastError(); if (WSANOTINITIALISED == error_code) { std::cerr << "WSANOTINITIALISED\n"; } else { std::cerr << "socket() failed with " << error_code << '\n'; } }
return error_code; }
|
Dismiss 演示:
1 2 3 4 5
| boost::scope_exit::aux::guard<void> defer; defer = [] { std::cout << "On scope exit!\n"; };
defer = {};
|
参考
https://www.boost.org/doc/libs/1_74_0/libs/scope_exit/doc/html/BOOST_SCOPE_EXIT_ALL.html