dispatchkit.hpp 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572
  1. // This file is distributed under the BSD License.
  2. // See "license.txt" for details.
  3. // Copyright 2009-2012, Jonathan Turner ([email protected])
  4. // Copyright 2009-2017, Jason Turner ([email protected])
  5. // http://www.chaiscript.com
  6. // This is an open source non-commercial project. Dear PVS-Studio, please check it.
  7. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
  8. #ifndef CHAISCRIPT_DISPATCHKIT_HPP_
  9. #define CHAISCRIPT_DISPATCHKIT_HPP_
  10. #include <algorithm>
  11. #include <iostream>
  12. #include <list>
  13. #include <map>
  14. #include <memory>
  15. #include <set>
  16. #include <stdexcept>
  17. #include <string>
  18. #include <typeinfo>
  19. #include <utility>
  20. #include <vector>
  21. #include "../chaiscript_defines.hpp"
  22. #include "../chaiscript_threading.hpp"
  23. #include "bad_boxed_cast.hpp"
  24. #include "boxed_cast.hpp"
  25. #include "boxed_cast_helper.hpp"
  26. #include "boxed_value.hpp"
  27. #include "type_conversions.hpp"
  28. #include "dynamic_object.hpp"
  29. #include "proxy_constructors.hpp"
  30. #include "proxy_functions.hpp"
  31. #include "type_info.hpp"
  32. #include "short_alloc.hpp"
  33. namespace chaiscript {
  34. class Boxed_Number;
  35. } // namespace chaiscript
  36. namespace chaiscript {
  37. namespace parser {
  38. class ChaiScript_Parser_Base;
  39. }
  40. namespace dispatch {
  41. class Dynamic_Proxy_Function;
  42. class Proxy_Function_Base;
  43. struct Placeholder_Object;
  44. } // namespace dispatch
  45. } // namespace chaiscript
  46. /// \namespace chaiscript::dispatch
  47. /// \brief Classes and functions specific to the runtime dispatch side of ChaiScript. Some items may be of use to the end user.
  48. namespace chaiscript
  49. {
  50. namespace exception
  51. {
  52. /// Exception thrown in the case that an object name is invalid because it is a reserved word
  53. class reserved_word_error : public std::runtime_error
  54. {
  55. public:
  56. explicit reserved_word_error(const std::string &t_word) noexcept
  57. : std::runtime_error("Reserved word not allowed in object name: " + t_word), m_word(t_word)
  58. {
  59. }
  60. reserved_word_error(const reserved_word_error &) = default;
  61. ~reserved_word_error() noexcept override = default;
  62. std::string word() const
  63. {
  64. return m_word;
  65. }
  66. private:
  67. std::string m_word;
  68. };
  69. /// Exception thrown in the case that an object name is invalid because it contains illegal characters
  70. class illegal_name_error : public std::runtime_error
  71. {
  72. public:
  73. explicit illegal_name_error(const std::string &t_name) noexcept
  74. : std::runtime_error("Reserved name not allowed in object name: " + t_name), m_name(t_name)
  75. {
  76. }
  77. illegal_name_error(const illegal_name_error &) = default;
  78. ~illegal_name_error() noexcept override = default;
  79. std::string name() const
  80. {
  81. return m_name;
  82. }
  83. private:
  84. std::string m_name;
  85. };
  86. /// Exception thrown in the case that an object name is invalid because it already exists in current context
  87. class name_conflict_error : public std::runtime_error
  88. {
  89. public:
  90. explicit name_conflict_error(const std::string &t_name) noexcept
  91. : std::runtime_error("Name already exists in current context " + t_name), m_name(t_name)
  92. {
  93. }
  94. name_conflict_error(const name_conflict_error &) = default;
  95. ~name_conflict_error() noexcept override = default;
  96. std::string name() const
  97. {
  98. return m_name;
  99. }
  100. private:
  101. std::string m_name;
  102. };
  103. /// Exception thrown in the case that a non-const object was added as a shared object
  104. class global_non_const : public std::runtime_error
  105. {
  106. public:
  107. global_non_const() noexcept
  108. : std::runtime_error("a global object must be const")
  109. {
  110. }
  111. global_non_const(const global_non_const &) = default;
  112. ~global_non_const() noexcept override = default;
  113. };
  114. }
  115. /// \brief Holds a collection of ChaiScript settings which can be applied to the ChaiScript runtime.
  116. /// Used to implement loadable module support.
  117. class Module
  118. {
  119. public:
  120. Module &add(Type_Info ti, std::string name)
  121. {
  122. m_typeinfos.emplace_back(ti, std::move(name));
  123. return *this;
  124. }
  125. Module &add(Type_Conversion d)
  126. {
  127. m_conversions.push_back(std::move(d));
  128. return *this;
  129. }
  130. Module &add(Proxy_Function f, std::string name)
  131. {
  132. m_funcs.emplace_back(std::move(f), std::move(name));
  133. return *this;
  134. }
  135. Module &add_global_const(Boxed_Value t_bv, std::string t_name)
  136. {
  137. if (!t_bv.is_const())
  138. {
  139. throw chaiscript::exception::global_non_const();
  140. }
  141. m_globals.emplace_back(std::move(t_bv), std::move(t_name));
  142. return *this;
  143. }
  144. //Add a bit of ChaiScript to eval during module implementation
  145. Module &eval(const std::string &str)
  146. {
  147. m_evals.push_back(str);
  148. return *this;
  149. }
  150. template<typename Eval, typename Engine>
  151. void apply(Eval &t_eval, Engine &t_engine) const
  152. {
  153. apply(m_typeinfos.begin(), m_typeinfos.end(), t_engine);
  154. apply(m_funcs.begin(), m_funcs.end(), t_engine);
  155. apply_eval(m_evals.begin(), m_evals.end(), t_eval);
  156. apply_single(m_conversions.begin(), m_conversions.end(), t_engine);
  157. apply_globals(m_globals.begin(), m_globals.end(), t_engine);
  158. }
  159. bool has_function(const Proxy_Function &new_f, const std::string &name)
  160. {
  161. return std::any_of(m_funcs.begin(), m_funcs.end(),
  162. [&](const std::pair<Proxy_Function, std::string> &existing_f) {
  163. return existing_f.second == name && *(existing_f.first) == *(new_f);
  164. }
  165. );
  166. }
  167. private:
  168. std::vector<std::pair<Type_Info, std::string>> m_typeinfos;
  169. std::vector<std::pair<Proxy_Function, std::string>> m_funcs;
  170. std::vector<std::pair<Boxed_Value, std::string>> m_globals;
  171. std::vector<std::string> m_evals;
  172. std::vector<Type_Conversion> m_conversions;
  173. template<typename T, typename InItr>
  174. static void apply(InItr begin, const InItr end, T &t)
  175. {
  176. for_each(begin, end,
  177. [&t](const auto &obj) {
  178. try {
  179. t.add(obj.first, obj.second);
  180. } catch (const chaiscript::exception::name_conflict_error &) {
  181. /// \todo Should we throw an error if there's a name conflict
  182. /// while applying a module?
  183. }
  184. }
  185. );
  186. }
  187. template<typename T, typename InItr>
  188. static void apply_globals(InItr begin, InItr end, T &t)
  189. {
  190. while (begin != end)
  191. {
  192. t.add_global_const(begin->first, begin->second);
  193. ++begin;
  194. }
  195. }
  196. template<typename T, typename InItr>
  197. static void apply_single(InItr begin, InItr end, T &t)
  198. {
  199. while (begin != end)
  200. {
  201. t.add(*begin);
  202. ++begin;
  203. }
  204. }
  205. template<typename T, typename InItr>
  206. static void apply_eval(InItr begin, InItr end, T &t)
  207. {
  208. while (begin != end)
  209. {
  210. t.eval(*begin);
  211. ++begin;
  212. }
  213. }
  214. };
  215. /// Convenience typedef for Module objects to be added to the ChaiScript runtime
  216. typedef std::shared_ptr<Module> ModulePtr;
  217. namespace detail
  218. {
  219. /// A Proxy_Function implementation that is able to take
  220. /// a vector of Proxy_Functions and perform a dispatch on them. It is
  221. /// used specifically in the case of dealing with Function object variables
  222. class Dispatch_Function final : public dispatch::Proxy_Function_Base
  223. {
  224. public:
  225. explicit Dispatch_Function(std::vector<Proxy_Function> t_funcs)
  226. : Proxy_Function_Base(build_type_infos(t_funcs), calculate_arity(t_funcs)),
  227. m_funcs(std::move(t_funcs))
  228. {
  229. }
  230. bool operator==(const dispatch::Proxy_Function_Base &rhs) const override
  231. {
  232. try {
  233. const auto &dispatch_fun = dynamic_cast<const Dispatch_Function &>(rhs);
  234. return m_funcs == dispatch_fun.m_funcs;
  235. } catch (const std::bad_cast &) {
  236. return false;
  237. }
  238. }
  239. std::vector<Const_Proxy_Function> get_contained_functions() const override
  240. {
  241. return std::vector<Const_Proxy_Function>(m_funcs.begin(), m_funcs.end());
  242. }
  243. static int calculate_arity(const std::vector<Proxy_Function> &t_funcs)
  244. {
  245. if (t_funcs.empty()) {
  246. return -1;
  247. }
  248. const auto arity = t_funcs.front()->get_arity();
  249. for (const auto &func : t_funcs)
  250. {
  251. if (arity != func->get_arity())
  252. {
  253. // The arities in the list do not match, so it's unspecified
  254. return -1;
  255. }
  256. }
  257. return arity;
  258. }
  259. bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const override
  260. {
  261. return std::any_of(std::begin(m_funcs), std::end(m_funcs),
  262. [&vals, &t_conversions](const Proxy_Function &f){ return f->call_match(vals, t_conversions); });
  263. }
  264. protected:
  265. Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const override
  266. {
  267. return dispatch::dispatch(m_funcs, params, t_conversions);
  268. }
  269. private:
  270. std::vector<Proxy_Function> m_funcs;
  271. static std::vector<Type_Info> build_type_infos(const std::vector<Proxy_Function> &t_funcs)
  272. {
  273. auto begin = t_funcs.cbegin();
  274. const auto &end = t_funcs.cend();
  275. if (begin != end)
  276. {
  277. std::vector<Type_Info> type_infos = (*begin)->get_param_types();
  278. ++begin;
  279. bool size_mismatch = false;
  280. while (begin != end)
  281. {
  282. std::vector<Type_Info> param_types = (*begin)->get_param_types();
  283. if (param_types.size() != type_infos.size())
  284. {
  285. size_mismatch = true;
  286. }
  287. for (size_t i = 0; i < type_infos.size() && i < param_types.size(); ++i)
  288. {
  289. if (!(type_infos[i] == param_types[i]))
  290. {
  291. type_infos[i] = detail::Get_Type_Info<Boxed_Value>::get();
  292. }
  293. }
  294. ++begin;
  295. }
  296. assert(!type_infos.empty() && " type_info vector size is < 0, this is only possible if something else is broken");
  297. if (size_mismatch)
  298. {
  299. type_infos.resize(1);
  300. }
  301. return type_infos;
  302. }
  303. return std::vector<Type_Info>();
  304. }
  305. };
  306. }
  307. namespace detail
  308. {
  309. struct Stack_Holder
  310. {
  311. //template <class T, std::size_t BufSize = sizeof(T)*20000>
  312. // using SmallVector = std::vector<T, short_alloc<T, BufSize>>;
  313. template <class T>
  314. using SmallVector = std::vector<T>;
  315. typedef SmallVector<std::pair<std::string, Boxed_Value>> Scope;
  316. typedef SmallVector<Scope> StackData;
  317. typedef SmallVector<StackData> Stacks;
  318. typedef SmallVector<Boxed_Value> Call_Param_List;
  319. typedef SmallVector<Call_Param_List> Call_Params;
  320. Stack_Holder()
  321. {
  322. push_stack();
  323. push_call_params();
  324. }
  325. void push_stack_data()
  326. {
  327. stacks.back().emplace_back();
  328. // stacks.back().emplace_back(Scope(scope_allocator));
  329. }
  330. void push_stack()
  331. {
  332. stacks.emplace_back(1);
  333. // stacks.emplace_back(StackData(1, Scope(scope_allocator), stack_data_allocator));
  334. }
  335. void push_call_params()
  336. {
  337. call_params.emplace_back();
  338. // call_params.emplace_back(Call_Param_List(call_param_list_allocator));
  339. }
  340. //Scope::allocator_type::arena_type scope_allocator;
  341. //StackData::allocator_type::arena_type stack_data_allocator;
  342. //Stacks::allocator_type::arena_type stacks_allocator;
  343. //Call_Param_List::allocator_type::arena_type call_param_list_allocator;
  344. //Call_Params::allocator_type::arena_type call_params_allocator;
  345. // Stacks stacks = Stacks(stacks_allocator);
  346. // Call_Params call_params = Call_Params(call_params_allocator);
  347. Stacks stacks;
  348. Call_Params call_params;
  349. int call_depth = 0;
  350. };
  351. /// Main class for the dispatchkit. Handles management
  352. /// of the object stack, functions and registered types.
  353. class Dispatch_Engine
  354. {
  355. public:
  356. typedef std::map<std::string, chaiscript::Type_Info> Type_Name_Map;
  357. typedef std::vector<std::pair<std::string, Boxed_Value>> Scope;
  358. typedef Stack_Holder::StackData StackData;
  359. struct State
  360. {
  361. std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> m_functions;
  362. std::vector<std::pair<std::string, Proxy_Function>> m_function_objects;
  363. std::vector<std::pair<std::string, Boxed_Value>> m_boxed_functions;
  364. std::map<std::string, Boxed_Value> m_global_objects;
  365. Type_Name_Map m_types;
  366. };
  367. explicit Dispatch_Engine(chaiscript::parser::ChaiScript_Parser_Base &parser)
  368. : m_stack_holder(),
  369. m_parser(parser)
  370. {
  371. }
  372. /// \brief casts an object while applying any Dynamic_Conversion available
  373. template<typename Type>
  374. decltype(auto) boxed_cast(const Boxed_Value &bv) const
  375. {
  376. Type_Conversions_State state(m_conversions, m_conversions.conversion_saves());
  377. return(chaiscript::boxed_cast<Type>(bv, &state));
  378. }
  379. /// Add a new conversion for upcasting to a base class
  380. void add(const Type_Conversion &d)
  381. {
  382. m_conversions.add_conversion(d);
  383. }
  384. /// Add a new named Proxy_Function to the system
  385. void add(const Proxy_Function &f, const std::string &name)
  386. {
  387. add_function(f, name);
  388. }
  389. /// Set the value of an object, by name. If the object
  390. /// is not available in the current scope it is created
  391. void add(Boxed_Value obj, const std::string &name)
  392. {
  393. auto &stack = get_stack_data();
  394. for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem)
  395. {
  396. auto itr = std::find_if(stack_elem->begin(), stack_elem->end(),
  397. [&](const std::pair<std::string, Boxed_Value> &o) {
  398. return o.first == name;
  399. });
  400. if (itr != stack_elem->end())
  401. {
  402. itr->second = std::move(obj);
  403. return;
  404. }
  405. }
  406. add_object(name, std::move(obj));
  407. }
  408. /// Adds a named object to the current scope
  409. /// \warning This version does not check the validity of the name
  410. /// it is meant for internal use only
  411. Boxed_Value &add_get_object(const std::string &t_name, Boxed_Value obj, Stack_Holder &t_holder)
  412. {
  413. auto &stack_elem = get_stack_data(t_holder).back();
  414. if (std::any_of(stack_elem.begin(), stack_elem.end(),
  415. [&](const std::pair<std::string, Boxed_Value> &o) {
  416. return o.first == t_name;
  417. }))
  418. {
  419. throw chaiscript::exception::name_conflict_error(t_name);
  420. }
  421. stack_elem.emplace_back(t_name, std::move(obj));
  422. return stack_elem.back().second;
  423. }
  424. /// Adds a named object to the current scope
  425. /// \warning This version does not check the validity of the name
  426. /// it is meant for internal use only
  427. void add_object(const std::string &t_name, Boxed_Value obj, Stack_Holder &t_holder)
  428. {
  429. auto &stack_elem = get_stack_data(t_holder).back();
  430. if (std::any_of(stack_elem.begin(), stack_elem.end(),
  431. [&](const std::pair<std::string, Boxed_Value> &o) {
  432. return o.first == t_name;
  433. }))
  434. {
  435. throw chaiscript::exception::name_conflict_error(t_name);
  436. }
  437. stack_elem.emplace_back(t_name, std::move(obj));
  438. }
  439. /// Adds a named object to the current scope
  440. /// \warning This version does not check the validity of the name
  441. /// it is meant for internal use only
  442. void add_object(const std::string &name, Boxed_Value obj)
  443. {
  444. add_object(name, std::move(obj), get_stack_holder());
  445. }
  446. /// Adds a new global shared object, between all the threads
  447. void add_global_const(const Boxed_Value &obj, const std::string &name)
  448. {
  449. if (!obj.is_const())
  450. {
  451. throw chaiscript::exception::global_non_const();
  452. }
  453. chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  454. if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end())
  455. {
  456. throw chaiscript::exception::name_conflict_error(name);
  457. } else {
  458. m_state.m_global_objects.insert(std::make_pair(name, obj));
  459. }
  460. }
  461. /// Adds a new global (non-const) shared object, between all the threads
  462. Boxed_Value add_global_no_throw(const Boxed_Value &obj, const std::string &name)
  463. {
  464. chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  465. const auto itr = m_state.m_global_objects.find(name);
  466. if (itr == m_state.m_global_objects.end())
  467. {
  468. m_state.m_global_objects.insert(std::make_pair(name, obj));
  469. return obj;
  470. } else {
  471. return itr->second;
  472. }
  473. }
  474. /// Adds a new global (non-const) shared object, between all the threads
  475. void add_global(const Boxed_Value &obj, const std::string &name)
  476. {
  477. chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  478. if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end())
  479. {
  480. throw chaiscript::exception::name_conflict_error(name);
  481. } else {
  482. m_state.m_global_objects.insert(std::make_pair(name, obj));
  483. }
  484. }
  485. /// Updates an existing global shared object or adds a new global shared object if not found
  486. void set_global(const Boxed_Value &obj, const std::string &name)
  487. {
  488. chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  489. const auto itr = m_state.m_global_objects.find(name);
  490. if (itr != m_state.m_global_objects.end())
  491. {
  492. itr->second.assign(obj);
  493. } else {
  494. m_state.m_global_objects.insert(std::make_pair(name, obj));
  495. }
  496. }
  497. /// Adds a new scope to the stack
  498. void new_scope()
  499. {
  500. new_scope(*m_stack_holder);
  501. }
  502. /// Pops the current scope from the stack
  503. void pop_scope()
  504. {
  505. pop_scope(*m_stack_holder);
  506. }
  507. /// Adds a new scope to the stack
  508. static void new_scope(Stack_Holder &t_holder)
  509. {
  510. t_holder.push_stack_data();
  511. t_holder.push_call_params();
  512. }
  513. /// Pops the current scope from the stack
  514. static void pop_scope(Stack_Holder &t_holder)
  515. {
  516. t_holder.call_params.pop_back();
  517. StackData &stack = get_stack_data(t_holder);
  518. assert(!stack.empty());
  519. stack.pop_back();
  520. }
  521. /// Pushes a new stack on to the list of stacks
  522. static void new_stack(Stack_Holder &t_holder)
  523. {
  524. // add a new Stack with 1 element
  525. t_holder.push_stack();
  526. }
  527. static void pop_stack(Stack_Holder &t_holder)
  528. {
  529. t_holder.stacks.pop_back();
  530. }
  531. /// Searches the current stack for an object of the given name
  532. /// includes a special overload for the _ place holder object to
  533. /// ensure that it is always in scope.
  534. Boxed_Value get_object(const std::string &name, std::atomic_uint_fast32_t &t_loc, Stack_Holder &t_holder) const
  535. {
  536. enum class Loc : uint_fast32_t {
  537. located = 0x80000000,
  538. is_local = 0x40000000,
  539. stack_mask = 0x0FFF0000,
  540. loc_mask = 0x0000FFFF
  541. };
  542. uint_fast32_t loc = t_loc;
  543. if (loc == 0)
  544. {
  545. auto &stack = get_stack_data(t_holder);
  546. // Is it in the stack?
  547. for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem)
  548. {
  549. for (auto s = stack_elem->begin(); s != stack_elem->end(); ++s )
  550. {
  551. if (s->first == name) {
  552. t_loc = static_cast<uint_fast32_t>(std::distance(stack.rbegin(), stack_elem) << 16)
  553. | static_cast<uint_fast32_t>(std::distance(stack_elem->begin(), s))
  554. | static_cast<uint_fast32_t>(Loc::located)
  555. | static_cast<uint_fast32_t>(Loc::is_local);
  556. return s->second;
  557. }
  558. }
  559. }
  560. t_loc = static_cast<uint_fast32_t>(Loc::located);
  561. } else if ((loc & static_cast<uint_fast32_t>(Loc::is_local)) != 0u) {
  562. auto &stack = get_stack_data(t_holder);
  563. return stack[stack.size() - 1 - ((loc & static_cast<uint_fast32_t>(Loc::stack_mask)) >> 16)][loc & static_cast<uint_fast32_t>(Loc::loc_mask)].second;
  564. }
  565. // Is the value we are looking for a global or function?
  566. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  567. const auto itr = m_state.m_global_objects.find(name);
  568. if (itr != m_state.m_global_objects.end())
  569. {
  570. return itr->second;
  571. }
  572. // no? is it a function object?
  573. auto obj = get_function_object_int(name, loc);
  574. if (obj.first != loc) { t_loc = uint_fast32_t(obj.first); }
  575. return obj.second;
  576. }
  577. /// Registers a new named type
  578. void add(const Type_Info &ti, const std::string &name)
  579. {
  580. add_global_const(const_var(ti), name + "_type");
  581. chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  582. m_state.m_types.insert(std::make_pair(name, ti));
  583. }
  584. /// Returns the type info for a named type
  585. Type_Info get_type(const std::string &name, bool t_throw = true) const
  586. {
  587. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  588. const auto itr = m_state.m_types.find(name);
  589. if (itr != m_state.m_types.end())
  590. {
  591. return itr->second;
  592. }
  593. if (t_throw) {
  594. throw std::range_error("Type Not Known");
  595. } else {
  596. return Type_Info();
  597. }
  598. }
  599. /// Returns the registered name of a known type_info object
  600. /// compares the "bare_type_info" for the broadest possible
  601. /// match
  602. std::string get_type_name(const Type_Info &ti) const
  603. {
  604. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  605. for (const auto & elem : m_state.m_types)
  606. {
  607. if (elem.second.bare_equal(ti))
  608. {
  609. return elem.first;
  610. }
  611. }
  612. return ti.bare_name();
  613. }
  614. /// Return all registered types
  615. std::vector<std::pair<std::string, Type_Info> > get_types() const
  616. {
  617. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  618. return std::vector<std::pair<std::string, Type_Info> >(m_state.m_types.begin(), m_state.m_types.end());
  619. }
  620. std::shared_ptr<std::vector<Proxy_Function>> get_method_missing_functions() const
  621. {
  622. uint_fast32_t method_missing_loc = m_method_missing_loc;
  623. auto method_missing_funs = get_function("method_missing", method_missing_loc);
  624. if (method_missing_funs.first != method_missing_loc) {
  625. m_method_missing_loc = uint_fast32_t(method_missing_funs.first);
  626. }
  627. return std::move(method_missing_funs.second);
  628. }
  629. /// Return a function by name
  630. std::pair<size_t, std::shared_ptr<std::vector< Proxy_Function>>> get_function(const std::string &t_name, const size_t t_hint) const
  631. {
  632. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  633. const auto &funs = get_functions_int();
  634. auto itr = find_keyed_value(funs, t_name, t_hint);
  635. if (itr != funs.end())
  636. {
  637. return std::make_pair(std::distance(funs.begin(), itr), itr->second);
  638. } else {
  639. return std::make_pair(size_t(0), std::make_shared<std::vector<Proxy_Function>>());
  640. }
  641. }
  642. /// \returns a function object (Boxed_Value wrapper) if it exists
  643. /// \throws std::range_error if it does not
  644. Boxed_Value get_function_object(const std::string &t_name) const
  645. {
  646. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  647. return get_function_object_int(t_name, 0).second;
  648. }
  649. /// \returns a function object (Boxed_Value wrapper) if it exists
  650. /// \throws std::range_error if it does not
  651. /// \warn does not obtain a mutex lock. \sa get_function_object for public version
  652. std::pair<size_t, Boxed_Value> get_function_object_int(const std::string &t_name, const size_t t_hint) const
  653. {
  654. const auto &funs = get_boxed_functions_int();
  655. auto itr = find_keyed_value(funs, t_name, t_hint);
  656. if (itr != funs.end())
  657. {
  658. return std::make_pair(std::distance(funs.begin(), itr), itr->second);
  659. } else {
  660. throw std::range_error("Object not found: " + t_name);
  661. }
  662. }
  663. /// Return true if a function exists
  664. bool function_exists(const std::string &name) const
  665. {
  666. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  667. const auto &functions = get_functions_int();
  668. return find_keyed_value(functions, name) != functions.end();
  669. }
  670. /// \returns All values in the local thread state in the parent scope, or if it doesn't exist,
  671. /// the current scope.
  672. std::map<std::string, Boxed_Value> get_parent_locals() const
  673. {
  674. auto &stack = get_stack_data();
  675. if (stack.size() > 1)
  676. {
  677. return std::map<std::string, Boxed_Value>(stack[1].begin(), stack[1].end());
  678. } else {
  679. return std::map<std::string, Boxed_Value>(stack[0].begin(), stack[0].end());
  680. }
  681. }
  682. /// \returns All values in the local thread state, added through the add() function
  683. std::map<std::string, Boxed_Value> get_locals() const
  684. {
  685. auto &stack = get_stack_data();
  686. auto &scope = stack.front();
  687. return std::map<std::string, Boxed_Value>(scope.begin(), scope.end());
  688. }
  689. /// \brief Sets all of the locals for the current thread state.
  690. ///
  691. /// \param[in] t_locals The map<name, value> set of variables to replace the current state with
  692. ///
  693. /// Any existing locals are removed and the given set of variables is added
  694. void set_locals(const std::map<std::string, Boxed_Value> &t_locals)
  695. {
  696. auto &stack = get_stack_data();
  697. auto &scope = stack.front();
  698. scope.assign(t_locals.begin(), t_locals.end());
  699. }
  700. ///
  701. /// Get a map of all objects that can be seen from the current scope in a scripting context
  702. ///
  703. std::map<std::string, Boxed_Value> get_scripting_objects() const
  704. {
  705. const Stack_Holder &s = *m_stack_holder;
  706. // We don't want the current context, but one up if it exists
  707. const StackData &stack = (s.stacks.size()==1)?(s.stacks.back()):(s.stacks[s.stacks.size()-2]);
  708. std::map<std::string, Boxed_Value> retval;
  709. // note: map insert doesn't overwrite existing values, which is why this works
  710. for (auto itr = stack.rbegin(); itr != stack.rend(); ++itr)
  711. {
  712. retval.insert(itr->begin(), itr->end());
  713. }
  714. // add the global values
  715. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  716. retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end());
  717. return retval;
  718. }
  719. ///
  720. /// Get a map of all functions that can be seen from a scripting context
  721. ///
  722. std::map<std::string, Boxed_Value> get_function_objects() const
  723. {
  724. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  725. const auto &funs = get_function_objects_int();
  726. std::map<std::string, Boxed_Value> objs;
  727. for (const auto & fun : funs)
  728. {
  729. objs.insert(std::make_pair(fun.first, const_var(fun.second)));
  730. }
  731. return objs;
  732. }
  733. /// Get a vector of all registered functions
  734. std::vector<std::pair<std::string, Proxy_Function > > get_functions() const
  735. {
  736. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  737. std::vector<std::pair<std::string, Proxy_Function> > rets;
  738. const auto &functions = get_functions_int();
  739. for (const auto & function : functions)
  740. {
  741. for (const auto & internal_func : *function.second)
  742. {
  743. rets.emplace_back(function.first, internal_func);
  744. }
  745. }
  746. return rets;
  747. }
  748. const Type_Conversions &conversions() const
  749. {
  750. return m_conversions;
  751. }
  752. static bool is_attribute_call(const std::vector<Proxy_Function> &t_funs, const std::vector<Boxed_Value> &t_params,
  753. bool t_has_params, const Type_Conversions_State &t_conversions)
  754. {
  755. if (!t_has_params || t_params.empty()) {
  756. return false;
  757. }
  758. return std::any_of(std::begin(t_funs), std::end(t_funs),
  759. [&](const auto &fun) {
  760. return fun->is_attribute_function() && fun->compare_first_type(t_params[0], t_conversions);
  761. }
  762. );
  763. }
  764. #ifdef CHAISCRIPT_MSVC
  765. // MSVC is unable to recognize that "rethrow_exception" causes the function to return
  766. // so we must disable it here.
  767. #pragma warning(push)
  768. #pragma warning(disable : 4715)
  769. #endif
  770. Boxed_Value call_member(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector<Boxed_Value> &params, bool t_has_params,
  771. const Type_Conversions_State &t_conversions)
  772. {
  773. uint_fast32_t loc = t_loc;
  774. const auto funs = get_function(t_name, loc);
  775. if (funs.first != loc) { t_loc = uint_fast32_t(funs.first); }
  776. const auto do_attribute_call =
  777. [this](int l_num_params, const std::vector<Boxed_Value> &l_params, const std::vector<Proxy_Function> &l_funs, const Type_Conversions_State &l_conversions)->Boxed_Value
  778. {
  779. std::vector<Boxed_Value> attr_params{l_params.begin(), l_params.begin() + l_num_params};
  780. Boxed_Value bv = dispatch::dispatch(l_funs, attr_params, l_conversions);
  781. if (l_num_params < int(l_params.size()) || bv.get_type_info().bare_equal(user_type<dispatch::Proxy_Function_Base>())) {
  782. struct This_Foist {
  783. This_Foist(Dispatch_Engine &e, const Boxed_Value &t_bv) : m_e(e) {
  784. m_e.get().new_scope();
  785. m_e.get().add_object("__this", t_bv);
  786. }
  787. ~This_Foist() {
  788. m_e.get().pop_scope();
  789. }
  790. std::reference_wrapper<Dispatch_Engine> m_e;
  791. };
  792. This_Foist fi(*this, l_params.front());
  793. try {
  794. auto func = boxed_cast<const dispatch::Proxy_Function_Base *>(bv);
  795. try {
  796. return (*func)({l_params.begin() + l_num_params, l_params.end()}, l_conversions);
  797. } catch (const chaiscript::exception::bad_boxed_cast &) {
  798. } catch (const chaiscript::exception::arity_error &) {
  799. } catch (const chaiscript::exception::guard_error &) {
  800. }
  801. throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()},
  802. std::vector<Const_Proxy_Function>{boxed_cast<Const_Proxy_Function>(bv)});
  803. } catch (const chaiscript::exception::bad_boxed_cast &) {
  804. // unable to convert bv into a Proxy_Function_Base
  805. throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()},
  806. std::vector<Const_Proxy_Function>(l_funs.begin(), l_funs.end()));
  807. }
  808. } else {
  809. return bv;
  810. }
  811. };
  812. if (is_attribute_call(*funs.second, params, t_has_params, t_conversions)) {
  813. return do_attribute_call(1, params, *funs.second, t_conversions);
  814. } else {
  815. std::exception_ptr except;
  816. if (!funs.second->empty()) {
  817. try {
  818. return dispatch::dispatch(*funs.second, params, t_conversions);
  819. } catch(chaiscript::exception::dispatch_error&) {
  820. except = std::current_exception();
  821. }
  822. }
  823. // If we get here we know that either there was no method with that name,
  824. // or there was no matching method
  825. const auto functions = [&]()->std::vector<Proxy_Function> {
  826. std::vector<Proxy_Function> fs;
  827. const auto method_missing_funs = get_method_missing_functions();
  828. for (const auto &f : *method_missing_funs)
  829. {
  830. if(f->compare_first_type(params[0], t_conversions)) {
  831. fs.push_back(f);
  832. }
  833. }
  834. return fs;
  835. }();
  836. const bool is_no_param = [&]()->bool{
  837. for (const auto &f : functions) {
  838. if (f->get_arity() != 2) {
  839. return false;
  840. }
  841. }
  842. return true;
  843. }();
  844. if (!functions.empty()) {
  845. try {
  846. if (is_no_param) {
  847. std::vector<Boxed_Value> tmp_params(params);
  848. tmp_params.insert(tmp_params.begin() + 1, var(t_name));
  849. return do_attribute_call(2, tmp_params, functions, t_conversions);
  850. } else {
  851. return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector<Boxed_Value>(params.begin()+1, params.end()))}, t_conversions);
  852. }
  853. } catch (const dispatch::option_explicit_set &e) {
  854. throw chaiscript::exception::dispatch_error(params, std::vector<Const_Proxy_Function>(funs.second->begin(), funs.second->end()),
  855. e.what());
  856. }
  857. }
  858. // If we get all the way down here we know there was no "method_missing"
  859. // method at all.
  860. if (except) {
  861. std::rethrow_exception(except);
  862. } else {
  863. throw chaiscript::exception::dispatch_error(params, std::vector<Const_Proxy_Function>(funs.second->begin(), funs.second->end()));
  864. }
  865. }
  866. }
  867. #ifdef CHAISCRIPT_MSVC
  868. #pragma warning(pop)
  869. #endif
  870. Boxed_Value call_function(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector<Boxed_Value> &params,
  871. const Type_Conversions_State &t_conversions) const
  872. {
  873. uint_fast32_t loc = t_loc;
  874. const auto funs = get_function(t_name, loc);
  875. if (funs.first != loc) { t_loc = uint_fast32_t(funs.first);
  876. }
  877. return dispatch::dispatch(*funs.second, params, t_conversions);
  878. }
  879. /// Dump object info to stdout
  880. void dump_object(const Boxed_Value &o) const
  881. {
  882. std::cout << (o.is_const()?"const ":"") << type_name(o) << '\n';
  883. }
  884. /// Dump type info to stdout
  885. void dump_type(const Type_Info &type) const
  886. {
  887. std::cout << (type.is_const()?"const ":"") << get_type_name(type);
  888. }
  889. /// Dump function to stdout
  890. void dump_function(const std::pair<const std::string, Proxy_Function > &f) const
  891. {
  892. std::vector<Type_Info> params = f.second->get_param_types();
  893. dump_type(params.front());
  894. std::cout << " " << f.first << "(";
  895. for (std::vector<Type_Info>::const_iterator itr = params.begin() + 1;
  896. itr != params.end();
  897. )
  898. {
  899. dump_type(*itr);
  900. ++itr;
  901. if (itr != params.end())
  902. {
  903. std::cout << ", ";
  904. }
  905. }
  906. std::cout << ") \n";
  907. }
  908. /// Returns true if a call can be made that consists of the first parameter
  909. /// (the function) with the remaining parameters as its arguments.
  910. Boxed_Value call_exists(const std::vector<Boxed_Value> &params) const
  911. {
  912. if (params.empty())
  913. {
  914. throw chaiscript::exception::arity_error(static_cast<int>(params.size()), 1);
  915. }
  916. const Const_Proxy_Function &f = this->boxed_cast<Const_Proxy_Function>(params[0]);
  917. const Type_Conversions_State convs(m_conversions, m_conversions.conversion_saves());
  918. return const_var(f->call_match(std::vector<Boxed_Value>(params.begin() + 1, params.end()), convs));
  919. }
  920. /// Dump all system info to stdout
  921. void dump_system() const
  922. {
  923. std::cout << "Registered Types: \n";
  924. std::vector<std::pair<std::string, Type_Info> > types = get_types();
  925. for (std::vector<std::pair<std::string, Type_Info> >::const_iterator itr = types.begin();
  926. itr != types.end();
  927. ++itr)
  928. {
  929. std::cout << itr->first << ": ";
  930. std::cout << itr->second.bare_name();
  931. std::cout << '\n';
  932. }
  933. std::cout << '\n';
  934. std::vector<std::pair<std::string, Proxy_Function > > funcs = get_functions();
  935. std::cout << "Functions: \n";
  936. for (std::vector<std::pair<std::string, Proxy_Function > >::const_iterator itr = funcs.begin();
  937. itr != funcs.end();
  938. ++itr)
  939. {
  940. dump_function(*itr);
  941. }
  942. std::cout << '\n';
  943. }
  944. /// return true if the Boxed_Value matches the registered type by name
  945. bool is_type(const Boxed_Value &r, const std::string &user_typename) const
  946. {
  947. try {
  948. if (get_type(user_typename).bare_equal(r.get_type_info()))
  949. {
  950. return true;
  951. }
  952. } catch (const std::range_error &) {
  953. }
  954. try {
  955. const dispatch::Dynamic_Object &d = boxed_cast<const dispatch::Dynamic_Object &>(r);
  956. return d.get_type_name() == user_typename;
  957. } catch (const std::bad_cast &) {
  958. }
  959. return false;
  960. }
  961. std::string type_name(const Boxed_Value &obj) const
  962. {
  963. return get_type_name(obj.get_type_info());
  964. }
  965. State get_state() const
  966. {
  967. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  968. return m_state;
  969. }
  970. void set_state(const State &t_state)
  971. {
  972. chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  973. m_state = t_state;
  974. }
  975. static void save_function_params(Stack_Holder &t_s, std::initializer_list<Boxed_Value> t_params)
  976. {
  977. t_s.call_params.back().insert(t_s.call_params.back().begin(), t_params);
  978. }
  979. static void save_function_params(Stack_Holder &t_s, std::vector<Boxed_Value> &&t_params)
  980. {
  981. for (auto &&param : t_params)
  982. {
  983. t_s.call_params.back().insert(t_s.call_params.back().begin(), std::move(param));
  984. }
  985. }
  986. static void save_function_params(Stack_Holder &t_s, const std::vector<Boxed_Value> &t_params)
  987. {
  988. t_s.call_params.back().insert(t_s.call_params.back().begin(), t_params.begin(), t_params.end());
  989. }
  990. void save_function_params(std::initializer_list<Boxed_Value> t_params)
  991. {
  992. save_function_params(*m_stack_holder, t_params);
  993. }
  994. void save_function_params(std::vector<Boxed_Value> &&t_params)
  995. {
  996. save_function_params(*m_stack_holder, std::move(t_params));
  997. }
  998. void save_function_params(const std::vector<Boxed_Value> &t_params)
  999. {
  1000. save_function_params(*m_stack_holder, t_params);
  1001. }
  1002. void new_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves)
  1003. {
  1004. if (t_s.call_depth == 0)
  1005. {
  1006. m_conversions.enable_conversion_saves(t_saves, true);
  1007. }
  1008. ++t_s.call_depth;
  1009. save_function_params(m_conversions.take_saves(t_saves));
  1010. }
  1011. void pop_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves)
  1012. {
  1013. --t_s.call_depth;
  1014. assert(t_s.call_depth >= 0);
  1015. if (t_s.call_depth == 0)
  1016. {
  1017. t_s.call_params.back().clear();
  1018. m_conversions.enable_conversion_saves(t_saves, false);
  1019. }
  1020. }
  1021. void new_function_call()
  1022. {
  1023. new_function_call(*m_stack_holder, m_conversions.conversion_saves());
  1024. }
  1025. void pop_function_call()
  1026. {
  1027. pop_function_call(*m_stack_holder, m_conversions.conversion_saves());
  1028. }
  1029. Stack_Holder &get_stack_holder()
  1030. {
  1031. return *m_stack_holder;
  1032. }
  1033. /// Returns the current stack
  1034. /// make const/non const versions
  1035. const StackData &get_stack_data() const
  1036. {
  1037. return m_stack_holder->stacks.back();
  1038. }
  1039. static StackData &get_stack_data(Stack_Holder &t_holder)
  1040. {
  1041. return t_holder.stacks.back();
  1042. }
  1043. StackData &get_stack_data()
  1044. {
  1045. return m_stack_holder->stacks.back();
  1046. }
  1047. parser::ChaiScript_Parser_Base &get_parser()
  1048. {
  1049. return m_parser.get();
  1050. }
  1051. private:
  1052. const std::vector<std::pair<std::string, Boxed_Value>> &get_boxed_functions_int() const
  1053. {
  1054. return m_state.m_boxed_functions;
  1055. }
  1056. std::vector<std::pair<std::string, Boxed_Value>> &get_boxed_functions_int()
  1057. {
  1058. return m_state.m_boxed_functions;
  1059. }
  1060. const std::vector<std::pair<std::string, Proxy_Function>> &get_function_objects_int() const
  1061. {
  1062. return m_state.m_function_objects;
  1063. }
  1064. std::vector<std::pair<std::string, Proxy_Function>> &get_function_objects_int()
  1065. {
  1066. return m_state.m_function_objects;
  1067. }
  1068. const std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> &get_functions_int() const
  1069. {
  1070. return m_state.m_functions;
  1071. }
  1072. std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> &get_functions_int()
  1073. {
  1074. return m_state.m_functions;
  1075. }
  1076. static bool function_less_than(const Proxy_Function &lhs, const Proxy_Function &rhs)
  1077. {
  1078. auto dynamic_lhs(std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(lhs));
  1079. auto dynamic_rhs(std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(rhs));
  1080. if (dynamic_lhs && dynamic_rhs)
  1081. {
  1082. if (dynamic_lhs->get_guard())
  1083. {
  1084. return dynamic_rhs->get_guard() ? false : true;
  1085. } else {
  1086. return false;
  1087. }
  1088. }
  1089. if (dynamic_lhs && !dynamic_rhs)
  1090. {
  1091. return false;
  1092. }
  1093. if (!dynamic_lhs && dynamic_rhs)
  1094. {
  1095. return true;
  1096. }
  1097. const auto &lhsparamtypes = lhs->get_param_types();
  1098. const auto &rhsparamtypes = rhs->get_param_types();
  1099. const auto lhssize = lhsparamtypes.size();
  1100. const auto rhssize = rhsparamtypes.size();
  1101. static const auto boxed_type = user_type<Boxed_Value>();
  1102. static const auto boxed_pod_type = user_type<Boxed_Number>();
  1103. for (size_t i = 1; i < lhssize && i < rhssize; ++i)
  1104. {
  1105. const Type_Info &lt = lhsparamtypes[i];
  1106. const Type_Info &rt = rhsparamtypes[i];
  1107. if (lt.bare_equal(rt) && lt.is_const() == rt.is_const())
  1108. {
  1109. continue; // The first two types are essentially the same, next iteration
  1110. }
  1111. // const is after non-const for the same type
  1112. if (lt.bare_equal(rt) && lt.is_const() && !rt.is_const())
  1113. {
  1114. return false;
  1115. }
  1116. if (lt.bare_equal(rt) && !lt.is_const())
  1117. {
  1118. return true;
  1119. }
  1120. // boxed_values are sorted last
  1121. if (lt.bare_equal(boxed_type))
  1122. {
  1123. return false;
  1124. }
  1125. if (rt.bare_equal(boxed_type))
  1126. {
  1127. return true;
  1128. }
  1129. if (lt.bare_equal(boxed_pod_type))
  1130. {
  1131. return false;
  1132. }
  1133. if (rt.bare_equal(boxed_pod_type))
  1134. {
  1135. return true;
  1136. }
  1137. // otherwise, we want to sort by typeid
  1138. return lt < rt;
  1139. }
  1140. return false;
  1141. }
  1142. template<typename Container, typename Key, typename Value>
  1143. static void add_keyed_value(Container &t_c, const Key &t_key, Value &&t_value)
  1144. {
  1145. auto itr = find_keyed_value(t_c, t_key);
  1146. if (itr == t_c.end()) {
  1147. t_c.reserve(t_c.size() + 1); // tightly control growth of memory usage here
  1148. t_c.emplace_back(t_key, std::forward<Value>(t_value));
  1149. } else {
  1150. typedef typename Container::value_type value_type;
  1151. *itr = value_type(t_key, std::forward<Value>(t_value));
  1152. }
  1153. }
  1154. template<typename Container, typename Key>
  1155. static typename Container::iterator find_keyed_value(Container &t_c, const Key &t_key)
  1156. {
  1157. return std::find_if(t_c.begin(), t_c.end(),
  1158. [&t_key](const typename Container::value_type &o) {
  1159. return o.first == t_key;
  1160. });
  1161. }
  1162. template<typename Container, typename Key>
  1163. static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key)
  1164. {
  1165. return std::find_if(t_c.begin(), t_c.end(),
  1166. [&t_key](const typename Container::value_type &o) {
  1167. return o.first == t_key;
  1168. });
  1169. }
  1170. template<typename Container, typename Key>
  1171. static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key, const size_t t_hint)
  1172. {
  1173. if (t_c.size() > t_hint && t_c[t_hint].first == t_key) {
  1174. return std::next(t_c.begin(), static_cast<typename std::iterator_traits<typename Container::const_iterator>::difference_type>(t_hint));
  1175. } else {
  1176. return find_keyed_value(t_c, t_key);
  1177. }
  1178. }
  1179. /// Implementation detail for adding a function.
  1180. /// \throws exception::name_conflict_error if there's a function matching the given one being added
  1181. void add_function(const Proxy_Function &t_f, const std::string &t_name)
  1182. {
  1183. chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
  1184. auto &funcs = get_functions_int();
  1185. auto itr = find_keyed_value(funcs, t_name);
  1186. Proxy_Function new_func =
  1187. [&]() -> Proxy_Function {
  1188. if (itr != funcs.end())
  1189. {
  1190. auto vec = *itr->second;
  1191. for (const auto &func : vec)
  1192. {
  1193. if ((*t_f) == *(func))
  1194. {
  1195. throw chaiscript::exception::name_conflict_error(t_name);
  1196. }
  1197. }
  1198. vec.reserve(vec.size() + 1); // tightly control vec growth
  1199. vec.push_back(t_f);
  1200. std::stable_sort(vec.begin(), vec.end(), &function_less_than);
  1201. itr->second = std::make_shared<std::vector<Proxy_Function>>(vec);
  1202. return std::make_shared<Dispatch_Function>(std::move(vec));
  1203. } else if (t_f->has_arithmetic_param()) {
  1204. // if the function is the only function but it also contains
  1205. // arithmetic operators, we must wrap it in a dispatch function
  1206. // to allow for automatic arithmetic type conversions
  1207. std::vector<Proxy_Function> vec({t_f});
  1208. funcs.emplace_back(t_name, std::make_shared<std::vector<Proxy_Function>>(vec));
  1209. return std::make_shared<Dispatch_Function>(std::move(vec));
  1210. } else {
  1211. funcs.emplace_back(t_name, std::make_shared<std::vector<Proxy_Function>>(std::initializer_list<Proxy_Function>({t_f})));
  1212. return t_f;
  1213. }
  1214. }();
  1215. add_keyed_value(get_boxed_functions_int(), t_name, const_var(new_func));
  1216. add_keyed_value(get_function_objects_int(), t_name, std::move(new_func));
  1217. }
  1218. mutable chaiscript::detail::threading::shared_mutex m_mutex;
  1219. Type_Conversions m_conversions;
  1220. chaiscript::detail::threading::Thread_Storage<Stack_Holder> m_stack_holder;
  1221. std::reference_wrapper<parser::ChaiScript_Parser_Base> m_parser;
  1222. mutable std::atomic_uint_fast32_t m_method_missing_loc = {0};
  1223. State m_state;
  1224. };
  1225. class Dispatch_State
  1226. {
  1227. public:
  1228. explicit Dispatch_State(Dispatch_Engine &t_engine)
  1229. : m_engine(t_engine),
  1230. m_stack_holder(t_engine.get_stack_holder()),
  1231. m_conversions(t_engine.conversions(), t_engine.conversions().conversion_saves())
  1232. {
  1233. }
  1234. Dispatch_Engine *operator->() const {
  1235. return &m_engine.get();
  1236. }
  1237. Dispatch_Engine &operator*() const {
  1238. return m_engine.get();
  1239. }
  1240. Stack_Holder &stack_holder() const {
  1241. return m_stack_holder.get();
  1242. }
  1243. const Type_Conversions_State &conversions() const {
  1244. return m_conversions;
  1245. }
  1246. Type_Conversions::Conversion_Saves &conversion_saves() const {
  1247. return m_conversions.saves();
  1248. }
  1249. Boxed_Value &add_get_object(const std::string &t_name, Boxed_Value obj) const {
  1250. return m_engine.get().add_get_object(t_name, std::move(obj), m_stack_holder.get());
  1251. }
  1252. void add_object(const std::string &t_name, Boxed_Value obj) const {
  1253. return m_engine.get().add_object(t_name, std::move(obj), m_stack_holder.get());
  1254. }
  1255. Boxed_Value get_object(const std::string &t_name, std::atomic_uint_fast32_t &t_loc) const {
  1256. return m_engine.get().get_object(t_name, t_loc, m_stack_holder.get());
  1257. }
  1258. private:
  1259. std::reference_wrapper<Dispatch_Engine> m_engine;
  1260. std::reference_wrapper<Stack_Holder> m_stack_holder;
  1261. Type_Conversions_State m_conversions;
  1262. };
  1263. }
  1264. }
  1265. #endif