diff --git a/docs/src/index.md b/docs/src/index.md index 79db23f..f5f6b96 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -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 ``` diff --git a/src/FunctionWrappersWrappers.jl b/src/FunctionWrappersWrappers.jl index 4fa86bd..97b9c0f 100644 --- a/src/FunctionWrappersWrappers.jl +++ b/src/FunctionWrappersWrappers.jl @@ -14,7 +14,7 @@ export Strict, AllowAll, AllowNonIsBits abstract type AbstractCacheMode end """ - NoCache() + NoCache() -> NoCache Disable fallback wrapper caching. @@ -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. @@ -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. @@ -57,7 +58,7 @@ struct DictCache <: AbstractCacheMode end abstract type AbstractFallbackPolicy end """ - Strict() + Strict() -> Strict Disable fallback calls. @@ -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. @@ -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. @@ -97,7 +98,7 @@ struct AllowNonIsBits <: AbstractFallbackPolicy end # Cache storage types # ============================================================================ """ - NoCacheStorage() + NoCacheStorage() -> NoCacheStorage Storage marker for the `NoCache()` fallback mode. @@ -122,7 +123,7 @@ storage = FunctionWrappersWrappers.NoCacheStorage() struct NoCacheStorage end """ - SingleCacheStorage() + SingleCacheStorage() -> SingleCacheStorage Storage for the `SingleCache()` fallback mode. @@ -154,7 +155,7 @@ mutable struct SingleCacheStorage end """ - DictCacheStorage() + DictCacheStorage() -> DictCacheStorage Storage for the `DictCache()` fallback mode. @@ -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. @@ -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. @@ -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. @@ -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`. @@ -402,7 +408,7 @@ end # ============================================================================ """ - unwrap(fww::FunctionWrappersWrapper) + unwrap(fww::FunctionWrappersWrapper) -> Function Return the original function that was wrapped. @@ -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. @@ -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. diff --git a/test/basictests.jl b/test/basictests.jl index a469614..7ba43a4 100644 --- a/test/basictests.jl +++ b/test/basictests.jl @@ -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) diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 5ceb69b..c8149f6 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -14,5 +14,4 @@ run_qa( ignore = (:tail, :FunctionWrapper, Symbol("@truncate_stacktrace")), ), ), - api_docs_kwargs = (; rendered = true), )