# How to Use absl::Cleanup for RAII-Style Scope Exit Callbacks in Abseil C++

> Master absl::Cleanup for RAII-style scope exit callbacks in Abseil C++. Ensure deterministic cleanup without heap allocation. Learn this powerful technique today.

- Repository: [Abseil/abseil-cpp](https://github.com/abseil/abseil-cpp)
- Tags: how-to-guide
- Published: 2026-07-14

---

**`absl::Cleanup`** implements the scope guard idiom by storing a callable that automatically executes when the object goes out of scope, providing deterministic cleanup without heap allocation.

The `absl::Cleanup` class in the [abseil/abseil-cpp](https://github.com/abseil/abseil-cpp) repository provides a lightweight, zero-overhead mechanism for ensuring resources are released when execution leaves a scope. Unlike `std::unique_ptr` with custom deleters, `absl::Cleanup` accepts any callable—lambdas, functors, or function pointers—making it ideal for complex teardown logic that doesn't fit a simple pointer model. This utility is header-only and lives in the `absl/cleanup/` module.

## Creating a Cleanup Object

The public API resides in [[`absl/cleanup/cleanup.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/cleanup/cleanup.h)](https://github.com/abseil/abseil-cpp/blob/master/absl/cleanup/cleanup.h). You can instantiate `absl::Cleanup` using either C++17 class template argument deduction (CTAD) or the `absl::MakeCleanup` factory function for C++11 compatibility.

### C++17 Class Template Argument Deduction

With C++17, the compiler automatically deduces the callback type, enabling concise syntax:

```cpp
#include "absl/cleanup/cleanup.h"

void ProcessFile(const char* path) {
  FILE* file = fopen(path, "r");
  if (!file) return;
  
  absl::Cleanup file_closer = [file] { fclose(file); };
  
  // Process data...
  // fclose called automatically when file_closer destructs
}

```

### C++11 absl::MakeCleanup Factory

For pre-C++17 compilers, use the `absl::MakeCleanup` helper function to avoid explicit template arguments:

```cpp
auto file_closer = absl::MakeCleanup([file] { fclose(file); });

```

Both approaches create a move-only object that holds your callable in internal storage.

## How absl::Cleanup Works Under the Hood

The implementation leverages placement-new and manual lifecycle management to avoid heap allocation while supporting callables of arbitrary size.

### Internal Storage Architecture

The internal implementation in [[`absl/cleanup/internal/cleanup.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/cleanup/internal/cleanup.h)](https://github.com/abseil/abseil-cpp/blob/master/absl/cleanup/internal/cleanup.h) uses `cleanup_internal::Storage<Callback>` to manage the callback lifetime. This storage class:

- Allocates space using a raw buffer (`callback_buffer_`) sized for the callable type
- Constructs the callback via **placement-new** to avoid dynamic allocation
- Tracks engagement state via `is_callback_engaged_`

When you create a `Cleanup` object, the callable is placement-new'd into the stack-allocated buffer within the `Storage` object.

### Engagement Tracking and Execution Guarantees

The destructor of `Cleanup` checks `IsCallbackEngaged()`. If engaged, it invokes `InvokeCallback()` followed by `DestroyCallback()`. This ensures:

- **Exactly-once execution**: The callback runs either on scope exit, explicit `Invoke()`, or not at all if `Cancel()` is called
- **No heap allocation**: The storage lives within the `Cleanup` object itself
- **Signal-safety**: No locks or dynamic allocation occurs during destruction (assuming your callback is also signal-safe)

## Controlling Cleanup Execution

While automatic execution on scope exit is the default behavior, `absl::Cleanup` provides explicit control for unusual control flows.

### Early Invocation with Invoke()

Call `Invoke()` to execute the callback immediately and disengage the guard:

```cpp
absl::Cleanup guard = [] { Log("Cleanup executed"); };

// Do work...

std::move(guard).Invoke();  // Calls Log now; destructor becomes no-op

```

Note that `Invoke()` requires moving from the cleanup object because `Cleanup` is move-only (non-copyable).

### Canceling Execution with Cancel()

Prevent the callback from running by calling `Cancel()`:

```cpp
absl::Cleanup guard = [] { ReleaseResource(); };

if (resource_already_released) {
  std::move(guard).Cancel();  // Disengages the callback
}
// No action taken when guard destructs

```

Both `Invoke()` and `Cancel()` move-from the object to enforce single ownership semantics.

## Practical Examples

### Resource Management with Early Returns

The canonical use case handles multiple resources with complex exit paths:

```cpp
absl::Status CopyGoodData(const char* source_path, const char* sink_path) {
  FILE* source_file = fopen(source_path, "r");
  if (!source_file) return absl::NotFoundError("No source file");

  absl::Cleanup source_closer = [source_file] { fclose(source_file); };

  FILE* sink_file = fopen(sink_path, "w");
  if (!sink_file) return absl::NotFoundError("No sink file");

  auto sink_closer = absl::MakeCleanup([sink_file] { fclose(sink_file); });

  while (ReadData(source_file, &data)) {
    if (!data.IsGood()) {
      return absl::FailedPreconditionError("Read bad data");  // Both cleanups run
    }
    SaveData(sink_file, &data);
  }

  return absl::OkStatus();  // Both cleanups run
}

```

### Non-Copyable Callables

`absl::Cleanup` supports move-only functors, making it compatible with unique resources:

```cpp
struct NonCopyable {
  NonCopyable() = default;
  NonCopyable(const NonCopyable&) = delete;
  NonCopyable(NonCopyable&&) = default;
  void operator()() const { /* cleanup code */ }
};

absl::Cleanup cleanup = NonCopyable{};  // Works fine

```

## Summary

- **`absl::Cleanup`** provides zero-overhead RAII scope guards for arbitrary teardown logic.
- **Implementation** uses placement-new in [[`absl/cleanup/internal/cleanup.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/cleanup/internal/cleanup.h)](https://github.com/abseil/abseil-cpp/blob/master/absl/cleanup/internal/cleanup.h) to store callables without heap allocation.
- **Creation** uses C++17 CTAD or `absl::MakeCleanup` for C++11 compatibility via the public header [[`absl/cleanup/cleanup.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/cleanup/cleanup.h)](https://github.com/abseil/abseil-cpp/blob/master/absl/cleanup/cleanup.h).
- **Control** explicitly via `std::move(obj).Invoke()` for early execution or `std::move(obj).Cancel()` to suppress execution.
- **Semantics** are move-only, ensuring unique ownership of the cleanup action.

## Frequently Asked Questions

### Is absl::Cleanup thread-safe?

**`absl::Cleanup` is not thread-safe.** The class provides no internal synchronization. If you need to share cleanup logic across threads, you must provide your own locking or ensure the `Cleanup` object is accessed by only one thread. However, the class is signal-safe because it contains no locks and only performs placement-new and destruction operations.

### What is the difference between absl::Cleanup and std::unique_ptr with a custom deleter?

**`std::unique_ptr`** manages a single pointer resource, while **`absl::Cleanup`** manages arbitrary callable logic. Use `absl::Cleanup` when you need to execute complex teardown code—such as logging, state restoration, or multiple resource releases—that doesn't fit the pointer ownership model. Additionally, `absl::Cleanup` provides explicit `Cancel()` and `Invoke()` methods for manual control.

### Can I use absl::Cleanup in C++11 code?

**Yes.** While C++17 allows direct initialization via class template argument deduction, C++11 users can create cleanup objects using the **`absl::MakeCleanup`** factory function. This function deduces the type automatically and returns a properly instantiated `Cleanup` object compatible with older standards.

### Does absl::Cleanup allocate memory on the heap?

**No.** The implementation stores the callable in a stack-allocated buffer using **placement-new**. The `cleanup_internal::Storage` class in [`absl/cleanup/internal/cleanup.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/cleanup/internal/cleanup.h) manages a raw byte buffer sized for the specific callable type, ensuring zero heap allocation overhead and predictable performance.