Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,64 @@ CurrentModule = FunctionWrappersWrappers

# FunctionWrappersWrappers

Documentation for [FunctionWrappersWrappers](https://github.com/chriselrod/FunctionWrappersWrappers.jl).
`FunctionWrappersWrappers` provides a callable, type-stable wrapper for a finite set of
function signatures. Calls outside that set can be rejected or delegated to the original
function with a configurable cache.

```@index
## Getting Started

Construct a wrapper by listing the argument tuple types and corresponding return types that
should use `FunctionWrappers.FunctionWrapper` dispatch:

```julia
using FunctionWrappersWrappers

wrapped_add = FunctionWrappersWrapper(
+,
(Tuple{Float64, Float64},),
(Float64,);
cache = SingleCache(),
policy = AllowNonIsBits(),
)

wrapped_add(1.0, 2.0) # 3.0 through the wrapped signature
wrapped_add(big(1), big(2)) # 3 as a cached fallback call
```

```@autodocs
Modules = [FunctionWrappersWrappers]
Use `Strict()` when unmatched calls must fail, `AllowAll()` when every unmatched call should
delegate to the original function, or `AllowNonIsBits()` to allow only non-isbits fallback
argument types. Use `unwrap`, `wrapped_signatures`, and `wrapped_return_types` to inspect a
wrapper without depending on its fields.

## API Reference

### Wrapper and Introspection

```@docs
FunctionWrappersWrapper
unwrap
wrapped_signatures
wrapped_return_types
```

### Fallback Configuration

```@docs
NoCache
SingleCache
DictCache
Strict
AllowAll
AllowNonIsBits
```

### Cache Storage

```@docs
NoCacheStorage
SingleCacheStorage
DictCacheStorage
```

```@index
```
46 changes: 26 additions & 20 deletions src/FunctionWrappersWrappers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export Strict, AllowAll, AllowNonIsBits
abstract type AbstractCacheMode end

"""
NoCache()
NoCache() -> NoCache

Disable fallback wrapper caching.

Expand All @@ -26,9 +26,10 @@ Fallback calls use dynamic dispatch through the original function each time.
struct NoCache <: AbstractCacheMode end

"""
SingleCache()
SingleCache() -> SingleCache

Cache one fallback `FunctionWrapper` for the most recent argument tuple type.
Cache one fallback `FunctionWrappers.FunctionWrapper` for the most recent argument tuple
type.

Repeated fallback calls with the same argument tuple type reuse the cached
wrapper. Calls with a different tuple type replace the cache entry.
Expand All @@ -39,7 +40,7 @@ wrapper. Calls with a different tuple type replace the cache entry.
struct SingleCache <: AbstractCacheMode end

"""
DictCache()
DictCache() -> DictCache

Cache fallback `FunctionWrapper`s in a dictionary keyed by argument tuple type.

Expand All @@ -57,7 +58,7 @@ struct DictCache <: AbstractCacheMode end
abstract type AbstractFallbackPolicy end

"""
Strict()
Strict() -> Strict

Disable fallback calls.

Expand All @@ -70,7 +71,7 @@ If no wrapped signature matches a call, `FunctionWrappersWrapper` throws a
struct Strict <: AbstractFallbackPolicy end

"""
AllowAll()
AllowAll() -> AllowAll

Always call the original function when no wrapped signature matches.

Expand All @@ -80,7 +81,7 @@ Always call the original function when no wrapped signature matches.
struct AllowAll <: AbstractFallbackPolicy end

"""
AllowNonIsBits()
AllowNonIsBits() -> AllowNonIsBits

Call the original function only when a mismatched argument tuple contains
non-isbits element types.
Expand All @@ -97,7 +98,7 @@ struct AllowNonIsBits <: AbstractFallbackPolicy end
# Cache storage types
# ============================================================================
"""
NoCacheStorage()
NoCacheStorage() -> NoCacheStorage

Storage marker for the `NoCache()` fallback mode.

Expand All @@ -122,7 +123,7 @@ storage = FunctionWrappersWrappers.NoCacheStorage()
struct NoCacheStorage end

"""
SingleCacheStorage()
SingleCacheStorage() -> SingleCacheStorage

Storage for the `SingleCache()` fallback mode.

Expand Down Expand Up @@ -154,7 +155,7 @@ mutable struct SingleCacheStorage
end

"""
DictCacheStorage()
DictCacheStorage() -> DictCacheStorage

Storage for the `DictCache()` fallback mode.

Expand Down Expand Up @@ -204,8 +205,8 @@ signature matches, fallback behavior is determined by policy `P` and cache
storage `CS`.

# Fields
- `fw`: Tuple of `FunctionWrappers.FunctionWrapper`s.
- `cache_storage`: Storage used by the fallback path.
- `fw::FW`: Tuple of `FunctionWrappers.FunctionWrapper`s, in matching order.
- `cache_storage::CS`: Storage used to cache fallback wrappers.

# Type Parameters
- `FW`: Tuple type of the wrapped `FunctionWrapper`s.
Expand All @@ -225,7 +226,7 @@ end
TruncatedStacktraces.@truncate_stacktrace FunctionWrappersWrapper

"""
FunctionWrappersWrapper{FW, P, CS}(f)
FunctionWrappersWrapper{FW, P, CS}(f) -> FunctionWrappersWrapper{FW, P, CS}

Create a `FunctionWrappersWrapper` with explicit type parameters.

Expand All @@ -247,7 +248,8 @@ function FunctionWrappersWrapper{FW, P, CS}(f) where {K, FW <: NTuple{K, Any}, P
end

"""
FunctionWrappersWrapper(f, argtypes, rettypes; cache=SingleCache(), policy=AllowNonIsBits())
FunctionWrappersWrapper(f, argtypes, rettypes;
cache = SingleCache(), policy = AllowNonIsBits()) -> FunctionWrappersWrapper

Create a callable wrapper for `f` over the given argument and return types.

Expand All @@ -256,9 +258,13 @@ Create a callable wrapper for `f` over the given argument and return types.
- `argtypes`: Tuple of argument tuple types, such as `(Tuple{Float64, Float64},)`.
- `rettypes`: Tuple of return types corresponding to `argtypes`, such as `(Float64,)`.

# Keyword Arguments
- `cache`: Fallback cache mode. Defaults to `SingleCache()`.
- `policy`: Fallback policy. Defaults to `AllowNonIsBits()`.
# Keywords
- `cache::AbstractCacheMode = SingleCache()`: Fallback cache mode. `NoCache()` performs
no caching, `SingleCache()` caches the most recent fallback signature, and `DictCache()`
caches each fallback signature.
- `policy::AbstractFallbackPolicy = AllowNonIsBits()`: Fallback policy. `Strict()` throws
for every unmatched signature, `AllowAll()` always calls `f`, and `AllowNonIsBits()` only
calls `f` when an unmatched argument type is non-isbits.

# Returns
- `FunctionWrappersWrapper`: A callable wrapper around `f`.
Expand Down Expand Up @@ -402,7 +408,7 @@ end
# ============================================================================

"""
unwrap(fww::FunctionWrappersWrapper)
unwrap(fww::FunctionWrappersWrapper) -> Function

Return the original function that was wrapped.

Expand All @@ -415,7 +421,7 @@ Return the original function that was wrapped.
unwrap(fww::FunctionWrappersWrapper) = first(fww.fw).obj[]

"""
wrapped_signatures(fww::FunctionWrappersWrapper)
wrapped_signatures(fww::FunctionWrappersWrapper) -> Tuple

Return a tuple of the argument type signatures that the wrapper can dispatch on.

Expand All @@ -430,7 +436,7 @@ function wrapped_signatures(fww::FunctionWrappersWrapper)
end

"""
wrapped_return_types(fww::FunctionWrappersWrapper)
wrapped_return_types(fww::FunctionWrappersWrapper) -> Tuple

Return a tuple of the return types for each wrapped function signature.

Expand Down
30 changes: 30 additions & 0 deletions test/basictests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,36 @@ end
end
end

@testset "Public fallback configuration interface" begin
modes = (NoCache(), SingleCache(), DictCache())

for cache in modes
@testset "$(typeof(cache))" begin
strict = FunctionWrappersWrapper(
identity, (Tuple{Float64},), (Float64,); cache, policy = Strict()
)
@test strict(1.0) === 1.0
@test_throws FunctionWrappersWrappers.NoFunctionWrapperFoundError strict(1.0f0)

allow_all = FunctionWrappersWrapper(
identity, (Tuple{Float64},), (Float64,); cache, policy = AllowAll()
)
@test allow_all(1.0) === 1.0
@test allow_all(1.0f0) === 1.0f0

allow_non_isbits = FunctionWrappersWrapper(
identity, (Tuple{Float64},), (Float64,); cache,
policy = AllowNonIsBits()
)
@test allow_non_isbits(1.0) === 1.0
@test_throws FunctionWrappersWrappers.NoFunctionWrapperFoundError allow_non_isbits(
1.0f0
)
@test allow_non_isbits(big(1)) == big(1)
end
end
end

@testset "Cache modes" begin
f!(du, u, p, t) = (du[1] = p[1] * u[1]; nothing)

Expand Down
1 change: 0 additions & 1 deletion test/qa/qa.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ run_qa(
ignore = (:tail, :FunctionWrapper, Symbol("@truncate_stacktrace")),
),
),
api_docs_kwargs = (; rendered = true),
)
Loading