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