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