v8toolkit  0.0.1
Utility library for embedding V8 Javascript engine in a c++ program
parameter_builder.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <tuple>
4 
5 
6 #include <v8.h>
7 
8 #include "casts.hpp"
9 #include "v8helpers.h"
11 
12 namespace v8toolkit {
13 
14 // define this to log debugging messages while running
15 //#define V8TOOLKIT_PARAMETER_BUILDER_LOGGING
16 
17 #ifdef V8TOOLKIT_PARAMETER_BUILDER_LOGGING
18 #define PB_PRINT(format_string, ...) \
19  std::cerr << fmt::format(format_string, ##__VA_ARGS__) << std::endl;
20 #else
21 #define PB_PRINT(format_string, ...)
22 #endif
23 
24 
25 /**
26  * Used by CallCallable to build an actual list of values to send to a function. Each call to ParameterBuilder
27  * is responsible for one parameter.
28  *
29  * If the parameter isn't self contained (like a char *), then the memory allocated for it will be stored in `stuff`
30  * which is automatically cleaned up when the function returns.
31  *
32  * If no explicit parameter is provided from JavaScript, a default value may be available in `default_args_tuple`
33  */
34 template<class T, class = void>
36 
37 
38 /**
39  * Handles lvalue reference types by delegating the call to the pointer version and dereferencing the
40  * returned pointer - except for char type, because char [const] * is special
41  */
42 template <class T>
43 struct ParameterBuilder<T &, std::enable_if_t<!std::is_same_v<std::remove_const_t<T>, char> && !is_wrapped_type_v<T>>> {
44  template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
45  T & operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
46  std::vector<std::unique_ptr<StuffBase>> & stuff,
47  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
48  PB_PRINT("ParameterBuilder handling lvalue reference: {}", demangle<T>());
49  return * ParameterBuilder<T*>()(info, i, stuff, std::move(default_args_tuple));
50  }
51 };
52 
53 
54 template <class T>
55 struct ParameterBuilder<T &, std::enable_if_t<std::is_same_v<std::remove_const_t<T>, char> && !is_wrapped_type_v<T>>> {
56  template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
57  T & operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
58  std::vector<std::unique_ptr<StuffBase>> & stuff,
59  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
60  PB_PRINT("ParameterBuilder handling lvalue reference for char: {}", demangle<T>());
61  auto value = info[i++];
62  auto isolate = info.GetIsolate();
63  stuff.push_back(std::make_unique<Stuff<char>>(std::make_unique<char>(CastToNative<char>()(isolate, value))));
64  return *static_cast<Stuff<char> &>(*stuff.back()).get();
65 
66  }
67 };
68 
69 
70 template <class T>
71 struct ParameterBuilder<T &&, std::enable_if_t<!is_wrapped_type_v<T>>> {
72 
73  template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
74  T && operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
75  std::vector<std::unique_ptr<StuffBase>> & stuff,
76  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
77 
78  PB_PRINT("ParameterBuilder handling rvalue reference to unwrapped type: {}", demangle<T>());
79 // std::cerr << fmt::format("default_arg_position: {}", default_arg_position) << std::endl;
80  return std::move(*(ParameterBuilder<T*>().template operator()<default_arg_position>(info, i, stuff, std::move(default_args_tuple))));
81  }
82 };
83 
84 
85 
86 /**
87 * Pointers to unwrapped types
88 */
89 template <class T>
90 struct ParameterBuilder<T*, std::enable_if_t<!is_wrapped_type_v<T> >
91 > {
92  using WrappedT = std::remove_const_t<std::remove_pointer_t<std::remove_reference_t<T>>>;
93 
94  template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
95  WrappedT * operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
96  std::vector<std::unique_ptr<StuffBase>> & stuff,
97  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
98 
99  PB_PRINT("ParameterBuilder handling pointers to unwrapped types: {}", demangle<T>());
100  if (i >= info.Length()) {
101  return get_default_parameter<WrappedT, default_arg_position>(info, i, stuff, default_args_tuple);
102  } else {
103  stuff.push_back(Stuff<WrappedT>::stuffer(CastToNative<WrappedT>()(info.GetIsolate(), info[i++])));
104  return static_cast<Stuff<WrappedT> &>(*stuff.back()).get();
105  }
106  }
107 };
108 
109 
110 
111 /**
112 * Pointers and references to wrapped types.
113 * Wrapped types already have a pointer to memory for an existing C++ object in side the javascript object, so no need
114 * to populate `stuff`
115 */
116 template <class T>
117 struct ParameterBuilder<T*, std::enable_if_t<is_wrapped_type_v<T> >
118 > {
119  using WrappedT = std::remove_pointer_t<std::remove_reference_t<T>>;
120 
121  template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
122  WrappedT * operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
123  std::vector<std::unique_ptr<StuffBase>> & stuff,
124  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
125 
126  PB_PRINT("ParameterBuilder handling pointer to wrapped type: {}", demangle<T>());
127 
128  //std::cerr << fmt::format("ParameterBuilder type: pointer-to {} default_arg_position = {}", v8toolkit::demangle<T>(), default_arg_position) << std::endl;
129  if (i >= info.Length()) {
130  return *get_default_parameter<WrappedT *, default_arg_position>(info, i, stuff, default_args_tuple);
131 
132  } else {
133  // try to get the object from inside a javascript object, otherwise, fall back to a CastToNative<T> call
134  if (auto wrapped_pointer = CastToNative<WrappedT *>()(info.GetIsolate(), info[i++])) {
135  return wrapped_pointer;
136 
137  } else if constexpr(CastToNative<WrappedT>::callable()) {
138  stuff.push_back(Stuff<WrappedT>::stuffer(CastToNative<WrappedT>()(info.GetIsolate(), info[i++])));
139  return static_cast<Stuff<WrappedT> &>(*stuff.back()).get();
140  } else {
141  throw CastException("Tried to send a pointer to a function but the JavaScript object wasn't a wrapped "
142  "C++ object and a new object couldn't be created from the JavaScript value provided");
143  }
144  }
145  }
146 };
147 
148 
149 
150 template<class T>
151 struct ParameterBuilder<T, std::enable_if_t<!std::is_pointer_v<T> && !std::is_reference_v<T> && !is_wrapped_type_v<T>>> {
152  using NoRefT = std::remove_reference_t<T>;
153 
154 
155  template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
156  T operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
157  std::vector<std::unique_ptr<StuffBase>> & stuff,
158  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
159 
160  PB_PRINT("ParameterBuilder handling type: {}", demangle<T>());
161  if (i < info.Length()) {
162  return CastToNative<T>()(info.GetIsolate(), info[i++]);
163 
164  } else if constexpr(default_arg_position >= 0 && default_arg_position < std::tuple_size_v<DefaultArgsTuple>) {
165 
166  std::cerr << fmt::format("getting default value from default args tuple: {} => {}",
167  demangle<T>(),
168  std::get<(std::size_t) default_arg_position>(default_args_tuple)) << std::endl;
169 
170  return std::get<(std::size_t) default_arg_position>(default_args_tuple);
171  } else {
172  throw CastException("Not enough parameters and no default value for {}", demangle<T>());
173  }
174  }
175 };
176 
177 
178 
179 
180 template<>
181 struct ParameterBuilder<char *> {
182  using T = char *;
183 
184  template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
185  char * operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
186  std::vector<std::unique_ptr<StuffBase>> & stuff,
187  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
188 
189  PB_PRINT("ParameterBuilder handling char *");
190 
191  //std::cerr << fmt::format("ParameterBuilder type: char* default_arg_position = {}", default_arg_position) << std::endl;
192 
193  // if there is a value, use it, otherwise just use empty string
194  if (i >= info.Length()) {
195  return *get_default_parameter<T, default_arg_position>(info, i, stuff, default_args_tuple);
196 
197  } else {
198  auto string = CastToNative<T>()(info.GetIsolate(), info[i++]);
199  stuff.emplace_back(std::make_unique<Stuff<decltype(string)>>(std::move(string)));
200  return static_cast<Stuff<decltype(string)> &>(*stuff.back()).get()->get();
201  }
202  }
203 
204 };
205 
206 
207 template<template<class, class...> class Container, class... Rest>
208 struct ParameterBuilder<Container<char *, Rest...>, std::enable_if_t<!std::is_reference<std::result_of_t<
209  CastToNative<std::remove_const_t<std::remove_reference_t<Container<char *, Rest...>>>>(v8::Isolate *,
211 > // end result_of
212 >::value
213 >> {
214  using ResultType = Container<char *>;
215  using IntermediaryType = std::result_of_t<CastToNative<char *>(v8::Isolate *, v8::Local<v8::Value>)>;
216  using DataHolderType = Container<IntermediaryType>;
217 
218 
219  template<int default_arg_position, class DefaultArgsTuple = std::tuple<>>
220  ResultType operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
221  std::vector<std::unique_ptr<StuffBase>> & stuffs,
222  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
223 
224  PB_PRINT("ParameterBuilder handling container of char *");
225 
226  //std::cerr << fmt::format("ParameterBuilder type: Container<char*,...> deafult_arg_position = {}", default_arg_position) << std::endl;
227 
228  if (i >= info.Length()) {
229  // static_assert(false, "implement me");
230  throw InvalidCallException(fmt::format(
231  "Not enough javascript parameters for function call - requires {} but only {} were specified",
232  i + 1 + sizeof(Rest)..., info.Length()));
233  }
234  Stuff<DataHolderType> stuff(CastToNative<ResultType>()(info.GetIsolate(), info[i++]));
235  auto data_holder = stuff.get();
236 
237  stuffs.emplace_back(std::move(stuff));
238 
239 
240  ResultType result;
241  for (auto & str : data_holder) {
242  result.push_back(str->c_str());
243  }
244  return result;
245  }
246 };
247 
248 template<template<class, class...> class Container, class... Rest>
249 struct ParameterBuilder<Container<char const *, Rest...>,
250  std::enable_if_t<!std::is_reference<std::result_of_t<
251  CastToNative<std::remove_reference_t<Container<const char *, Rest...>>>(v8::Isolate *, v8::Local<v8::Value>)
252  > // end result_of
253  >::value
254  >> {
255  using ResultType = Container<const char *>;
256  using IntermediaryType = std::result_of_t<CastToNative<const char *>(v8::Isolate *, v8::Local<v8::Value>)>;
257  using DataHolderType = Container<IntermediaryType>;
258 
259 
260  template<int default_arg_position, class DefaultArgsTuple = std::tuple<>>
261  ResultType operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
262  std::vector<std::unique_ptr<StuffBase>> & stuffs,
263  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
264 
265  PB_PRINT("ParameterBuilder handling container of char const *");
266 
267  //std::cerr << fmt::format("parameterbuilder type: Container<char const *,...> default_arg_position = {}", default_arg_position) << std::endl;
268  if (i >= info.Length()) {
269  return *get_default_parameter<ResultType, default_arg_position>(info, i, stuffs, default_args_tuple);
270 
271  } else {
272  stuffs.emplace_back(Stuff<ResultType>::stuffer(CastToNative<ResultType>()(info.GetIsolate(), info[i++])));
273  return *static_cast<Stuff<ResultType> &>(*stuffs.back()).get()->get();
274  }
275  }
276 };
277 
278 
279 // const char *
280 template<>
281 struct ParameterBuilder<char const *> {
282  using T = char const *;
283 
284  template<int default_arg_position, class DefaultArgsTuple = std::tuple<>>
285  char const * operator()(const v8::FunctionCallbackInfo<v8::Value> & info,
286  int & i,
287  std::vector<std::unique_ptr<StuffBase>> & stuff,
288  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
289 
290  PB_PRINT("ParameterBuilder handling char const *");
291 
292  //std::cerr << fmt::format("ParameterBuilder type: char const *, default_arg_position = {}", default_arg_position) << std::endl;
293 
294  // if there is a value, use it, otherwise just use empty string
295  if (i >= info.Length()) {
296  return *get_default_parameter<T, default_arg_position>(info, i, stuff, default_args_tuple);
297 
298  } else {
299  auto string = CastToNative<T>()(info.GetIsolate(), info[i++]);
300  stuff.emplace_back(std::make_unique<Stuff<decltype(string)>>(std::move(string)));
301  return static_cast<Stuff<decltype(string)> &>(*stuff.back()).get()->get();
302  }
303  }
304 };
305 
306 
307 template<>
308 struct ParameterBuilder<const v8::FunctionCallbackInfo<v8::Value> &> {
309 
310  template<int default_arg_position, class DefaultArgsTuple = std::tuple<>>
311  const v8::FunctionCallbackInfo<v8::Value> & operator()(const v8::FunctionCallbackInfo<v8::Value> & info,
312  int & i,
313  std::vector<std::unique_ptr<StuffBase>> & stuff,
314  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
315  PB_PRINT("ParameterBuilder handling v8::FunctionCallbackInfo<v8::Value> const &");
316 
317  return info;
318  }
319 };
320 
321 
322 template<>
324  template<int default_arg_position, class DefaultArgsTuple = std::tuple<>>
325  v8::Isolate * operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
326  std::vector<std::unique_ptr<StuffBase>> & stuff,
327  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
328  PB_PRINT("ParameterBuilder handling v8::Isolate *");
329 
330  return info.GetIsolate();
331  }
332 };
333 
334 
335 template<>
336 struct ParameterBuilder<v8::Local<v8::Context>> {
337  template<int default_arg_position, class DefaultArgsTuple = std::tuple<>>
338  v8::Local<v8::Context> operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
339  std::vector<std::unique_ptr<StuffBase>> & stuff,
340  DefaultArgsTuple const & default_args_tuple = DefaultArgsTuple()) {
341  PB_PRINT("ParameterBuilder handling v8::Local<v8::Context>");
342 
343  return info.GetIsolate()->GetCurrentContext();
344  }
345 };
346 
347 
348 /**
349  * If the type wants a JavaScript object directly, pass it through
350  * @tparam T
351  */
352 template <class T>
354  v8::Local<T>,
355  std::enable_if_t<
356  !std::is_pointer_v<v8::Local<T>> &&
357  !std::is_reference_v<v8::Local<T>> &&
358  !is_wrapped_type_v<v8::Local<T>>
359  > // enable_if
360 > {
361 
362  template<int default_arg_position, class DefaultArgsTuple = std::tuple<>>
363  v8::Local<T> operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
364  std::vector<std::unique_ptr<StuffBase>> & stuff,
365  DefaultArgsTuple const & default_args_tuple = DefaultArgsTuple()) {
366  PB_PRINT("ParameterBuilder handling v8::Local<{}>", demangle<T>());
367 
368  return v8toolkit::get_value_as<T>(info.This());
369  }
370 };
371 
372 } // namespace v8toolkit
WrappedT * operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
T & operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
STL namespace.
char * operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
T && operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
v8::Local< v8::Context > operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple const &default_args_tuple=DefaultArgsTuple())
v8::Isolate * operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
ResultType operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuffs, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
Definition: sample.cpp:29
char const * operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
ResultType operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuffs, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
T operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
#define PB_PRINT(format_string,...)
const v8::FunctionCallbackInfo< v8::Value > & operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
std::remove_const_t< std::remove_pointer_t< std::remove_reference_t< T >>> WrappedT
std::remove_pointer_t< std::remove_reference_t< T >> WrappedT
v8::Local< T > operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple const &default_args_tuple=DefaultArgsTuple())
const T & move(const T &t)
Definition: gtest-port.h:1317
WrappedT * operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
T & operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())