ChaiScript
proxy_functions.hpp
1 // This file is distributed under the BSD License.
2 // See "license.txt" for details.
3 // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
4 // Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
5 // http://www.chaiscript.com
6 
7 // This is an open source non-commercial project. Dear PVS-Studio, please check it.
8 // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
9 
10 #ifndef CHAISCRIPT_PROXY_FUNCTIONS_HPP_
11 #define CHAISCRIPT_PROXY_FUNCTIONS_HPP_
12 
13 #include <cassert>
14 #include <functional>
15 #include <iterator>
16 #include <memory>
17 #include <stdexcept>
18 #include <string>
19 #include <type_traits>
20 #include <vector>
21 
22 #include "../chaiscript_defines.hpp"
23 #include "boxed_cast.hpp"
24 #include "boxed_value.hpp"
25 #include "dynamic_object.hpp"
26 #include "function_params.hpp"
27 #include "proxy_functions_detail.hpp"
28 #include "type_info.hpp"
29 
30 namespace chaiscript {
31  class Type_Conversions;
32  namespace exception {
33  class bad_boxed_cast;
34  struct arity_error;
35  } // namespace exception
36 } // namespace chaiscript
37 
38 namespace chaiscript {
39  class Boxed_Number;
40  struct AST_Node;
41 
42  using AST_NodePtr = std::unique_ptr<AST_Node>;
43 
44  namespace dispatch {
45  template<typename FunctionType>
46  std::function<FunctionType> functor(std::shared_ptr<const Proxy_Function_Base> func, const Type_Conversions_State *t_conversions);
47 
48  class Param_Types {
49  public:
50  Param_Types()
51  : m_has_types(false) {
52  }
53 
54  explicit Param_Types(std::vector<std::pair<std::string, Type_Info>> t_types)
55  : m_types(std::move(t_types))
56  , m_has_types(false) {
57  update_has_types();
58  }
59 
60  void push_front(std::string t_name, Type_Info t_ti) {
61  m_types.emplace(m_types.begin(), std::move(t_name), t_ti);
62  update_has_types();
63  }
64 
65  bool operator==(const Param_Types &t_rhs) const noexcept { return m_types == t_rhs.m_types; }
66 
67  std::vector<Boxed_Value> convert(Function_Params t_params, const Type_Conversions_State &t_conversions) const {
68  auto vals = t_params.to_vector();
69  const auto dynamic_object_type_info = user_type<Dynamic_Object>();
70  for (size_t i = 0; i < vals.size(); ++i) {
71  const auto &name = m_types[i].first;
72  if (!name.empty()) {
73  const auto &bv = vals[i];
74 
75  if (!bv.get_type_info().bare_equal(dynamic_object_type_info)) {
76  const auto &ti = m_types[i].second;
77  if (!ti.is_undef()) {
78  if (!bv.get_type_info().bare_equal(ti)) {
79  if (t_conversions->converts(ti, bv.get_type_info())) {
80  try {
81  // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it
82  // either way, we are not responsible if it doesn't work
83  vals[i] = t_conversions->boxed_type_conversion(m_types[i].second, t_conversions.saves(), vals[i]);
84  } catch (...) {
85  try {
86  // try going the other way
87  vals[i] = t_conversions->boxed_type_down_conversion(m_types[i].second, t_conversions.saves(), vals[i]);
89  throw exception::bad_boxed_cast(bv.get_type_info(), *m_types[i].second.bare_type_info());
90  }
91  }
92  }
93  }
94  }
95  }
96  }
97  }
98 
99  return vals;
100  }
101 
102  // first result: is a match
103  // second result: needs conversions
104  std::pair<bool, bool> match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept {
105  const auto dynamic_object_type_info = user_type<Dynamic_Object>();
106  bool needs_conversion = false;
107 
108  if (!m_has_types) {
109  return std::make_pair(true, needs_conversion);
110  }
111  if (vals.size() != m_types.size()) {
112  return std::make_pair(false, needs_conversion);
113  }
114 
115  for (size_t i = 0; i < vals.size(); ++i) {
116  const auto &name = m_types[i].first;
117  if (!name.empty()) {
118  const auto &bv = vals[i];
119 
120  if (bv.get_type_info().bare_equal(dynamic_object_type_info)) {
121  try {
122  const Dynamic_Object &d = boxed_cast<const Dynamic_Object &>(bv, &t_conversions);
123  if (!(name == "Dynamic_Object" || d.get_type_name() == name)) {
124  return std::make_pair(false, false);
125  }
126  } catch (const std::bad_cast &) {
127  return std::make_pair(false, false);
128  }
129  } else {
130  const auto &ti = m_types[i].second;
131  if (!ti.is_undef()) {
132  if (!bv.get_type_info().bare_equal(ti)) {
133  if (!t_conversions->converts(ti, bv.get_type_info())) {
134  return std::make_pair(false, false);
135  } else {
136  needs_conversion = true;
137  }
138  }
139  } else {
140  return std::make_pair(false, false);
141  }
142  }
143  }
144  }
145 
146  return std::make_pair(true, needs_conversion);
147  }
148 
149  const std::vector<std::pair<std::string, Type_Info>> &types() const noexcept { return m_types; }
150 
151  private:
152  void update_has_types() {
153  for (const auto &type : m_types) {
154  if (!type.first.empty()) {
155  m_has_types = true;
156  return;
157  }
158  }
159 
160  m_has_types = false;
161  }
162 
163  std::vector<std::pair<std::string, Type_Info>> m_types;
164  bool m_has_types;
165  };
166 
176  public:
177  virtual ~Proxy_Function_Base() = default;
178 
179  Boxed_Value operator()(const Function_Params &params, const chaiscript::Type_Conversions_State &t_conversions) const {
180  if (m_arity < 0 || size_t(m_arity) == params.size()) {
181  return do_call(params, t_conversions);
182  } else {
183  throw exception::arity_error(static_cast<int>(params.size()), m_arity);
184  }
185  }
186 
191  const std::vector<Type_Info> &get_param_types() const noexcept { return m_types; }
192 
193  virtual bool operator==(const Proxy_Function_Base &) const noexcept = 0;
194  virtual bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const = 0;
195 
196  virtual bool is_attribute_function() const noexcept { return false; }
197 
198  bool has_arithmetic_param() const noexcept { return m_has_arithmetic_param; }
199 
200  virtual std::vector<std::shared_ptr<const Proxy_Function_Base>> get_contained_functions() const {
201  return std::vector<std::shared_ptr<const Proxy_Function_Base>>();
202  }
203 
206  bool filter(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept {
207  assert(m_arity == -1 || (m_arity > 0 && static_cast<int>(vals.size()) == m_arity));
208 
209  if (m_arity < 0) {
210  return true;
211  } else if (m_arity > 1) {
212  return compare_type_to_param(m_types[1], vals[0], t_conversions) && compare_type_to_param(m_types[2], vals[1], t_conversions);
213  } else {
214  return compare_type_to_param(m_types[1], vals[0], t_conversions);
215  }
216  }
217 
219  int get_arity() const noexcept { return m_arity; }
220 
221  static bool compare_type_to_param(const Type_Info &ti, const Boxed_Value &bv, const Type_Conversions_State &t_conversions) noexcept {
222  const auto boxed_value_ti = user_type<Boxed_Value>();
223  const auto boxed_number_ti = user_type<Boxed_Number>();
224  const auto function_ti = user_type<std::shared_ptr<const Proxy_Function_Base>>();
225 
226  if (ti.is_undef() || ti.bare_equal(boxed_value_ti)
227  || (!bv.get_type_info().is_undef()
228  && ((ti.bare_equal(boxed_number_ti) && bv.get_type_info().is_arithmetic()) || ti.bare_equal(bv.get_type_info())
229  || bv.get_type_info().bare_equal(function_ti) || t_conversions->converts(ti, bv.get_type_info())))) {
230  return true;
231  } else {
232  return false;
233  }
234  }
235 
236  virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const noexcept {
238  return compare_type_to_param(m_types[1], bv, t_conversions);
239  }
240 
241  protected:
242  virtual Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const = 0;
243 
244  Proxy_Function_Base(std::vector<Type_Info> t_types, int t_arity)
245  : m_types(std::move(t_types))
246  , m_arity(t_arity)
247  , m_has_arithmetic_param(false) {
248  for (size_t i = 1; i < m_types.size(); ++i) {
249  if (m_types[i].is_arithmetic()) {
250  m_has_arithmetic_param = true;
251  return;
252  }
253  }
254  }
255 
256  static bool compare_types(const std::vector<Type_Info> &tis, const Function_Params &bvs, const Type_Conversions_State &t_conversions) noexcept {
257  if (tis.size() - 1 != bvs.size()) {
258  return false;
259  } else {
260  const size_t size = bvs.size();
261  for (size_t i = 0; i < size; ++i) {
262  if (!compare_type_to_param(tis[i + 1], bvs[i], t_conversions)) {
263  return false;
264  }
265  }
266  }
267  return true;
268  }
269 
270  std::vector<Type_Info> m_types;
271  int m_arity;
272  bool m_has_arithmetic_param;
273  };
274  } // namespace dispatch
275 
277  using Proxy_Function = std::shared_ptr<dispatch::Proxy_Function_Base>;
278 
281  using Const_Proxy_Function = std::shared_ptr<const dispatch::Proxy_Function_Base>;
282 
283  namespace exception {
285  class guard_error : public std::runtime_error {
286  public:
287  guard_error() noexcept
288  : std::runtime_error("Guard evaluation failed") {
289  }
290 
291  guard_error(const guard_error &) = default;
292 
293  ~guard_error() noexcept override = default;
294  };
295  } // namespace exception
296 
297  namespace dispatch {
301  public:
302  Dynamic_Proxy_Function(const int t_arity,
303  std::shared_ptr<AST_Node> t_parsenode,
304  Param_Types t_param_types = Param_Types(),
305  Proxy_Function t_guard = Proxy_Function())
306  : Proxy_Function_Base(build_param_type_list(t_param_types), t_arity)
307  , m_param_types(std::move(t_param_types))
308  , m_guard(std::move(t_guard))
309  , m_parsenode(std::move(t_parsenode)) {
310  // assert(t_parsenode);
311  }
312 
313  bool operator==(const Proxy_Function_Base &rhs) const noexcept override {
314  const Dynamic_Proxy_Function *prhs = dynamic_cast<const Dynamic_Proxy_Function *>(&rhs);
315 
316  return this == &rhs
317  || ((prhs != nullptr) && this->m_arity == prhs->m_arity && !this->m_guard && !prhs->m_guard
318  && this->m_param_types == prhs->m_param_types);
319  }
320 
321  bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const override {
322  return call_match_internal(vals, t_conversions).first;
323  }
324 
325  bool has_guard() const noexcept { return bool(m_guard); }
326 
327  Proxy_Function get_guard() const noexcept { return m_guard; }
328 
329  bool has_parse_tree() const noexcept { return static_cast<bool>(m_parsenode); }
330 
331  const AST_Node &get_parse_tree() const {
332  if (m_parsenode) {
333  return *m_parsenode;
334  } else {
335  throw std::runtime_error("Dynamic_Proxy_Function does not have parse_tree");
336  }
337  }
338 
339  protected:
340  bool test_guard(const Function_Params &params, const Type_Conversions_State &t_conversions) const {
341  if (m_guard) {
342  try {
343  return boxed_cast<bool>((*m_guard)(params, t_conversions));
344  } catch (const exception::arity_error &) {
345  return false;
346  } catch (const exception::bad_boxed_cast &) {
347  return false;
348  }
349  } else {
350  return true;
351  }
352  }
353 
354  // first result: is a match
355  // second result: needs conversions
356  std::pair<bool, bool> call_match_internal(const Function_Params &vals, const Type_Conversions_State &t_conversions) const {
357  const auto comparison_result = [&]() {
358  if (m_arity < 0) {
359  return std::make_pair(true, false);
360  } else if (vals.size() == size_t(m_arity)) {
361  return m_param_types.match(vals, t_conversions);
362  } else {
363  return std::make_pair(false, false);
364  }
365  }();
366 
367  return std::make_pair(comparison_result.first && test_guard(vals, t_conversions), comparison_result.second);
368  }
369 
370  private:
371  static std::vector<Type_Info> build_param_type_list(const Param_Types &t_types) {
372  // For the return type
373  std::vector<Type_Info> types{chaiscript::detail::Get_Type_Info<Boxed_Value>::get()};
374 
375  for (const auto &t : t_types.types()) {
376  if (t.second.is_undef()) {
378  } else {
379  types.push_back(t.second);
380  }
381  }
382 
383  return types;
384  }
385 
386  protected:
387  Param_Types m_param_types;
388 
389  private:
390  Proxy_Function m_guard;
391  std::shared_ptr<AST_Node> m_parsenode;
392  };
393 
394  template<typename Callable>
396  public:
397  Dynamic_Proxy_Function_Impl(Callable t_f,
398  int t_arity = -1,
399  std::shared_ptr<AST_Node> t_parsenode = AST_NodePtr(),
400  Param_Types t_param_types = Param_Types(),
401  Proxy_Function t_guard = Proxy_Function())
402  : Dynamic_Proxy_Function(t_arity, std::move(t_parsenode), std::move(t_param_types), std::move(t_guard))
403  , m_f(std::move(t_f)) {
404  }
405 
406  protected:
407  Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const override {
408  const auto [is_a_match, needs_conversions] = call_match_internal(params, t_conversions);
409  if (is_a_match) {
410  if (needs_conversions) {
411  return m_f(Function_Params{m_param_types.convert(params, t_conversions)});
412  } else {
413  return m_f(params);
414  }
415  } else {
416  throw exception::guard_error();
417  }
418  }
419 
420  private:
421  Callable m_f;
422  };
423 
424  template<typename Callable, typename... Arg>
425  Proxy_Function make_dynamic_proxy_function(Callable &&c, Arg &&...a) {
426  return chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Dynamic_Proxy_Function_Impl<Callable>>(std::forward<Callable>(c),
427  std::forward<Arg>(a)...);
428  }
429 
433  };
434 
439  class Bound_Function final : public Proxy_Function_Base {
440  public:
441  Bound_Function(const Const_Proxy_Function &t_f, const std::vector<Boxed_Value> &t_args)
442  : Proxy_Function_Base(build_param_type_info(t_f, t_args),
443  (t_f->get_arity() < 0 ? -1 : static_cast<int>(build_param_type_info(t_f, t_args).size()) - 1))
444  , m_f(t_f)
445  , m_args(t_args) {
446  assert(m_f->get_arity() < 0 || m_f->get_arity() == static_cast<int>(m_args.size()));
447  }
448 
449  bool operator==(const Proxy_Function_Base &t_f) const noexcept override { return &t_f == this; }
450 
451  bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const override {
452  return m_f->call_match(Function_Params(build_param_list(vals)), t_conversions);
453  }
454 
455  std::vector<Const_Proxy_Function> get_contained_functions() const override { return std::vector<Const_Proxy_Function>{m_f}; }
456 
457  std::vector<Boxed_Value> build_param_list(const Function_Params &params) const {
458  auto parg = params.begin();
459  auto barg = m_args.begin();
460 
461  std::vector<Boxed_Value> args;
462 
463  while (!(parg == params.end() && barg == m_args.end())) {
464  while (barg != m_args.end() && !(barg->get_type_info() == chaiscript::detail::Get_Type_Info<Placeholder_Object>::get())) {
465  args.push_back(*barg);
466  ++barg;
467  }
468 
469  if (parg != params.end()) {
470  args.push_back(*parg);
471  ++parg;
472  }
473 
474  if (barg != m_args.end() && barg->get_type_info() == chaiscript::detail::Get_Type_Info<Placeholder_Object>::get()) {
475  ++barg;
476  }
477  }
478  return args;
479  }
480 
481  protected:
482  static std::vector<Type_Info> build_param_type_info(const Const_Proxy_Function &t_f, const std::vector<Boxed_Value> &t_args) {
483  assert(t_f->get_arity() < 0 || t_f->get_arity() == static_cast<int>(t_args.size()));
484 
485  if (t_f->get_arity() < 0) {
486  return std::vector<Type_Info>();
487  }
488 
489  const auto types = t_f->get_param_types();
490  assert(types.size() == t_args.size() + 1);
491 
492  // this analysis warning is invalid in MSVC12 and doesn't exist in MSVC14
493  std::vector<Type_Info> retval{types[0]};
494 
495  for (size_t i = 0; i < types.size() - 1; ++i) {
496  if (t_args[i].get_type_info() == chaiscript::detail::Get_Type_Info<Placeholder_Object>::get()) {
497  retval.push_back(types[i + 1]);
498  }
499  }
500 
501  return retval;
502  }
503 
504  Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const override {
505  return (*m_f)(Function_Params{build_param_list(params)}, t_conversions);
506  }
507 
508  private:
510  std::vector<Boxed_Value> m_args;
511  };
512 
514  public:
515  explicit Proxy_Function_Impl_Base(const std::vector<Type_Info> &t_types)
516  : Proxy_Function_Base(t_types, static_cast<int>(t_types.size()) - 1) {
517  }
518 
519  bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override {
520  return static_cast<int>(vals.size()) == get_arity()
521  && (compare_types(m_types, vals, t_conversions) && compare_types_with_cast(vals, t_conversions));
522  }
523 
524  virtual bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept = 0;
525  };
526 
528  template<typename Func, typename Callable>
530  public:
531  explicit Proxy_Function_Callable_Impl(Callable f)
532  : Proxy_Function_Impl_Base(detail::build_param_type_list(static_cast<Func *>(nullptr)))
533  , m_f(std::move(f)) {
534  }
535 
536  bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override {
537  return detail::compare_types_cast(static_cast<Func *>(nullptr), vals, t_conversions);
538  }
539 
540  bool operator==(const Proxy_Function_Base &t_func) const noexcept override {
541  return dynamic_cast<const Proxy_Function_Callable_Impl<Func, Callable> *>(&t_func) != nullptr;
542  }
543 
544  protected:
545  Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const override {
546  return detail::call_func(static_cast<Func *>(nullptr), m_f, params, t_conversions);
547  }
548 
549  private:
550  Callable m_f;
551  };
552 
554  public:
555  explicit Assignable_Proxy_Function(const std::vector<Type_Info> &t_types)
556  : Proxy_Function_Impl_Base(t_types) {
557  }
558 
559  virtual void assign(const std::shared_ptr<const Proxy_Function_Base> &t_rhs) = 0;
560  };
561 
562  template<typename Func>
564  public:
565  Assignable_Proxy_Function_Impl(std::reference_wrapper<std::function<Func>> t_f, std::shared_ptr<std::function<Func>> t_ptr)
566  : Assignable_Proxy_Function(detail::build_param_type_list(static_cast<Func *>(nullptr)))
567  , m_f(std::move(t_f))
568  , m_shared_ptr_holder(std::move(t_ptr)) {
569  assert(!m_shared_ptr_holder || m_shared_ptr_holder.get() == &m_f.get());
570  }
571 
572  bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override {
573  return detail::compare_types_cast(static_cast<Func *>(nullptr), vals, t_conversions);
574  }
575 
576  bool operator==(const Proxy_Function_Base &t_func) const noexcept override {
577  return dynamic_cast<const Assignable_Proxy_Function_Impl<Func> *>(&t_func) != nullptr;
578  }
579 
580  std::function<Func> internal_function() const { return m_f.get(); }
581 
582  void assign(const std::shared_ptr<const Proxy_Function_Base> &t_rhs) override { m_f.get() = dispatch::functor<Func>(t_rhs, nullptr); }
583 
584  protected:
585  Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const override {
586  return detail::call_func(static_cast<Func *>(nullptr), m_f.get(), params, t_conversions);
587  }
588 
589  private:
590  std::reference_wrapper<std::function<Func>> m_f;
591  std::shared_ptr<std::function<Func>> m_shared_ptr_holder;
592  };
593 
595  template<typename T, typename Class>
596  class Attribute_Access final : public Proxy_Function_Base {
597  public:
598  explicit Attribute_Access(T Class::*t_attr)
599  : Proxy_Function_Base(param_types(), 1)
600  , m_attr(t_attr) {
601  }
602 
603  bool is_attribute_function() const noexcept override { return true; }
604 
605  bool operator==(const Proxy_Function_Base &t_func) const noexcept override {
606  const Attribute_Access<T, Class> *aa = dynamic_cast<const Attribute_Access<T, Class> *>(&t_func);
607 
608  if (aa) {
609  return m_attr == aa->m_attr;
610  } else {
611  return false;
612  }
613  }
614 
615  bool call_match(const Function_Params &vals, const Type_Conversions_State &) const noexcept override {
616  if (vals.size() != 1) {
617  return false;
618  }
619  const auto class_type_info = user_type<Class>();
620  return vals[0].get_type_info().bare_equal(class_type_info);
621  }
622 
623  protected:
624  Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const override {
625  const Boxed_Value &bv = params[0];
626  if (bv.is_const()) {
627  const Class *o = boxed_cast<const Class *>(bv, &t_conversions);
628  return do_call_impl<T>(o);
629  } else {
630  Class *o = boxed_cast<Class *>(bv, &t_conversions);
631  return do_call_impl<T>(o);
632  }
633  }
634 
635  private:
636  template<typename Type>
637  auto do_call_impl(Class *o) const {
638  if constexpr (std::is_pointer<Type>::value) {
639  return detail::Handle_Return<Type>::handle(o->*m_attr);
640  } else {
642  }
643  }
644 
645  template<typename Type>
646  auto do_call_impl(const Class *o) const {
647  if constexpr (std::is_pointer<Type>::value) {
649  } else {
651  }
652  }
653 
654  static std::vector<Type_Info> param_types() { return {user_type<T>(), user_type<Class>()}; }
655 
656  std::vector<Type_Info> m_param_types{user_type<T>(), user_type<Class>()};
657  T Class::*m_attr;
658  };
659  } // namespace dispatch
660 
661  namespace exception {
667  class dispatch_error : public std::runtime_error {
668  public:
669  dispatch_error(const Function_Params &t_parameters, std::vector<Const_Proxy_Function> t_functions)
670  : std::runtime_error("Error with function dispatch")
671  , parameters(t_parameters.to_vector())
672  , functions(std::move(t_functions)) {
673  }
674 
675  dispatch_error(const Function_Params &t_parameters, std::vector<Const_Proxy_Function> t_functions, const std::string &t_desc)
676  : std::runtime_error(t_desc)
677  , parameters(t_parameters.to_vector())
678  , functions(std::move(t_functions)) {
679  }
680 
681  dispatch_error(const dispatch_error &) = default;
682  ~dispatch_error() noexcept override = default;
683 
684  std::vector<Boxed_Value> parameters;
685  std::vector<Const_Proxy_Function> functions;
686  };
687  } // namespace exception
688 
689  namespace dispatch {
690  namespace detail {
691  template<typename FuncType>
692  bool types_match_except_for_arithmetic(const FuncType &t_func,
693  const chaiscript::Function_Params &plist,
694  const Type_Conversions_State &t_conversions) noexcept {
695  const std::vector<Type_Info> &types = t_func->get_param_types();
696 
697  if (t_func->get_arity() == -1) {
698  return false;
699  }
700 
701  assert(plist.size() == types.size() - 1);
702 
703  return std::mismatch(plist.begin(),
704  plist.end(),
705  types.begin() + 1,
706  [&](const Boxed_Value &bv, const Type_Info &ti) {
707  return Proxy_Function_Base::compare_type_to_param(ti, bv, t_conversions)
708  || (bv.get_type_info().is_arithmetic() && ti.is_arithmetic());
709  })
710  == std::make_pair(plist.end(), types.end());
711  }
712 
713  template<typename InItr, typename Funcs>
714  Boxed_Value dispatch_with_conversions(InItr begin,
715  const InItr &end,
716  const chaiscript::Function_Params &plist,
717  const Type_Conversions_State &t_conversions,
718  const Funcs &t_funcs) {
719  InItr matching_func(end);
720 
721  while (begin != end) {
722  if (types_match_except_for_arithmetic(begin->second, plist, t_conversions)) {
723  if (matching_func == end) {
724  matching_func = begin;
725  } else {
726  // handle const members vs non-const member, which is not really ambiguous
727  const auto &mat_fun_param_types = matching_func->second->get_param_types();
728  const auto &next_fun_param_types = begin->second->get_param_types();
729 
730  if (plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) {
731  matching_func = begin; // keep the new one, the const/non-const matchup is correct
732  } else if (!plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) {
733  // keep the old one, it has a better const/non-const matchup
734  } else {
735  // ambiguous function call
736  throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(t_funcs.begin(), t_funcs.end()));
737  }
738  }
739  }
740 
741  ++begin;
742  }
743 
744  if (matching_func == end) {
745  // no appropriate function to attempt arithmetic type conversion on
746  throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(t_funcs.begin(), t_funcs.end()));
747  }
748 
749  std::vector<Boxed_Value> newplist;
750  newplist.reserve(plist.size());
751 
752  const std::vector<Type_Info> &tis = matching_func->second->get_param_types();
753  std::transform(tis.begin() + 1, tis.end(), plist.begin(), std::back_inserter(newplist), [](const Type_Info &ti, const Boxed_Value &param) -> Boxed_Value {
754  if (ti.is_arithmetic() && param.get_type_info().is_arithmetic() && param.get_type_info() != ti) {
755  return Boxed_Number(param).get_as(ti).bv;
756  } else {
757  return param;
758  }
759  });
760 
761  try {
762  return (*(matching_func->second))(chaiscript::Function_Params{newplist}, t_conversions);
763  } catch (const exception::bad_boxed_cast &) {
764  // parameter failed to cast
765  } catch (const exception::arity_error &) {
766  // invalid num params
767  } catch (const exception::guard_error &) {
768  // guard failed to allow the function to execute
769  }
770 
771  throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(t_funcs.begin(), t_funcs.end()));
772  }
773  } // namespace detail
774 
778  template<typename Funcs>
779  Boxed_Value dispatch(const Funcs &funcs, const Function_Params &plist, const Type_Conversions_State &t_conversions) {
780  std::vector<std::pair<size_t, const Proxy_Function_Base *>> ordered_funcs;
781  ordered_funcs.reserve(funcs.size());
782 
783  for (const auto &func : funcs) {
784  const auto arity = func->get_arity();
785 
786  if (arity == -1) {
787  ordered_funcs.emplace_back(plist.size(), func.get());
788  } else if (arity == static_cast<int>(plist.size())) {
789  size_t numdiffs = 0;
790  for (size_t i = 0; i < plist.size(); ++i) {
791  if (!func->get_param_types()[i + 1].bare_equal(plist[i].get_type_info())) {
792  ++numdiffs;
793  }
794  }
795  ordered_funcs.emplace_back(numdiffs, func.get());
796  }
797  }
798 
799  for (size_t i = 0; i <= plist.size(); ++i) {
800  for (const auto &func : ordered_funcs) {
801  try {
802  if (func.first == i && (i == 0 || func.second->filter(plist, t_conversions))) {
803  return (*(func.second))(plist, t_conversions);
804  }
805  } catch (const exception::bad_boxed_cast &) {
806  // parameter failed to cast, try again
807  } catch (const exception::arity_error &) {
808  // invalid num params, try again
809  } catch (const exception::guard_error &) {
810  // guard failed to allow the function to execute,
811  // try again
812  }
813  }
814  }
815 
816  return detail::dispatch_with_conversions(ordered_funcs.cbegin(), ordered_funcs.cend(), plist, t_conversions, funcs);
817  }
818  } // namespace dispatch
819 } // namespace chaiscript
820 
821 #endif
Compile time deduced information about a type.
Definition: type_info.hpp:27
const std::vector< Type_Info > & get_param_types() const noexcept
Returns a vector containing all of the types of the parameters the function returns/takes if the func...
Definition: proxy_functions.hpp:191
Helper used to create a Type_Info object.
Definition: type_info.hpp:106
Definition: proxy_functions.hpp:553
bool filter(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept
Return true if the function is a possible match to the passed in values.
Definition: proxy_functions.hpp:206
decltype(auto) boxed_cast(const Boxed_Value &bv, const Type_Conversions_State *t_conversions=nullptr)
Function for extracting a value stored in a Boxed_Value object.
Definition: boxed_cast.hpp:71
Definition: type_conversions.hpp:417
Exception thrown if a function&#39;s guard fails.
Definition: proxy_functions.hpp:285
Namespace chaiscript contains every API call that the average user will be concerned with...
Definition: function_params.hpp:16
Definition: proxy_functions.hpp:395
An object used by Bound_Function to represent "_" parameters of a binding.
Definition: proxy_functions.hpp:432
An implementation of Proxy_Function that takes a Proxy_Function and substitutes bound parameters into...
Definition: proxy_functions.hpp:439
Pure virtual base class for all Proxy_Function implementations Proxy_Functions are a type erasure of ...
Definition: proxy_functions.hpp:175
A wrapper for holding any valid C++ type.
Definition: boxed_value.hpp:24
Thrown in the event that a Boxed_Value cannot be cast to the desired type.
Definition: bad_boxed_cast.hpp:31
Definition: proxy_functions.hpp:48
Boxed_Value dispatch(const Funcs &funcs, const Function_Params &plist, const Type_Conversions_State &t_conversions)
Take a vector of functions and a vector of parameters.
Definition: proxy_functions.hpp:779
Used internally for handling a return value from a Proxy_Function call.
Definition: handle_return.hpp:34
Struct that doubles as both a parser ast_node and an AST node.
Definition: chaiscript_common.hpp:174
std::shared_ptr< const dispatch::Proxy_Function_Base > Const_Proxy_Function
Const version of Proxy_Function.
Definition: proxy_functions.hpp:281
Exception thrown when there is a mismatch in number of parameters during Proxy_Function execution...
Definition: proxy_functions_detail.hpp:38
std::shared_ptr< dispatch::Proxy_Function_Base > Proxy_Function
Common typedef used for passing of any registered function in ChaiScript.
Definition: proxy_functions.hpp:277
int get_arity() const noexcept
Definition: proxy_functions.hpp:219
Definition: dynamic_object.hpp:38
Thrown in the event that an Any cannot be cast to the desired type.
Definition: any.hpp:20
Attribute getter Proxy_Function implementation.
Definition: proxy_functions.hpp:596
Exception thrown in the case that a method dispatch fails because no matching function was found...
Definition: proxy_functions.hpp:667
Represents any numeric type, generically. Used internally for generic operations between POD values...
Definition: boxed_number.hpp:60
Definition: proxy_functions.hpp:513
std::unique_ptr< AST_Node > AST_NodePtr
Typedef for pointers to AST_Node objects. Used in building of the AST_Node tree.
Definition: proxy_functions.hpp:42
For any callable object.
Definition: handle_return.hpp:27
virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const noexcept
Definition: proxy_functions.hpp:236
A Proxy_Function implementation that is not type safe, the called function is expecting a vector<Boxe...
Definition: proxy_functions.hpp:300