函数作用域末尾执行代码的简便方法及测试全局变量恢复问题咨询
Hey there! Let's break down your two C++ questions one by one—both are about reliable scope-based execution/cleanup, which is super critical especially in testing scenarios.
1. 简便地在函数作用域末尾执行代码
The gold standard for this in C++ is RAII (Resource Acquisition Is Initialization). The core idea is to tie the code you want to run at scope end to an object's destructor, since destructors are guaranteed to run no matter how the scope exits (normal return, early return, or exception). Here are two practical approaches:
Option 1: Custom RAII Scope Guard (works with all C++ versions)
You can roll your own tiny struct to wrap any cleanup/end-of-scope logic:
#include <functional> struct ScopeGuard { std::function<void()> cleanupFunc; explicit ScopeGuard(std::function<void()> func) : cleanupFunc(std::move(func)) {} // Destructor runs automatically when the guard goes out of scope ~ScopeGuard() { cleanupFunc(); } // Prevent copying to avoid duplicate execution ScopeGuard(const ScopeGuard&) = delete; ScopeGuard& operator=(const ScopeGuard&) = delete; };
Usage example:
void processData() { // Initialize resources or setup here ScopeGuard guard([](){ // This code runs at the end of the scope, guaranteed std::cout << "Cleaning up resources..." << std::endl; // e.g., close files, release locks, reset state }); // Your main logic here if (someErrorCondition) { return; // Guard's destructor still runs! } throw std::runtime_error("Unexpected issue"); // Destructor runs too! }
Option 2: C++20 std::scope_exit (native standard library support)
If you're using C++20 or later, skip the custom struct and use the standard library's built-in solution:
#include <scope> // Required header void processData() { auto guard = std::scope_exit([](){ std::cout << "Executed at scope end!" << std::endl; }); // Same behavior as the custom guard—works with all exit paths }
2. Automatically Restore Global Variables in Tests (Fix Early Exit/Exception Issues)
Your current manual restore approach fails because if the test exits early (via return or exception), the final setCsrfToken call never runs. RAII is again the perfect fix here—we'll wrap the save/restore logic into a struct that handles it automatically.
Solution: RAII Wrapper for Global State
Let's create a specialized guard for your CSRF token scenario first:
struct CsrfTokenRestorer { std::string originalToken; CsrfTokenRestorer() { // Save the current token when the guard is created originalToken = Http::getCsrfToken(); } ~CsrfTokenRestorer() { // Restore the original token when the guard goes out of scope Http::setCsrfToken(originalToken); } // Prevent copying to avoid conflicting restores CsrfTokenRestorer(const CsrfTokenRestorer&) = delete; CsrfTokenRestorer& operator=(const CsrfTokenRestorer&) = delete; };
Now update your test case to use this guard:
BOOST_AUTO_TEST_CASE(HttpClientCsrf) { CsrfTokenRestorer tokenGuard; // Automatically saves the current CSRF token // Modify the token for testing Http::setCsrfToken("test-csrf-123"); // Your test logic here if (someTestFailure) { BOOST_TEST_FAIL("Invalid CSRF response"); // Exception thrown—restore still happens return; // Early return—restore still happens } // No need for manual restore code anymore! }
Generic Version (Works for Any Global Variable)
If you need this for multiple global variables, a template-based guard is even more flexible:
template<typename T> struct GlobalStateRestorer { T& globalVar; T originalValue; explicit GlobalStateRestorer(T& var) : globalVar(var), originalValue(var) {} ~GlobalStateRestorer() { globalVar = originalValue; } GlobalStateRestorer(const GlobalStateRestorer&) = delete; GlobalStateRestorer& operator=(const GlobalStateRestorer&) = delete; };
Usage for your CSRF token (assuming Http::csrfToken is a directly accessible global variable):
BOOST_AUTO_TEST_CASE(HttpClientCsrf) { GlobalStateRestorer<std::string> tokenGuard(Http::csrfToken); Http::csrfToken = "test-csrf-123"; // Test logic... // Automatic restore on scope exit }
Why This Works
C++ guarantees that an object's destructor will execute when it goes out of scope, regardless of how the scope is exited. This eliminates the risk of forgetting to restore state or missing the restore step due to early exits/exceptions.
内容的提问来源于stack exchange,提问作者Pavel P

