A header-only C++17 library providing scope(exit)
, scope(success)
, and scope(failure)
macros for automatic resource cleanup using RAII principles. This library offers a zero-overhead alternative to BOOST_SCOPE_EXIT
without dynamic memory allocation.
- Header-only: No compilation or linking required
- C++17 standard: Modern C++ with perfect forwarding
- Zero overhead: No dynamic memory allocation
- Exception safe: Cleanup code executes during stack unwinding
- Multiple scope types:
scope(exit)
,scope(success)
, andscope(failure)
macros - Simple syntax: Intuitive macro-based API
- C++17 compatible compiler
- CMake 3.28+ (for building tests only)
- No runtime dependencies
#include <scope_exit/scope_exit.hpp>
#include <cstdio>
int main() {
FILE* f = fopen("example.txt", "r");
if (!f) return -1;
// Automatic cleanup on scope exit
scope(exit) {
fclose(f);
};
// Use file...
// File automatically closed when leaving scope
return 0;
}
#include <scope_exit/scope_exit.hpp>
void example() {
int* ptr = new int(42);
scope(exit) {
delete ptr;
};
// Use ptr...
// Memory automatically freed on scope exit
}
void multiple_guards() {
scope(exit) { std::cout << "Third\n"; }; // Executes 1st
scope(exit) { std::cout << "Second\n"; }; // Executes 2nd
scope(exit) { std::cout << "First\n"; }; // Executes 3rd
// Output: First, Second, Third
}
void exception_safe() {
bool cleanup_needed = true;
scope(exit) {
if (cleanup_needed) {
std::cout << "Cleaning up after exception\n";
}
};
throw std::runtime_error("Error!");
// Cleanup code still executes during stack unwinding
}
void transaction_example() {
bool committed = false;
scope(exit) {
if (!committed) {
rollback_transaction();
}
};
perform_operations();
commit_transaction();
committed = true;
// No rollback needed on successful path
}
#include <scope_exit/scope_exit.hpp>
void conditional_cleanup_example() {
scope(success) {
std::cout << "Operation completed successfully!\n";
cleanup_temporary_files();
};
scope(failure) {
std::cout << "Operation failed, performing error recovery\n";
log_error_details();
notify_administrators();
};
scope(exit) {
std::cout << "Always executed regardless of success/failure\n";
};
// Perform some operation that might throw
risky_operation();
}
void database_transaction() {
auto transaction = db.begin_transaction();
scope(success) {
transaction.commit();
std::cout << "Transaction committed successfully\n";
};
scope(failure) {
transaction.rollback();
std::cout << "Transaction rolled back due to error\n";
};
// Perform database operations
db.insert_record(record1);
db.update_record(record2);
db.delete_record(record3);
// If any operation throws, failure guard executes
// If all succeed, success guard executes
}
void process_file(const std::string& filename) {
std::ifstream file(filename);
if (!file) throw std::runtime_error("Cannot open file");
std::string backup_filename = filename + ".backup";
bool backup_created = false;
scope(success) {
// Remove backup file on successful processing
if (backup_created) {
std::remove(backup_filename.c_str());
}
std::cout << "File processed successfully\n";
};
scope(failure) {
// Restore from backup on failure
if (backup_created) {
std::rename(backup_filename.c_str(), filename.c_str());
std::cout << "Restored original file from backup\n";
}
};
// Create backup
std::ofstream backup(backup_filename);
backup << file.rdbuf();
backup_created = true;
// Process the file (might throw)
transform_file_content(filename);
}
void complex_operation() {
Resource* resource = acquire_resource();
bool operation_started = false;
// Always cleanup resource
scope(exit) {
release_resource(resource);
};
// Log success metrics
scope(success) {
increment_success_counter();
log_operation_success();
};
// Handle failures
scope(failure) {
increment_failure_counter();
log_operation_failure();
if (operation_started) {
perform_emergency_cleanup();
}
};
operation_started = true;
perform_critical_operation(resource);
// Success/failure guards execute based on whether an exception was thrown
}
Copy the single header file to your project:
#include "scope_exit.hpp" // Local copy
include(FetchContent)
FetchContent_Declare(
scope_exit
GIT_REPOSITORY https://github.com/alexeiz/scope-exit.git
GIT_TAG master # or specific version tag
)
FetchContent_MakeAvailable(scope_exit)
# Link to your target
target_link_libraries(your_target PRIVATE scope_exit::scope_exit)
git clone https://github.com/alexeiz/scope-exit.git
cd scope-exit
cmake --preset=release
cmake --build --preset=release
sudo cmake --install build
Then in your CMake project:
find_package(scope_exit REQUIRED)
target_link_libraries(your_target PRIVATE scope_exit::scope_exit)
# Configure (Debug by default)
cmake --preset=debug
# Or configure for release
cmake --preset=release
# Build
cmake --build --preset=debug
# Run all tests
ctest --preset=debug
# Or run specific test executable
./build/test/test_scope_exit
# One command: configure + build + test
cmake --workflow --preset=debug
scope(exit) { /* cleanup code */ };
- Purpose: Execute arbitrary code when leaving the current scope
- Execution: Always executes, even during exception unwinding
- Order: Multiple scope guards execute in LIFO (reverse declaration) order
- Capture: Lambda-style capture of surrounding variables by reference
scope(success) { /* success code */ };
- Purpose: Execute code only when scope exits normally (without exception)
- Execution: Only executes if no exception is thrown during scope lifetime
- Use cases: Commit transactions, cleanup temporary resources, success logging
- Order: Multiple scope guards execute in LIFO (reverse declaration) order
- Capture: Lambda-style capture of surrounding variables by reference
scope(failure) { /* failure code */ };
- Purpose: Execute code only when scope exits due to exception
- Execution: Only executes if an exception is thrown during scope lifetime
- Use cases: Rollback transactions, error logging, recovery operations
- Order: Multiple scope guards execute in LIFO (reverse declaration) order
- Capture: Lambda-style capture of surrounding variables by reference
This library is distributed under the Boost Software License 1.0.
Copyright Alexei Zakharov, 2025.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
Issues and pull requests are welcome on GitHub.