Chrysalis - Hooks
Loading...
Searching...
No Matches
types.h
Go to the documentation of this file.
1#ifndef CHRYSALIS_TYPES_H
2#define CHRYSALIS_TYPES_H
3
4#include "HookHandle.h"
5
6// =============================================================================
7// type-level traits for concepts
8//
9// MSVC cannot deduce partial specializations of templates whose non-type
10// template parameter is a member function pointer (e.g. Traits<&Foo::bar>).
11// The workaround is to specialize on the pointer *type* via decltype(F),
12// which uses ordinary type-level partial specialization that all compilers
13// handle reliably.
14// =============================================================================
15
16// ── AfterType<R, ExtraArgs...> ────────────────────────────────────────────────
17// Lazily builds the After std::function signature.
18// std::conditional_t is NOT used here because it instantiates both branches
19// eagerly — forming std::function<void(HookHandle, void&, ...)> is ill-formed.
20// Partial specialization on void avoids that instantiation entirely.
21template <typename R, typename... ExtraArgs>
22struct AfterType {
23 // Non-void: callback receives the return value as a mutable ref first.
24 using type = std::function<void(HookHandle, R&, ExtraArgs&...)>;
25};
26
27template <typename... ExtraArgs>
28struct AfterType<void, ExtraArgs...> {
29 // Void: no return value parameter.
30 using type = std::function<void(HookHandle, ExtraArgs&...)>;
31};
32
33// ── ReplaceType<R, ExtraArgs...> ──────────────────────────────────────────────
34// Builds the Replace std::function signature: the callback fully replaces
35// the original function body, so it receives the (mutable) call arguments
36// and must itself produce the return value — no void-split needed since R
37// is simply the function's own return type here (possibly void).
38template <typename R, typename... ExtraArgs>
40 using type = std::function<R(HookHandle, ExtraArgs&...)>;
41};
42
43// ── IgnoreConditionallyType<R, ExtraArgs...> ──────────────────────────────────
44// Builds the IgnoreConditionally callback signature: like Before, it sees
45// every call argument by reference, plus a leading bool& the callback sets
46// to decide (per-call) whether the original should run. For non-void R a
47// ResultArg is also exposed so the callback can supply a substitute result
48// when it chooses to skip the original — reference returns use a pointer
49// since a reference cannot be default-initialized before the real call.
50template <typename R, typename... ExtraArgs>
52 using ResultArg = std::conditional_t<std::is_reference_v<R>, std::remove_reference_t<R>*, R>;
53 using type = std::function<void(HookHandle, bool&, ResultArg&, ExtraArgs&...)>;
54};
55
56template <typename... ExtraArgs>
57struct IgnoreConditionallyType<void, ExtraArgs...> {
58 using type = std::function<void(HookHandle, bool&, ExtraArgs&...)>;
59};
60
61// ── FuncTraits<T> — undefined base (SFINAE / concept failure for bad T) ──────
62template <typename T>
64
65// ── Free function: R(*)(Args...) ──────────────────────────────────────────────
66template <typename R, typename... Args>
67struct FuncTraits<R (*)(Args...)> {
68 using ReturnType = R;
69 using Before = std::function<void(HookHandle, Args&...)>;
70 using After = AfterType<R, Args...>::type;
71 using Replace = ReplaceType<R, Args...>::type;
73};
74
75// ── Non-const member function: R(Class::*)(Args...) ──────────────────────────
76template <typename R, typename Class, typename... Args>
77struct FuncTraits<R (Class::*)(Args...)> {
78 using ReturnType = R;
79 using Before = std::function<void(HookHandle, Class*&, Args&...)>;
80 using After = AfterType<R, Class*, Args...>::type;
81 using Replace = ReplaceType<R, Class*, Args...>::type;
83};
84
85// ── Const member function: R(Class::*)(Args...) const ────────────────────────
86// The callback types here (Before/After/Replace/IgnoreConditionally) are
87// uniform for every R — trivial, non-trivial, or void — and never expose
88// a raw pointer to the return value. Non-trivial R needs extra care at
89// the HookTraits level below (where the actual trampoline/hook() ABI is
90// handled), but that's entirely internal and doesn't affect this type.
91template <typename R, typename Class, typename... Args>
92struct FuncTraits<R (Class::*)(Args...) const> {
93 using ReturnType = R;
94 using Before = std::function<void(HookHandle, const Class*&, Args&...)>;
95 using After = AfterType<R, const Class*, Args...>::type;
96 using Replace = ReplaceType<R, const Class*, Args...>::type;
97 using IgnoreConditionally = IgnoreConditionallyType<R, const Class*, Args...>::type;
98};
99
100// ── Noexcept free function: R(*)(Args...) noexcept ───────────────────────────
101template <typename R, typename... Args>
102struct FuncTraits<R (*)(Args...) noexcept> {
103 using ReturnType = R;
104 using Before = std::function<void(HookHandle, Args&...)>;
105 using After = AfterType<R, Args...>::type;
106 using Replace = ReplaceType<R, Args...>::type;
108};
109
110// ── Noexcept non-const member function: R(Class::*)(Args...) noexcept ────────
111template <typename R, typename Class, typename... Args>
112struct FuncTraits<R (Class::*)(Args...) noexcept> {
113 using ReturnType = R;
114 using Before = std::function<void(HookHandle, Class*&, Args&...)>;
115 using After = AfterType<R, Class*, Args...>::type;
116 using Replace = ReplaceType<R, Class*, Args...>::type;
118};
119
120// ── Noexcept const member function: R(Class::*)(Args...) const noexcept ──────
121template <typename R, typename Class, typename... Args>
122struct FuncTraits<R (Class::*)(Args...) const noexcept> {
123 using ReturnType = R;
124 using Before = std::function<void(HookHandle, const Class*&, Args&...)>;
125 using After = AfterType<R, const Class*, Args...>::type;
126 using Replace = ReplaceType<R, const Class*, Args...>::type;
127 using IgnoreConditionally = IgnoreConditionallyType<R, const Class*, Args...>::type;
128};
129
130// =============================================================================
131// Concepts
132// =============================================================================
133
134// ── HookableFunction<F> ───────────────────────────────────────────────────────
135// F must be a free function pointer or a (possibly const) member function
136// pointer. Implemented via standard type traits to avoid MSVC's partial
137// specialization deduction issues with member function pointer NTTPs.
138template <auto F>
139concept HookableFunction = std::is_member_function_pointer_v<decltype(F)> ||
140 (std::is_pointer_v<decltype(F)> && std::is_function_v<std::remove_pointer_t<decltype(F)>>);
141
142// ── VoidHookableFunction<F> ───────────────────────────────────────────────────
143// F must be hookable AND return void. The Ignore feature only makes sense for
144// void functions: there is no result to fabricate when the original call is
145// skipped, so the API is restricted at compile time to this subset.
146template <auto F>
147concept VoidHookableFunction = HookableFunction<F> && std::is_void_v<typename FuncTraits<decltype(F)>::ReturnType>;
148
149// ── BeforeCallbackFor<Cb, F> ──────────────────────────────────────────────────
150// Cb must be implicitly convertible to the expected Before callback type for F.
151// Gives a readable error at the addBefore call site when the signature is wrong.
152template <typename Cb, auto F>
153concept BeforeCallbackFor = HookableFunction<F> && std::convertible_to<Cb, typename FuncTraits<decltype(F)>::Before>;
154
155// ── AfterCallbackFor<Cb, F> ───────────────────────────────────────────────────
156// Same as above for After callbacks, which additionally carry the return value
157// (if non-void) as a first mutable reference before the call arguments.
158template <typename Cb, auto F>
159concept AfterCallbackFor = HookableFunction<F> && std::convertible_to<Cb, typename FuncTraits<decltype(F)>::After>;
160
161// ── ReplaceCallbackFor<Cb, F> ─────────────────────────────────────────────────
162// Cb must be implicitly convertible to the expected Replace callback type for
163// F: it receives the call arguments and must produce the return value itself,
164// since (unlike Before/After) the original function is never invoked.
165template <typename Cb, auto F>
166concept ReplaceCallbackFor = HookableFunction<F> && std::convertible_to<Cb, typename FuncTraits<decltype(F)>::Replace>;
167
168// ── IgnoreConditionallyCallbackFor<Cb, F> ─────────────────────────────────────
169// Cb must be implicitly convertible to the expected IgnoreConditionally
170// callback type for F: like Before, plus a leading bool& the callback sets to
171// decide per-call whether the original should be skipped.
172template <typename Cb, auto F>
174 HookableFunction<F> && std::convertible_to<Cb, typename FuncTraits<decltype(F)>::IgnoreConditionally>;
175
176#endif // CHRYSALIS_TYPES_H
Definition HookHandle.h:19
Definition types.h:159
Definition types.h:153
Definition types.h:139
Definition types.h:173
Definition types.h:166
Definition types.h:147
std::function< void(HookHandle, ExtraArgs &...)> type
Definition types.h:30
Definition types.h:22
std::function< void(HookHandle, R &, ExtraArgs &...)> type
Definition types.h:24
ReplaceType< R, const Class *, Args... >::type Replace
Definition types.h:96
std::function< void(HookHandle, const Class *&, Args &...)> Before
Definition types.h:94
AfterType< R, const Class *, Args... >::type After
Definition types.h:95
IgnoreConditionallyType< R, const Class *, Args... >::type IgnoreConditionally
Definition types.h:97
std::function< void(HookHandle, const Class *&, Args &...)> Before
Definition types.h:124
AfterType< R, const Class *, Args... >::type After
Definition types.h:125
ReplaceType< R, const Class *, Args... >::type Replace
Definition types.h:126
IgnoreConditionallyType< R, const Class *, Args... >::type IgnoreConditionally
Definition types.h:127
ReplaceType< R, Class *, Args... >::type Replace
Definition types.h:116
IgnoreConditionallyType< R, Class *, Args... >::type IgnoreConditionally
Definition types.h:117
AfterType< R, Class *, Args... >::type After
Definition types.h:115
std::function< void(HookHandle, Class *&, Args &...)> Before
Definition types.h:114
AfterType< R, Class *, Args... >::type After
Definition types.h:80
R ReturnType
Definition types.h:78
std::function< void(HookHandle, Class *&, Args &...)> Before
Definition types.h:79
ReplaceType< R, Class *, Args... >::type Replace
Definition types.h:81
IgnoreConditionallyType< R, Class *, Args... >::type IgnoreConditionally
Definition types.h:82
std::function< void(HookHandle, Args &...)> Before
Definition types.h:104
R ReturnType
Definition types.h:103
ReplaceType< R, Args... >::type Replace
Definition types.h:106
IgnoreConditionallyType< R, Args... >::type IgnoreConditionally
Definition types.h:107
AfterType< R, Args... >::type After
Definition types.h:105
ReplaceType< R, Args... >::type Replace
Definition types.h:71
AfterType< R, Args... >::type After
Definition types.h:70
R ReturnType
Definition types.h:68
IgnoreConditionallyType< R, Args... >::type IgnoreConditionally
Definition types.h:72
std::function< void(HookHandle, Args &...)> Before
Definition types.h:69
Definition types.h:63
std::function< void(HookHandle, bool &, ExtraArgs &...)> type
Definition types.h:58
Definition types.h:51
std::conditional_t< std::is_reference_v< R >, std::remove_reference_t< R > *, R > ResultArg
Definition types.h:52
std::function< void(HookHandle, bool &, ResultArg &, ExtraArgs &...)> type
Definition types.h:53
Definition types.h:39
std::function< R(HookHandle, ExtraArgs &...)> type
Definition types.h:40