ChaiScript / ChaiScript
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

11
#ifndef CHAISCRIPT_PROXY_FUNCTIONS_HPP_
12
#define CHAISCRIPT_PROXY_FUNCTIONS_HPP_
13

14

15
#include <cassert>
16
#include <functional>
17
#include <memory>
18
#include <stdexcept>
19
#include <string>
20
#include <type_traits>
21
#include <vector>
22
#include <iterator>
23

24
#include "../chaiscript_defines.hpp"
25
#include "boxed_cast.hpp"
26
#include "boxed_value.hpp"
27
#include "proxy_functions_detail.hpp"
28
#include "type_info.hpp"
29
#include "dynamic_object.hpp"
30
#include "function_params.hpp"
31

32
namespace chaiscript {
33
class Type_Conversions;
34
namespace exception {
35
class bad_boxed_cast;
36
struct arity_error;
37
}  // namespace exception
38
}  // namespace chaiscript
39

40
namespace chaiscript
41
{
42
  class Boxed_Number;
43
  struct AST_Node;
44

45
  using AST_NodePtr = std::unique_ptr<AST_Node>;
46

47
  namespace dispatch
48
  {
49
    template<typename FunctionType>
50
      std::function<FunctionType> functor(std::shared_ptr<const Proxy_Function_Base> func, const Type_Conversions_State *t_conversions);
51

52
    class Param_Types
53
    {
54
      public:
55 1
        Param_Types()
56 1
          : m_has_types(false)
57 1
        {}
58

59 1
        explicit Param_Types(std::vector<std::pair<std::string, Type_Info>> t_types)
60 1
          : m_types(std::move(t_types)),
61 1
            m_has_types(false)
62
        {
63 1
          update_has_types();
64
        }
65

66 1
        void push_front(std::string t_name, Type_Info t_ti)
67
        {
68 1
          m_types.emplace(m_types.begin(), std::move(t_name), t_ti);
69 1
          update_has_types();
70
        }
71

72 1
        bool operator==(const Param_Types &t_rhs) const noexcept
73
        {
74 1
          return m_types == t_rhs.m_types;
75
        }
76

77 1
        std::vector<Boxed_Value> convert(Function_Params t_params, const Type_Conversions_State &t_conversions) const
78
        {
79 1
          auto vals = t_params.to_vector();
80 1
          const auto dynamic_object_type_info = user_type<Dynamic_Object>();
81 1
          for (size_t i = 0; i < vals.size(); ++i)
82
          {
83 1
            const auto &name = m_types[i].first;
84 1
            if (!name.empty()) {
85 1
              const auto &bv = vals[i];
86

87 1
              if (!bv.get_type_info().bare_equal(dynamic_object_type_info))
88
              {
89 1
                const auto &ti = m_types[i].second;
90 1
                if (!ti.is_undef())
91
                {
92 1
                  if (!bv.get_type_info().bare_equal(ti)) {
93 1
                    if (t_conversions->converts(ti, bv.get_type_info())) {
94
                      try {
95
                        // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it
96
                        // either way, we are not responsible if it doesn't work
97 0
                        vals[i] = t_conversions->boxed_type_conversion(m_types[i].second, t_conversions.saves(), vals[i]);
98 1
                      } catch (...) {
99
                        try {
100
                          // try going the other way
101 1
                          vals[i] = t_conversions->boxed_type_down_conversion(m_types[i].second, t_conversions.saves(), vals[i]);
102 0
                        } catch (const chaiscript::detail::exception::bad_any_cast &) {
103 0
                          throw exception::bad_boxed_cast(bv.get_type_info(), *m_types[i].second.bare_type_info());
104
                        }
105
                      }
106
                    }
107
                  }
108
                }
109
              }
110
            }
111
          }
112

113 1
          return vals;
114
        }
115

116
        // first result: is a match
117
        // second result: needs conversions
118 1
        std::pair<bool, bool> match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept
119
        {
120 1
          const auto dynamic_object_type_info = user_type<Dynamic_Object>();
121 1
          bool needs_conversion = false;
122

123 1
          if (!m_has_types) { return std::make_pair(true, needs_conversion); }
124 0
          if (vals.size() != m_types.size()) { return std::make_pair(false, needs_conversion); }
125

126 1
          for (size_t i = 0; i < vals.size(); ++i)
127
          {
128 1
            const auto &name = m_types[i].first;
129 1
            if (!name.empty()) {
130 1
              const auto &bv = vals[i];
131

132 1
              if (bv.get_type_info().bare_equal(dynamic_object_type_info))
133
              {
134
                try {
135 1
                  const Dynamic_Object &d = boxed_cast<const Dynamic_Object &>(bv, &t_conversions);
136 0
                  if (!(name == "Dynamic_Object" || d.get_type_name() == name)) {
137 0
                    return std::make_pair(false, false);
138
                  }
139 0
                } catch (const std::bad_cast &) {
140 0
                  return std::make_pair(false, false);
141
                }
142
              } else {
143 1
                const auto &ti = m_types[i].second;
144 1
                if (!ti.is_undef())
145
                {
146 1
                  if (!bv.get_type_info().bare_equal(ti)) {
147 1
                    if (!t_conversions->converts(ti, bv.get_type_info())) {
148 1
                      return std::make_pair(false, false);
149
                    } else {
150 1
                      needs_conversion = true;
151
                    }
152
                  }
153
                } else {
154 1
                  return std::make_pair(false, false);
155
                }
156
              }
157
            }
158
          }
159

160 1
          return std::make_pair(true, needs_conversion);
161
        }
162

163 1
        const std::vector<std::pair<std::string, Type_Info>> &types() const noexcept
164
        {
165 1
          return m_types;
166
        }
167

168
      private:
169 1
        void update_has_types()
170
        {
171 1
          for (const auto &type : m_types)
172
          {
173 1
            if (!type.first.empty())
174
            {
175 1
              m_has_types = true;
176 1
              return;
177
            }
178
          }
179

180 1
          m_has_types = false;
181
        }
182

183
        std::vector<std::pair<std::string, Type_Info>> m_types;
184
        bool m_has_types;
185

186
    };
187

188
    /**
189
     * Pure virtual base class for all Proxy_Function implementations
190
     * Proxy_Functions are a type erasure of type safe C++
191
     * function calls. At runtime parameter types are expected to be
192
     * tested against passed in types.
193
     * Dispatch_Engine only knows how to work with Proxy_Function, no other
194
     * function classes.
195
     */
196
    class Proxy_Function_Base
197
    {
198
      public:
199 1
        virtual ~Proxy_Function_Base() = default;
200

201 1
        Boxed_Value operator()(const Function_Params &params, const chaiscript::Type_Conversions_State &t_conversions) const
202
        {
203 1
          if (m_arity < 0 || size_t(m_arity) == params.size()) {
204 1
            return do_call(params, t_conversions);
205
          } else {
206 1
            throw exception::arity_error(static_cast<int>(params.size()), m_arity);
207
          }
208
        }
209

210
        /// Returns a vector containing all of the types of the parameters the function returns/takes
211
        /// if the function is variadic or takes no arguments (arity of 0 or -1), the returned
212
        /// value contains exactly 1 Type_Info object: the return type
213
        /// \returns the types of all parameters.
214 1
        const std::vector<Type_Info> &get_param_types() const noexcept { return m_types; }
215

216
        virtual bool operator==(const Proxy_Function_Base &) const noexcept = 0;
217
        virtual bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const = 0;
218

219 1
        virtual bool is_attribute_function() const noexcept { return false; }
220

221 1
        bool has_arithmetic_param() const noexcept
222
        {
223 1
          return m_has_arithmetic_param;
224
        }
225

226 1
        virtual std::vector<std::shared_ptr<const Proxy_Function_Base> > get_contained_functions() const
227
        {
228 1
          return std::vector<std::shared_ptr<const Proxy_Function_Base> >();
229
        }
230

231
        //! Return true if the function is a possible match
232
        //! to the passed in values
233 1
        bool filter(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept
234
        {
235 0
          assert(m_arity == -1 || (m_arity > 0 && static_cast<int>(vals.size()) == m_arity));
236

237 1
          if (m_arity < 0)
238
          {
239 1
            return true;
240 1
          } else if (m_arity > 1) {
241 1
            return compare_type_to_param(m_types[1], vals[0], t_conversions) && compare_type_to_param(m_types[2], vals[1], t_conversions);
242
          } else {
243 1
            return compare_type_to_param(m_types[1], vals[0], t_conversions);
244
          }
245
        }
246

247
        /// \returns the number of arguments the function takes or -1 if it is variadic
248 1
        int get_arity() const noexcept
249
        {
250 1
          return m_arity;
251
        }
252

253 1
        static bool compare_type_to_param(const Type_Info &ti, const Boxed_Value &bv, const Type_Conversions_State &t_conversions) noexcept
254
        {
255 1
          const auto boxed_value_ti = user_type<Boxed_Value>();
256 1
          const auto boxed_number_ti = user_type<Boxed_Number>();
257 1
          const auto function_ti = user_type<std::shared_ptr<const Proxy_Function_Base>>();
258

259 1
          if (ti.is_undef()
260 1
              || ti.bare_equal(boxed_value_ti)
261 1
              || (!bv.get_type_info().is_undef()
262 1
                && ( (ti.bare_equal(boxed_number_ti) && bv.get_type_info().is_arithmetic())
263 1
                  || ti.bare_equal(bv.get_type_info())
264 1
                  || bv.get_type_info().bare_equal(function_ti)
265 1
                  || t_conversions->converts(ti, bv.get_type_info())
266
                  )
267
                )
268
             )
269
          {
270 1
            return true;
271
          } else {
272 1
            return false;
273
          }
274
        }
275

276 1
        virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const noexcept
277
        {
278
          /// TODO is m_types guaranteed to be at least 2??
279 1
          return compare_type_to_param(m_types[1], bv, t_conversions);
280
        }
281

282
      protected:
283
        virtual Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const = 0;
284

285 1
        Proxy_Function_Base(std::vector<Type_Info> t_types, int t_arity)
286 1
          : m_types(std::move(t_types)), m_arity(t_arity), m_has_arithmetic_param(false)
287
        {
288 1
          for (size_t i = 1; i < m_types.size(); ++i)
289
          {
290 1
            if (m_types[i].is_arithmetic())
291
            {
292 1
              m_has_arithmetic_param = true;
293 1
              return;
294
            }
295
          }
296

297
        }
298

299

300 1
        static bool compare_types(const std::vector<Type_Info> &tis, const Function_Params &bvs,
301
                                  const Type_Conversions_State &t_conversions) noexcept
302
        {
303 1
          if (tis.size() - 1 != bvs.size())
304
          {
305 0
            return false;
306
          } else {
307 1
            const size_t size = bvs.size();
308 1
            for (size_t i = 0; i < size; ++i)
309
            {
310 1
              if (!compare_type_to_param(tis[i + 1], bvs[i], t_conversions)) { return false;  }
311
            }
312
          }
313 1
          return true;
314
        }
315

316
        std::vector<Type_Info> m_types;
317
        int m_arity;
318
        bool m_has_arithmetic_param;
319
    };
320
  }
321

322
  /// \brief Common typedef used for passing of any registered function in ChaiScript
323
  using Proxy_Function = std::shared_ptr<dispatch::Proxy_Function_Base>;
324

325
  /// \brief Const version of Proxy_Function. Points to a const Proxy_Function. This is how most registered functions
326
  ///        are handled internally.
327
  using Const_Proxy_Function = std::shared_ptr<const dispatch::Proxy_Function_Base>;
328

329
  namespace exception
330
  {
331
    /// \brief  Exception thrown if a function's guard fails
332
    class guard_error : public std::runtime_error
333
    {
334
      public:
335 1
        guard_error() noexcept
336 1
          : std::runtime_error("Guard evaluation failed")
337 1
        { }
338

339
        guard_error(const guard_error &) = default;
340

341 1
        ~guard_error() noexcept override = default;
342
    };
343
  }
344

345
  namespace dispatch
346
  {
347
    /// A Proxy_Function implementation that is not type safe, the called function
348
    /// is expecting a vector<Boxed_Value> that it works with how it chooses.
349
    class Dynamic_Proxy_Function : public Proxy_Function_Base
350
    {
351
      public:
352 1
        Dynamic_Proxy_Function(
353
            const int t_arity,
354
            std::shared_ptr<AST_Node> t_parsenode,
355
            Param_Types t_param_types = Param_Types(),
356
            Proxy_Function t_guard = Proxy_Function())
357 1
          : Proxy_Function_Base(build_param_type_list(t_param_types), t_arity),
358 1
            m_param_types(std::move(t_param_types)),
359 1
            m_guard(std::move(t_guard)), m_parsenode(std::move(t_parsenode))
360
        {
361
          // assert(t_parsenode);
362
        }
363

364

365 1
        bool operator==(const Proxy_Function_Base &rhs) const noexcept override
366
        {
367 0
          const Dynamic_Proxy_Function *prhs = dynamic_cast<const Dynamic_Proxy_Function *>(&rhs);
368

369 1
          return this == &rhs
370 1
            || ((prhs != nullptr)
371 1
                && this->m_arity == prhs->m_arity
372 1
                && !this->m_guard && !prhs->m_guard
373 1
                && this->m_param_types == prhs->m_param_types);
374
        }
375

376 1
        bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const override
377
        {
378 1
          return call_match_internal(vals, t_conversions).first;
379
        }
380

381 1
        bool has_guard() const noexcept
382
        {
383 1
          return bool(m_guard);
384
        }
385

386 1
        Proxy_Function get_guard() const noexcept
387
        {
388 1
          return m_guard;
389
        }
390

391 1
        bool has_parse_tree() const noexcept {
392 1
          return static_cast<bool>(m_parsenode);
393
        }
394

395 1
        const AST_Node &get_parse_tree() const
396
        {
397 1
          if (m_parsenode) {
398 1
            return *m_parsenode;
399
          } else {
400 0
            throw std::runtime_error("Dynamic_Proxy_Function does not have parse_tree");
401
          }
402
        }
403

404

405
      protected:
406 1
        bool test_guard(const Function_Params &params, const Type_Conversions_State &t_conversions) const
407
        {
408 1
          if (m_guard)
409
          {
410
            try {
411 1
              return boxed_cast<bool>((*m_guard)(params, t_conversions));
412 0
            } catch (const exception::arity_error &) {
413 0
              return false;
414 1
            } catch (const exception::bad_boxed_cast &) {
415 1
              return false;
416
            }
417
          } else {
418 1
            return true;
419
          }
420
        }
421

422
        // first result: is a match
423
        // second result: needs conversions
424 1
        std::pair<bool, bool> call_match_internal(const Function_Params &vals, const Type_Conversions_State &t_conversions) const
425
        {
426
          const auto comparison_result = [&](){
427
            if (m_arity < 0) {
428
              return std::make_pair(true, false);
429
            } else if (vals.size() == size_t(m_arity)) {
430
              return m_param_types.match(vals, t_conversions);
431
            } else {
432 0
              return std::make_pair(false, false);
433
            }
434 1
          }();
435

436
          return std::make_pair(
437 1
              comparison_result.first && test_guard(vals, t_conversions),
438
              comparison_result.second
439 1
              );
440
        }
441

442
      private:
443 1
        static std::vector<Type_Info> build_param_type_list(const Param_Types &t_types)
444
        {
445
          // For the return type
446 1
          std::vector<Type_Info> types{chaiscript::detail::Get_Type_Info<Boxed_Value>::get()};
447

448 1
          for (const auto &t : t_types.types())
449
          {
450 1
            if (t.second.is_undef()) {
451 1
              types.push_back(chaiscript::detail::Get_Type_Info<Boxed_Value>::get());
452
            } else {
453 1
              types.push_back(t.second);
454
            }
455
          }
456

457 1
          return types;
458
        }
459

460
      protected:
461
        Param_Types m_param_types;
462

463
      private:
464
        Proxy_Function m_guard;
465
        std::shared_ptr<AST_Node> m_parsenode;
466
    };
467

468

469

470
    template<typename Callable>
471
    class Dynamic_Proxy_Function_Impl final : public Dynamic_Proxy_Function
472
    {
473
      public:
474 1
        Dynamic_Proxy_Function_Impl(
475
            Callable t_f,
476
            int t_arity=-1,
477
            std::shared_ptr<AST_Node> t_parsenode = AST_NodePtr(),
478
            Param_Types t_param_types = Param_Types(),
479
            Proxy_Function t_guard = Proxy_Function())
480
          : Dynamic_Proxy_Function(
481
                t_arity,
482 1
                std::move(t_parsenode),
483 1
                std::move(t_param_types),
484 1
                std::move(t_guard)
485
              ),
486 1
            m_f(std::move(t_f))
487
        {
488
        }
489

490

491
      protected:
492 1
        Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const override
493
        {
494 1
          const auto [is_a_match, needs_conversions] = call_match_internal(params, t_conversions);
495 1
          if (is_a_match)
496
          {
497 1
            if (needs_conversions) {
498 1
              return m_f(Function_Params{m_param_types.convert(params, t_conversions)});
499
            } else {
500 1
              return m_f(params);
501
            }
502
          } else {
503 1
            throw exception::guard_error();
504
          }
505
        }
506

507
      private:
508
        Callable m_f;
509
    };
510

511
    template<typename Callable, typename ... Arg>
512 1
    Proxy_Function make_dynamic_proxy_function(Callable &&c, Arg&& ... a)
513
    {
514
      return chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Dynamic_Proxy_Function_Impl<Callable>>(
515 1
          std::forward<Callable>(c), std::forward<Arg>(a)...);
516
    }
517

518
    /// An object used by Bound_Function to represent "_" parameters
519
    /// of a binding. This allows for unbound parameters during bind.
520
    struct Placeholder_Object
521
    {
522
    };
523

524
    /// An implementation of Proxy_Function that takes a Proxy_Function
525
    /// and substitutes bound parameters into the parameter list
526
    /// at runtime, when call() is executed.
527
    /// it is used for bind(function, param1, _, param2) style calls
528
    class Bound_Function final : public Proxy_Function_Base
529
    {
530
      public:
531 1
        Bound_Function(const Const_Proxy_Function &t_f,
532
            const std::vector<Boxed_Value> &t_args)
533 1
          : Proxy_Function_Base(build_param_type_info(t_f, t_args), (t_f->get_arity()<0?-1:static_cast<int>(build_param_type_info(t_f, t_args).size())-1)),
534 0
            m_f(t_f), m_args(t_args)
535
        {
536 0
          assert(m_f->get_arity() < 0 || m_f->get_arity() == static_cast<int>(m_args.size()));
537
        }
538

539 0
        bool operator==(const Proxy_Function_Base &t_f) const noexcept override
540
        {
541 0
          return &t_f == this;
542
        }
543

544

545 0
        bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const override
546
        {
547 0
          return m_f->call_match(Function_Params(build_param_list(vals)), t_conversions);
548
        }
549

550 0
        std::vector<Const_Proxy_Function> get_contained_functions() const override
551
        {
552 0
          return std::vector<Const_Proxy_Function>{m_f};
553
        }
554

555

556 1
        std::vector<Boxed_Value> build_param_list(const Function_Params &params) const
557
        {
558 1
          auto parg = params.begin();
559 1
          auto barg = m_args.begin();
560

561 1
          std::vector<Boxed_Value> args;
562

563 1
          while (!(parg == params.end() && barg == m_args.end()))
564
          {
565 1
            while (barg != m_args.end()
566 1
                && !(barg->get_type_info() == chaiscript::detail::Get_Type_Info<Placeholder_Object>::get()))
567
            {
568 1
              args.push_back(*barg);
569 1
              ++barg;
570
            }
571

572 1
            if (parg != params.end())
573
            {
574 1
              args.push_back(*parg);
575 1
              ++parg;
576
            }
577

578 1
            if (barg != m_args.end()
579 1
                && barg->get_type_info() == chaiscript::detail::Get_Type_Info<Placeholder_Object>::get())
580
            {
581 1
              ++barg;
582
            }
583
          }
584 1
          return args;
585
        }
586

587

588
      protected:
589 1
        static std::vector<Type_Info> build_param_type_info(const Const_Proxy_Function &t_f,
590
            const std::vector<Boxed_Value> &t_args)
591
        {
592 0
          assert(t_f->get_arity() < 0 || t_f->get_arity() == static_cast<int>(t_args.size()));
593

594 0
          if (t_f->get_arity() < 0) { return std::vector<Type_Info>(); }
595

596 1
          const auto types = t_f->get_param_types();
597 0
          assert(types.size() == t_args.size() + 1);
598

599
          // this analysis warning is invalid in MSVC12 and doesn't exist in MSVC14
600 1
          std::vector<Type_Info> retval{types[0]};
601

602 1
          for (size_t i = 0; i < types.size() - 1; ++i)
603
          {
604 1
            if (t_args[i].get_type_info() == chaiscript::detail::Get_Type_Info<Placeholder_Object>::get())
605
            {
606 1
              retval.push_back(types[i+1]);
607
            }
608
          }
609

610 1
          return retval;
611
        }
612

613 1
        Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const override
614
        {
615 1
          return (*m_f)(Function_Params{build_param_list(params)}, t_conversions);
616
        }
617

618
      private:
619
        Const_Proxy_Function m_f;
620
        std::vector<Boxed_Value> m_args;
621
    };
622

623
    class Proxy_Function_Impl_Base : public Proxy_Function_Base
624
    {
625
      public:
626 1
        explicit Proxy_Function_Impl_Base(const std::vector<Type_Info> &t_types)
627 1
          : Proxy_Function_Base(t_types, static_cast<int>(t_types.size()) - 1)
628
        {
629
        }
630

631 1
        bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override
632
        {
633 1
          return static_cast<int>(vals.size()) == get_arity()
634 1
            && (compare_types(m_types, vals, t_conversions) && compare_types_with_cast(vals, t_conversions));
635
        }
636

637
        virtual bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept = 0;
638
    };
639

640

641

642
    /// For any callable object
643
    template<typename Func, typename Callable>
644
      class Proxy_Function_Callable_Impl final : public Proxy_Function_Impl_Base
645
    {
646
      public:
647 1
        explicit Proxy_Function_Callable_Impl(Callable f)
648
          : Proxy_Function_Impl_Base(detail::build_param_type_list(static_cast<Func *>(nullptr))),
649 1
            m_f(std::move(f))
650
        {
651
        }
652

653 1
        bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override
654
        {
655 1
          return detail::compare_types_cast(static_cast<Func *>(nullptr), vals, t_conversions);
656
        }
657

658 1
        bool operator==(const Proxy_Function_Base &t_func) const noexcept override
659
        {
660 0
          return dynamic_cast<const Proxy_Function_Callable_Impl<Func, Callable> *>(&t_func) != nullptr;
661
        }
662

663

664
      protected:
665 1
        Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const override
666
        {
667 1
          return detail::call_func(static_cast<Func *>(nullptr), m_f, params, t_conversions);
668
        }
669

670
      private:
671
        Callable m_f;
672
    };
673

674

675
    class Assignable_Proxy_Function : public Proxy_Function_Impl_Base
676
    {
677
      public:
678 1
        explicit Assignable_Proxy_Function(const std::vector<Type_Info> &t_types)
679 1
          : Proxy_Function_Impl_Base(t_types)
680
        {
681
        }
682

683
        virtual void assign(const std::shared_ptr<const Proxy_Function_Base> &t_rhs) = 0;
684
    };
685

686
    template<typename Func>
687
      class Assignable_Proxy_Function_Impl final : public Assignable_Proxy_Function
688
    {
689
      public:
690 1
        Assignable_Proxy_Function_Impl(std::reference_wrapper<std::function<Func>> t_f, std::shared_ptr<std::function<Func>> t_ptr)
691
          : Assignable_Proxy_Function(detail::build_param_type_list(static_cast<Func *>(nullptr))),
692 1
            m_f(std::move(t_f)), m_shared_ptr_holder(std::move(t_ptr))
693
        {
694
          assert(!m_shared_ptr_holder || m_shared_ptr_holder.get() == &m_f.get());
695
        }
696

697 0
        bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override
698
        {
699 0
          return detail::compare_types_cast(static_cast<Func *>(nullptr), vals, t_conversions);
700
        }
701

702 0
        bool operator==(const Proxy_Function_Base &t_func) const noexcept override
703
        {
704 0
          return dynamic_cast<const Assignable_Proxy_Function_Impl<Func> *>(&t_func) != nullptr;
705
        }
706

707
        std::function<Func> internal_function() const
708
        {
709
          return m_f.get();
710
        }
711

712 1
        void assign(const std::shared_ptr<const Proxy_Function_Base> &t_rhs) override {
713 1
          m_f.get() = dispatch::functor<Func>(t_rhs, nullptr);
714
        }
715

716
      protected:
717 1
        Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const override
718
        {
719 1
          return detail::call_func(static_cast<Func *>(nullptr), m_f.get(), params, t_conversions);
720
        }
721

722

723
      private:
724
        std::reference_wrapper<std::function<Func>> m_f;
725
        std::shared_ptr<std::function<Func>> m_shared_ptr_holder;
726
    };
727

728

729
    /// Attribute getter Proxy_Function implementation
730
    template<typename T, typename Class>
731
      class Attribute_Access final : public Proxy_Function_Base
732
    {
733
      public:
734 1
        explicit Attribute_Access(T Class::* t_attr)
735
          : Proxy_Function_Base(param_types(), 1),
736 1
            m_attr(t_attr)
737
        {
738
        }
739

740 1
        bool is_attribute_function() const noexcept override { return true; }
741

742 1
        bool operator==(const Proxy_Function_Base &t_func) const noexcept override
743
        {
744 0
          const Attribute_Access<T, Class> * aa
745 1
            = dynamic_cast<const Attribute_Access<T, Class> *>(&t_func);
746

747 1
          if (aa) {
748 0
            return m_attr == aa->m_attr;
749
          } else {
750 1
            return false;
751
          }
752
        }
753

754 1
        bool call_match(const Function_Params &vals, const Type_Conversions_State &) const noexcept override
755
        {
756 1
          if (vals.size() != 1)
757
          {
758 0
            return false;
759
          }
760 1
          const auto class_type_info = user_type<Class>();
761 1
          return vals[0].get_type_info().bare_equal(class_type_info);
762
        }
763

764
      protected:
765 1
        Boxed_Value do_call(const Function_Params &params, const Type_Conversions_State &t_conversions) const override
766
        {
767 1
          const Boxed_Value &bv = params[0];
768 1
          if (bv.is_const())
769
          {
770 1
            const Class *o = boxed_cast<const Class *>(bv, &t_conversions);
771 1
            return do_call_impl<T>(o);
772
          } else {
773 1
            Class *o = boxed_cast<Class *>(bv, &t_conversions);
774 1
            return do_call_impl<T>(o);
775
          }
776
        }
777

778
      private:
779
        template<typename Type>
780 1
        auto do_call_impl(Class *o) const
781
        {
782
          if constexpr(std::is_pointer<Type>::value) {
783 1
            return detail::Handle_Return<Type>::handle(o->*m_attr);
784
          } else {
785 1
            return detail::Handle_Return<typename std::add_lvalue_reference<Type>::type>::handle(o->*m_attr);
786
          }
787
        }
788

789
        template<typename Type>
790 1
        auto do_call_impl(const Class *o) const
791
        {
792
          if constexpr(std::is_pointer<Type>::value) {
793 0
            return detail::Handle_Return<const Type>::handle(o->*m_attr);
794
          } else {
795 1
            return detail::Handle_Return<typename std::add_lvalue_reference<typename std::add_const<Type>::type>::type>::handle(o->*m_attr);
796
          }
797
        }
798

799

800 1
        static std::vector<Type_Info> param_types()
801
        {
802 1
          return {user_type<T>(), user_type<Class>()};
803
        }
804

805
        std::vector<Type_Info> m_param_types{user_type<T>(), user_type<Class>()};
806
        T Class::* m_attr;
807
    };
808
  }
809

810
  namespace exception
811
  {
812
     /// \brief Exception thrown in the case that a method dispatch fails
813
     ///        because no matching function was found
814
     ///
815
     /// May be thrown due to an arity_error, a guard_error or a bad_boxed_cast
816
     /// exception
817
    class dispatch_error : public std::runtime_error
818
    {
819
      public:
820 1
        dispatch_error(const Function_Params &t_parameters,
821
            std::vector<Const_Proxy_Function> t_functions)
822 1
          : std::runtime_error("Error with function dispatch"), parameters(t_parameters.to_vector()), functions(std::move(t_functions))
823
        {
824
        }
825

826 1
        dispatch_error(const Function_Params &t_parameters,
827
            std::vector<Const_Proxy_Function> t_functions,
828
            const std::string &t_desc)
829 1
          : std::runtime_error(t_desc), parameters(t_parameters.to_vector()), functions(std::move(t_functions))
830
        {
831
        }
832

833

834
        dispatch_error(const dispatch_error &) = default;
835 1
        ~dispatch_error() noexcept override = default;
836

837
        std::vector<Boxed_Value> parameters;
838
        std::vector<Const_Proxy_Function> functions;
839
    };
840
  }
841

842
  namespace dispatch
843
  {
844
    namespace detail
845
    {
846
      template<typename FuncType>
847 1
        bool types_match_except_for_arithmetic(const FuncType &t_func, const chaiscript::Function_Params &plist,
848
            const Type_Conversions_State &t_conversions) noexcept
849
        {
850 1
          const std::vector<Type_Info> &types = t_func->get_param_types();
851

852 1
          if (t_func->get_arity() == -1) { return false; }
853

854 0
          assert(plist.size() == types.size() - 1);
855

856 1
          return std::mismatch(plist.begin(), plist.end(),
857 1
                               types.begin()+1,
858
                               [&](const Boxed_Value &bv, const Type_Info &ti) {
859
                                 return Proxy_Function_Base::compare_type_to_param(ti, bv, t_conversions)
860
                                       || (bv.get_type_info().is_arithmetic() && ti.is_arithmetic());
861
                               }
862 1
              ) == std::make_pair(plist.end(), types.end());
863
        }
864

865
      template<typename InItr, typename Funcs>
866 1
        Boxed_Value dispatch_with_conversions(InItr begin, const InItr &end, const chaiscript::Function_Params &plist,
867
            const Type_Conversions_State &t_conversions, const Funcs &t_funcs)
868
        {
869 1
          InItr matching_func(end);
870

871 1
          while (begin != end)
872
          {
873 1
            if (types_match_except_for_arithmetic(begin->second, plist, t_conversions))
874
            {
875 1
              if (matching_func == end)
876
              {
877 1
                matching_func = begin;
878
              } else {
879
                // handle const members vs non-const member, which is not really ambiguous
880 1
                const auto &mat_fun_param_types = matching_func->second->get_param_types();
881 1
                const auto &next_fun_param_types = begin->second->get_param_types();
882

883 1
                if (plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) {
884 1
                  matching_func = begin; // keep the new one, the const/non-const matchup is correct
885 1
                } else if (!plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) {
886
                  // keep the old one, it has a better const/non-const matchup
887
                } else {
888
                  // ambiguous function call
889 1
                  throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(t_funcs.begin(), t_funcs.end()));
890
                }
891
              }
892
            }
893

894 1
            ++begin;
895
          }
896

897 1
          if (matching_func == end)
898
          {
899
            // no appropriate function to attempt arithmetic type conversion on
900 1
            throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(t_funcs.begin(), t_funcs.end()));
901
          }
902

903

904 1
          std::vector<Boxed_Value> newplist;
905 1
          newplist.reserve(plist.size());
906

907 1
          const std::vector<Type_Info> &tis = matching_func->second->get_param_types();
908 1
          std::transform(tis.begin() + 1, tis.end(),
909
                         plist.begin(),
910
                         std::back_inserter(newplist),
911
                         [](const Type_Info &ti, const Boxed_Value &param) -> Boxed_Value {
912
                           if (ti.is_arithmetic() && param.get_type_info().is_arithmetic()
913
                               && param.get_type_info() != ti) {
914
                             return Boxed_Number(param).get_as(ti).bv;
915
                           } else {
916
                             return param;
917
                           }
918
                         }
919
                       );
920

921
          try {
922 1
            return (*(matching_func->second))(chaiscript::Function_Params{newplist}, t_conversions);
923 1
          } catch (const exception::bad_boxed_cast &) {
924
            //parameter failed to cast
925 0
          } catch (const exception::arity_error &) {
926
            //invalid num params
927 1
          } catch (const exception::guard_error &) {
928
            //guard failed to allow the function to execute
929
          }
930

931 1
          throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(t_funcs.begin(), t_funcs.end()));
932

933
        }
934
    }
935

936
    /// Take a vector of functions and a vector of parameters. Attempt to execute
937
    /// each function against the set of parameters, in order, until a matching
938
    /// function is found or throw dispatch_error if no matching function is found
939
    template<typename Funcs>
940
      Boxed_Value dispatch(const Funcs &funcs,
941
          const Function_Params &plist, const Type_Conversions_State &t_conversions)
942
      {
943
        std::vector<std::pair<size_t, const Proxy_Function_Base *>> ordered_funcs;
944
        ordered_funcs.reserve(funcs.size());
945

946
        for (const auto &func : funcs)
947
        {
948
          const auto arity = func->get_arity();
949

950
          if (arity == -1)
951
          {
952
            ordered_funcs.emplace_back(plist.size(), func.get());
953
          } else if (arity == static_cast<int>(plist.size())) {
954
            size_t numdiffs = 0;
955
            for (size_t i = 0; i < plist.size(); ++i)
956
            {
957
              if (!func->get_param_types()[i+1].bare_equal(plist[i].get_type_info()))
958
              {
959
                ++numdiffs;
960
              }
961
            }
962
            ordered_funcs.emplace_back(numdiffs, func.get());
963
          }
964
        }
965

966

967
        for (size_t i = 0; i <= plist.size(); ++i)
968
        {
969
          for (const auto &func : ordered_funcs )
970
          {
971
            try {
972
              if (func.first == i && (i == 0 || func.second->filter(plist, t_conversions)))
973
              {
974
                return (*(func.second))(plist, t_conversions);
975
              }
976 1
            } catch (const exception::bad_boxed_cast &) {
977
              //parameter failed to cast, try again
978 1
            } catch (const exception::arity_error &) {
979
              //invalid num params, try again
980 1
            } catch (const exception::guard_error &) {
981
              //guard failed to allow the function to execute,
982
              //try again
983
            }
984
          }
985
        }
986

987
        return detail::dispatch_with_conversions(ordered_funcs.cbegin(), ordered_funcs.cend(), plist, t_conversions, funcs);
988
      }
989
  }
990
}
991

992

993
#endif

Read our documentation on viewing source code .

Loading