# ASIO Executor Customization with `require` and `prefer` Concepts: A Complete Guide

> Master ASIO executor customization using require and prefer concepts. Learn how to modify executors with properties through compile-time resolution and graceful fallbacks for robust asynchronous operations.

- Repository: [chriskohlhoff/asio](https://github.com/chriskohlhoff/asio)
- Tags: deep-dive
- Published: 2026-07-11

---

**ASIO's `require` and `prefer` customization-point objects let you modify executors by applying properties through a compile-time resolution hierarchy that falls back gracefully when properties cannot be satisfied.**

The `chriskohlhoff/asio` repository implements a sophisticated executor customization model using C++ customization-point objects (CPOs). These mechanisms enable generic code to request executor modifications without knowing the concrete executor type, leveraging compile-time traits to select the optimal implementation strategy.

## How `require` and `prefer` Work

ASIO provides two primary customization points for executor modification. Both are defined as CPOs that use template metaprogramming to resolve the best available implementation at compile time.

### The `require` CPO

**`asio::require`** enforces properties that **must** be satisfied. When invoked in [`include/asio/require.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/require.hpp), it attempts to transform an executor by applying a specific property. If the property cannot be satisfied, the expression is ill-formed and produces a compile-time error.

The implementation uses `call_traits` structs to build a resolution hierarchy. First, it checks if the property satisfies `static_require` via a static query. If valid, it returns the original executor unchanged (the identity overload). Otherwise, it attempts to detect a member `require` function, then falls back to free functions found via ADL.

### The `prefer` CPO

**`asio::prefer`** attempts to apply properties but allows graceful degradation. Defined in [`include/asio/prefer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/prefer.hpp), it mirrors the `require` logic but adds a critical fallback: when a property is not directly preferable, it attempts the same mechanisms as `require` before finally invoking the identity overload.

This means `prefer` never produces a hard error. If the property cannot be applied through any mechanism, it simply returns the original executor unchanged, enabling code to request optimizations without requiring their presence.

## The Compile-Time Resolution Hierarchy

Both CPOs follow a strict concept-preserving resolution order encoded in their respective `call_traits` implementations:

1. **Static query check** – Uses `static_require` from [`include/asio/traits/static_require.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/traits/static_require.hpp) to determine if the property is a static property. If `static_query_v` is valid, the CPO returns the identity result.
2. **Member function detection** – Uses `require_member` or `prefer_member` traits (defined in [`include/asio/traits/require_member.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/traits/require_member.hpp) and [`include/asio/traits/prefer_member.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/traits/prefer_member.hpp)) to detect if the type exposes a matching member function.
3. **Free function discovery** – Uses `require_free` or `prefer_free` traits (from [`include/asio/traits/require_free.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/traits/require_free.hpp) and [`include/asio/traits/prefer_free.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/traits/prefer_free.hpp)) to find free functions via ADL.
4. **Recursive chaining** – When multiple properties are supplied, the CPO recursively applies each property to the result of the previous call using `two_props` and `n_props` overloads.
5. **Ill-formed fallback** – For `require`, if no valid overload exists, the expression is ill-formed. For `prefer`, the identity overload is always available as a final fallback.

## Core Traits and Type Support

The customization mechanism relies on traits defined in the `include/asio/traits/` directory and [`include/asio/is_applicable_property.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/is_applicable_property.hpp).

### Applicability and Static Requirements

**`is_applicable_property<T, P>`** determines if property `P` can be queried on type `T`. This is the first gate checked before attempting any customization.

**`static_require<T, P>`** checks whether a static query is valid for the property. When true, the CPO bypasses all runtime mechanisms and returns the executor unchanged, providing zero-overhead property application for static metadata.

### Member and Free Function Detection

The traits system uses SFINAE to detect implementation strategies:

- **`require_member`** and **`prefer_member`** detect member functions with the signature `require(Prop)` or `prefer(Prop)`.
- **`require_free`** and **`prefer_free`** detect free functions via ADL with signatures `require(E, P)` or `prefer(E, P)`.

### Result and Noexcept Traits

For generic programming, ASIO provides result type and exception specification traits:

- **`can_require<T, Props...>`** and **`can_prefer<T, Props...>`** – `true` if the expression is well-formed.
- **`is_nothrow_require<T, Props...>`** and **`is_nothrow_prefer<T, Props...>`** – `true` if the selected overload is `noexcept`.
- **`require_result<T, Props...>::type`** and **`prefer_result<T, Props...>::type`** – expose the resulting type after applying properties.

These traits enable `static_assert` validations and `noexcept` specifications in generic executor wrappers.

## Practical Examples of ASIO Executor Customization

### Basic `require` with Static Properties

When a property is static, `require` returns the original executor without modification:

```cpp
#include <asio.hpp>
#include <type_traits>

using namespace asio;

int main() {
    auto ex = make_strand(io_context{});
    auto ex_req = require(ex, execution::blocking.never);
    
    // Static property: no change in type
    static_assert(std::is_same_v<decltype(ex_req), decltype(ex)>);
}

```

This invokes the identity overload because `execution::blocking.never` satisfies `static_require` in [`include/asio/traits/static_require.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/traits/static_require.hpp).

### Member Function Fallback

Executors can implement member functions to handle property requests:

```cpp
struct my_executor {
    using execution_context = void;
    
    template <typename Prop>
    my_executor require(Prop) const noexcept { 
        return *this; 
    }
};

int main() {
    my_executor exec;
    auto exec2 = require(exec, execution::outstanding_work.tracked);
    // Calls require_member overload via asio::require CPO
}

```

The `require` CPO detects the member function using `require_member` traits and forwards the call accordingly.

### Graceful Degradation with `prefer`

Use `prefer` when a property is optional:

```cpp
auto ex = make_strand(io_context{});
auto ex_preferred = prefer(ex, execution::outstanding_work.untracked);

```

If `untracked` is not marked as `is_preferable` and no `require` mechanism exists, the CPO returns `ex` unchanged rather than failing compilation.

### Chaining Multiple Properties

Both CPOs support variadic property application:

```cpp
auto ex = make_strand(io_context{});
auto ex_mod = prefer(
    require(ex, execution::blocking.always),
    execution::outstanding_work.tracked,
    execution::relationship.fork);

```

The implementation recursively applies each property, using the result of the previous transformation as input to the next.

### Compile-Time Verification

Validate executor capabilities before use:

```cpp
static_assert(can_require_v<decltype(ex), execution::blocking.never>);
static_assert(!can_prefer_v<decltype(ex), execution::blocking.never>);
static_assert(is_nothrow_prefer_v<decltype(ex), execution::outstanding_work.tracked>);

```

These assertions use the traits defined in [`require.hpp`](https://github.com/chriskohlhoff/asio/blob/main/require.hpp) and [`prefer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/prefer.hpp) to verify interface compliance.

## Summary

- **`asio::require`** enforces mandatory properties through a hierarchy of static queries, member functions, and free functions, failing compilation if the property cannot be satisfied.
- **`asio::prefer`** attempts property application with graceful fallback to the original executor, never causing compile-time errors.
- The resolution order is implemented in [`include/asio/require.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/require.hpp) and [`include/asio/prefer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/prefer.hpp) using `call_traits` that prioritize static requirements, then member functions, then ADL-discovered free functions.
- Support traits in `include/asio/traits/` provide the SFINAE-based detection mechanisms for `require_member`, `require_free`, `prefer_member`, and `prefer_free`.
- Result type traits (`require_result`, `prefer_result`) and capability checks (`can_require`, `can_prefer`) enable robust generic programming against ASIO executors.

## Frequently Asked Questions

### What is the difference between `asio::require` and `asio::prefer`?

`asio::require` demands that a property be satisfied and produces a compile-time error if it cannot be applied, making it suitable for essential executor characteristics. `asio::prefer` attempts to apply a property but falls back to returning the original executor unchanged if the property is unavailable, making it ideal for optional optimizations that should not break compilation when unsupported.

### How does ASIO detect which `require` overload to use?

The `asio::require` CPO in [`include/asio/require.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/require.hpp) uses a series of `call_traits` to test conditions in order: first checking `static_require` for static properties, then using `require_member` traits to detect member functions, then `require_free` traits to find free functions via ADL, and finally supporting variadic chaining. This compile-time dispatch ensures the most efficient implementation is selected without runtime overhead.

### What happens when a property cannot be applied to an executor?

When using `require`, the expression is ill-formed and compilation fails, enforcing that the executor must support the requested property. When using `prefer`, if no static query, member function, or free function can satisfy the property, the CPO invokes the identity overload and returns the original executor unchanged, effectively ignoring the request.

### Where are the customization point implementations defined?

The primary CPOs are defined in [`include/asio/require.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/require.hpp) and [`include/asio/prefer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/prefer.hpp). The supporting traits for detecting member and free functions reside in [`include/asio/traits/require_member.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/traits/require_member.hpp), [`include/asio/traits/require_free.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/traits/require_free.hpp), [`include/asio/traits/prefer_member.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/traits/prefer_member.hpp), and [`include/asio/traits/prefer_free.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/traits/prefer_free.hpp). Static property support is implemented in [`include/asio/traits/static_require.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/traits/static_require.hpp), and general applicability checks are in [`include/asio/is_applicable_property.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/is_applicable_property.hpp).