inkcpp
Loading...
Searching...
No Matches
functional.h
1/* Copyright (c) 2024 Julian Benda
2 *
3 * This file is part of inkCPP which is released under MIT license.
4 * See file LICENSE.txt or go to
5 * https://github.com/JBenda/inkcpp for full license details.
6 */
7#pragma once
8
9#include "config.h"
10#include "list.h"
11#include "traits.h"
12#include "system.h"
13#include "types.h"
14
15#ifdef INK_ENABLE_UNREAL
16# include "../InkVar.h"
17#endif
18
19namespace ink::runtime::internal
20{
21class basic_eval_stack;
22class string_table;
23class list_table;
24
25class callback_base
26{
27public:
28 virtual void call(ink::runtime::value, ink::optional<ink::runtime::value>) = 0;
29 virtual ~callback_base() = default;
30};
31
32template<typename F>
33class callback final : public callback_base
34{
35 using traits = function_traits<F>;
36 static_assert(traits::arity < 3);
37
38 template<ink::runtime::value::Type Ty>
39 void call_functor(ink::runtime::value new_val, ink::optional<ink::runtime::value> old_val)
40 {
41 if constexpr (traits::arity == 2) {
42 if (old_val.has_value()) {
43 functor(
44 new_val.get<Ty>(),
45 typename traits::template argument<1>::type{old_val.value().get<Ty>()}
46 );
47 } else {
48 functor(new_val.get<Ty>(), ink::nullopt);
49 }
50 } else {
51 functor(new_val.get<Ty>());
52 }
53 }
54
55public:
56 callback(const callback&) = delete;
57 callback(callback&&) = delete;
58 callback& operator=(const callback&) = delete;
59 callback& operator=(callback&&) = delete;
60
61 callback(F functor)
62 : functor(functor)
63 {
64 }
65
66 virtual void call(ink::runtime::value new_val, ink::optional<ink::runtime::value> old_val)
67 {
68 using value = ink::runtime::value;
69 auto check_type = [&new_val, &old_val](value::Type type) {
71 new_val.type == type, "Missmatch type for variable observer: expected %i, got %i",
72 static_cast<int>(type), static_cast<int>(new_val.type)
73 );
74 if constexpr (traits::arity == 2) {
75 if (old_val.has_value() && old_val.value().type != type) {
76 inkFail(
77 "Missmatch type for variable observers old value: expected optional<%i> got "
78 "optional<%i>",
79 static_cast<int>(type), static_cast<int>(old_val.value().type)
80 );
81 }
82 }
83 };
84 if constexpr (traits::arity > 0) {
85 using arg_t = typename remove_cvref<typename traits::template argument<0>::type>::type;
86 if constexpr (is_same<arg_t, value>::value) {
87 if constexpr (traits::arity == 2) {
88 functor(new_val, old_val);
89 } else {
90 functor(new_val);
91 }
92 } else if constexpr (is_same<arg_t, bool>::value) {
93 check_type(value::Type::Bool);
94 call_functor<value::Type::Bool>(new_val, old_val);
95 } else if constexpr (is_same<arg_t, uint32_t>::value) {
96 check_type(value::Type::Uint32);
97 call_functor<value::Type::Uint32>(new_val, old_val);
98 } else if constexpr (is_same<arg_t, int32_t>::value) {
99 check_type(value::Type::Int32);
100 call_functor<value::Type::Int32>(new_val, old_val);
101 } else if constexpr (is_same<arg_t, const char*>::value) {
102 check_type(value::Type::String);
103 call_functor<value::Type::String>(new_val, old_val);
104 } else if constexpr (is_same<arg_t, float>::value) {
105 check_type(value::Type::Float);
106 call_functor<value::Type::Float>(new_val, old_val);
107 } else if constexpr (is_same<arg_t, list_interface*>::value) {
108 check_type(value::Type::List);
109 call_functor<value::Type::List>(new_val, old_val);
110 } else {
111 static_assert(
112 always_false<arg_t>::value, "Unsupported value for variable observer callback!"
113 );
114 }
115 } else {
116 functor();
117 }
118 }
119
120private:
121 F functor;
122};
123
124// base function container with virtual callback methods
125class function_base
126{
127public:
128 function_base(bool lookaheadSafe)
129 : _lookaheadSafe(lookaheadSafe)
130 {
131 }
132
133 virtual ~function_base() {}
134
135 // calls the underlying function object taking parameters from a stack
136 // TODO: remove ?
137#ifdef INK_ENABLE_UNREAL
138 virtual void
139 call(basic_eval_stack* stack, size_t length, string_table& strings, list_table& lists)
140 = 0;
141#else
142 virtual void
143 call(basic_eval_stack* stack, size_t length, string_table& strings, list_table& lists)
144 = 0;
145#endif
146
147 bool lookaheadSafe() const { return _lookaheadSafe; }
148
149protected:
150 bool _lookaheadSafe;
151 // used to hide basic_eval_stack and value definitions
152 template<typename T>
153 static T pop(basic_eval_stack* stack, list_table& lists);
154
155 // used to hide basic_eval_stack and value definitions
156 template<typename T>
157 static void push(basic_eval_stack* stack, const T& value);
158
159 static void push_void(basic_eval_stack* stack);
160
161 // string special push
162 static void push_string(basic_eval_stack* stack, const char* dynamic_string);
163
164 // used to hide string_table definitions
165 static char* allocate(string_table& strings, ink::size_t len);
166};
167
168// Stores a Callable function object and forwards calls to it
169template<typename F>
170class function : public function_base
171{
172public:
173 function(F functor, bool lookaheadSafe)
174 : function_base(lookaheadSafe)
175 , functor(functor)
176 {
177 }
178
179 // calls the underlying function using arguments on the stack
180 virtual void call(
181 basic_eval_stack* stack, size_t length, string_table& strings, list_table& lists
182 ) override
183 {
184 call(stack, length, strings, lists, GenSeq<traits::arity>());
185 }
186
187private:
188 // Callable functor object
189 F functor;
190
191 // function traits
192 using traits = function_traits<F>;
193
194 // argument types
195 template<int index>
196 using arg_type = typename function_traits<F>::template argument<index>::type;
197
198 // pops an argument from the stack using the function-type
199 template<int index>
200 arg_type<index> pop_arg(basic_eval_stack* stack, list_table& lists)
201 {
202 // todo - type assert?
203 return pop<arg_type<index>>(stack, lists);
204 }
205
206 static constexpr bool is_array_call()
207 {
208 if constexpr (traits::arity == 2) {
209 return is_same<
210 const ink::runtime::value*, typename traits::template argument<1>::type>::value;
211 } else {
212 return false;
213 }
214 }
215
216 template<size_t... Is>
217 void
218 call(basic_eval_stack* stack, size_t length, string_table& strings, list_table& lists, seq<Is...>)
219 {
220 inkAssert(
221 is_array_call() || sizeof...(Is) == length,
222 "Attempting to call functor with too few/many arguments"
223 );
224 static_assert(sizeof...(Is) == traits::arity);
225 if_t<is_array_call(), value[config::maxArrayCallArity], char> vals;
226 if constexpr (is_array_call()) {
227 inkAssert(
229 "AIttampt to call array call with more arguments then supportet, please change in "
230 "config"
231 );
232 for (size_t i = 0; i < length; ++i) {
233 vals[i] = pop<ink::runtime::value>(stack, lists);
234 }
235 }
236 // void functions
237 if constexpr (is_same<void, typename traits::return_type>::value) {
238 // Just evaluevaluatelate
239 if constexpr (is_array_call()) {
240 functor(length, vals);
241 } else {
242 functor(pop_arg<Is>(stack, lists)...);
243 }
244
245 // Ink expects us to push something
246 push_void(stack);
247 } else {
248 typename traits::return_type res;
249 if constexpr (is_array_call()) {
250 res = functor(length, vals);
251 } else {
252 res = functor(pop_arg<Is>(stack, lists)...);
253 }
254 if constexpr (is_string<typename traits::return_type>::value) {
255 // SPECIAL: The result of the functor is a string type
256 // in order to store it in the inkcpp interpreter we
257 // need to store it in our allocated string table
258 // Get string length
259 size_t len = string_handler<typename traits::return_type>::length(res);
260
261 // Get source and allocate buffer
262 char* buffer = allocate(strings, len + 1);
263 string_handler<typename traits::return_type>::src_copy(res, buffer);
264
265 // push string result
266 push_string(stack, buffer);
267 } else if constexpr (is_same<value, remove_cvref<typename traits::return_type>>::value) {
268 if (res.type() == ink::runtime::value::Type::String) {
269 auto src = res.template get<ink::runtime::value::Type::String>();
270 size_t len = string_handler<decltype(src)>::length(src);
271 char* buffer = allocate(strings, len + 1);
272 string_handler<decltype(src)>::src_copy(src, buffer);
273 push_string(stack, buffer);
274 } else {
275 push(stack, res);
276 }
277 } else {
278 // Evaluate and push the result onto the stack
279 push(stack, res);
280 }
281 }
282 }
283};
284
285#ifdef INK_ENABLE_UNREAL
286template<typename D>
287class function_array_delegate : public function_base
288{
289public:
290 function_array_delegate(const D& del, bool lookaheadSafe)
291 : function_base(lookaheadSafe)
292 , invocableDelegate(del)
293 {
294 }
295
296 // calls the underlying delegate using arguments on the stack
297 virtual void call(
298 basic_eval_stack* stack, size_t length, string_table& strings, list_table& lists
299 ) override
300 {
301 constexpr bool RET_VOID
302 = is_same<typename function_traits<decltype(&D::Execute)>::return_type, void>::value;
303 // Create variable array
304 TArray<FInkVar> variables;
305 for (size_t i = 0; i < length; i++) {
306 variables.Add(pop<FInkVar>(stack, lists));
307 }
308 if constexpr (RET_VOID) {
309 invocableDelegate.Execute(variables);
310 push(stack, 0);
311 } else {
312
313 auto ret = invocableDelegate.Execute(variables);
314 ink::runtime::value result = ret.to_value();
316 const char* src = result.get<ink::runtime::value::Type::String>();
317 size_t len = string_handler<const char*>::length(src);
318 char* buffer = allocate(strings, len + 1);
319 char* ptr = buffer;
320 while (*src != '\0')
321 *(ptr++) = *(src++);
322 *ptr = 0;
323 result = ink::runtime::value(buffer);
324 }
325 push(stack, result);
326 }
327 }
328
329private:
330 D invocableDelegate;
331};
332#endif
333} // namespace ink::runtime::internal
#define inkFail(text,...)
Compile argument agnostic assert macro (always asserts).
Definition system.h:59
#define inkAssert(condition, text,...)
Compile argument agnostic assert macro.
Definition system.h:58
constexpr int maxArrayCallArity
max number of arguments for external functions (dynamic not possible).
Definition config.h:81
std::optional< T > optional
custom optional implementation for usage if STL is disabled
Definition system.h:335
unsigned int size_t
Used for the size of arrays.
Definition system.h:113
constexpr std::nullopt_t nullopt
an empty optional
Definition system.h:337
Type
Type labels for ink::runtime::value.
Definition types.h:49
@ Float
containing a float
Definition types.h:54
@ String
contaning a const char*
Definition types.h:53
@ Uint32
containing uint32_t
Definition types.h:51
@ List
containing a list_interface
Definition types.h:55
@ Int32
containing int32_t
Definition types.h:52
@ Bool
containing bool
Definition types.h:50
enum ink::runtime::value::Type type
Label of type currently contained in value.
const auto & get() const
Get value to corresponding type.
Definition types.h:141