PageRenderTime 56ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/unittests/compiled_tests.cpp

http://github.com/ChaiScript/ChaiScript
C++ | 1285 lines | 943 code | 279 blank | 63 comment | 86 complexity | d37d176f0199ff6e113e1ce4ff6bff44 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // All of these are necessary because of catch.hpp. It's OK, they'll be
  2. // caught in other cpp files if chaiscript causes them
  3. #ifdef _MSC_VER
  4. #pragma warning(push)
  5. #pragma warning(disable : 4062 4242 4566 4640 4702 6330 28251)
  6. #endif
  7. #ifdef __GNUC__
  8. #pragma GCC diagnostic push
  9. #pragma GCC diagnostic ignored "-Wunknown-pragmas"
  10. #pragma GCC diagnostic ignored "-Wparentheses"
  11. // This one is necessary for the const return non-reference test
  12. #pragma GCC diagnostic ignored "-Wignored-qualifiers"
  13. #endif
  14. #include <chaiscript/chaiscript.hpp>
  15. #include <chaiscript/chaiscript_basic.hpp>
  16. #include <chaiscript/dispatchkit/bootstrap_stl.hpp>
  17. #include <chaiscript/utility/utility.hpp>
  18. #include "../static_libs/chaiscript_parser.hpp"
  19. #include "../static_libs/chaiscript_stdlib.hpp"
  20. #define CATCH_CONFIG_MAIN
  21. #include <clocale>
  22. #include "catch.hpp"
  23. // lambda_tests
  24. TEST_CASE("C++11 Lambdas Can Be Registered") {
  25. // We cannot deduce the type of a lambda expression, you must either wrap it
  26. // in an std::function or provide the signature
  27. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  28. chai.add(chaiscript::fun([]() -> std::string { return "hello"; }), "f1");
  29. // wrap
  30. chai.add(chaiscript::fun(std::function<std::string()>([] { return "world"; })), "f2");
  31. CHECK(chai.eval<std::string>("f1()") == "hello");
  32. CHECK(chai.eval<std::string>("f2()") == "world");
  33. }
  34. TEST_CASE("Lambdas can return boolean") {
  35. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  36. // check lambdas returning bool from chaiscript:
  37. std::function<bool()> chai_function;
  38. CHECK_NOTHROW(chai_function = chai.eval<std::function<bool()>>("fun() { 42 != 0 }"));
  39. bool result = false;
  40. CHECK_NOTHROW(result = chai_function());
  41. CHECK(result == true);
  42. // check lambdas returning bool from C++:
  43. auto cpp_function = [](int x) { return x == 42; };
  44. CHECK_NOTHROW(chai.add(chaiscript::fun(cpp_function), "cpp_function"));
  45. CHECK_NOTHROW(result = chai.eval<bool>("cpp_function(314)"));
  46. CHECK(result == false);
  47. }
  48. // dynamic_object tests
  49. TEST_CASE("Dynamic_Object attributes can be shared with C++") {
  50. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  51. chai("attr bob::z; def bob::bob() { this.z = 10 }; auto x = bob()");
  52. chaiscript::dispatch::Dynamic_Object &mydo = chai.eval<chaiscript::dispatch::Dynamic_Object &>("x");
  53. CHECK(mydo.get_type_name() == "bob");
  54. CHECK(chaiscript::boxed_cast<int>(mydo.get_attr("z")) == 10);
  55. chai("x.z = 15");
  56. CHECK(chaiscript::boxed_cast<int>(mydo.get_attr("z")) == 15);
  57. int &z = chaiscript::boxed_cast<int &>(mydo.get_attr("z"));
  58. CHECK(z == 15);
  59. z = 20;
  60. CHECK(z == 20);
  61. CHECK(chaiscript::boxed_cast<int>(chai("x.z")) == 20);
  62. }
  63. TEST_CASE("Function objects can be created from chaiscript functions") {
  64. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  65. chai.eval("def func() { print(\"Hello World\"); } ");
  66. std::function<void()> f = chai.eval<std::function<void()>>("func");
  67. f();
  68. CHECK(chai.eval<std::function<std::string(int)>>("to_string")(6) == "6");
  69. CHECK(chai.eval<std::function<std::string(const chaiscript::Boxed_Value &)>>("to_string")(chaiscript::var(6)) == "6");
  70. }
  71. TEST_CASE("ChaiScript can be created and destroyed on heap") {
  72. auto *chai = new chaiscript::ChaiScript_Basic(create_chaiscript_stdlib(), create_chaiscript_parser());
  73. delete chai;
  74. }
  75. ///////// Arithmetic Conversions
  76. // Tests to make sure that type conversions happen only when they should
  77. void arithmetic_conversions_f1(int) {}
  78. void arithmetic_conversions_f4(std::string) {}
  79. void arithmetic_conversions_f2(int) {}
  80. void arithmetic_conversions_f3(double) {}
  81. void arithmetic_conversions_f_func_return(const std::function<unsigned int(unsigned long)> &f) {
  82. // test the ability to return an unsigned with auto conversion
  83. f(4);
  84. }
  85. TEST_CASE("Test automatic arithmetic conversions") {
  86. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  87. chai.add(chaiscript::fun(&arithmetic_conversions_f1), "f1");
  88. chai.add(chaiscript::fun(&arithmetic_conversions_f2), "f2");
  89. chai.add(chaiscript::fun(&arithmetic_conversions_f3), "f2");
  90. chai.add(chaiscript::fun(&arithmetic_conversions_f1), "f3");
  91. chai.add(chaiscript::fun(&arithmetic_conversions_f4), "f3");
  92. chai.add(chaiscript::fun(&arithmetic_conversions_f_func_return), "func_return");
  93. // no overloads
  94. chai.eval("f1(0)");
  95. chai.eval("f1(0l)");
  96. chai.eval("f1(0ul)");
  97. chai.eval("f1(0ll)");
  98. chai.eval("f1(0ull)");
  99. chai.eval("f1(0.0)");
  100. chai.eval("f1(0.0f)");
  101. chai.eval("f1(0.0l)");
  102. // expected overloads
  103. chai.eval("f2(1)");
  104. chai.eval("f2(1.0)");
  105. // 1 non-arithmetic overload
  106. chai.eval("f2(1.0)");
  107. // various options for returning with conversions from chaiscript
  108. chai.eval("func_return(fun(x) { return 5u; })");
  109. chai.eval("func_return(fun(x) { return 5; })");
  110. chai.eval("func_return(fun(x) { return 5.0f; })");
  111. CHECK_THROWS(chai.eval("f2(1.0l)"));
  112. }
  113. /////// Exception handling
  114. TEST_CASE("Generic exception handling with C++") {
  115. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  116. try {
  117. chai.eval("throw(runtime_error(\"error\"));");
  118. REQUIRE(false);
  119. } catch (const chaiscript::Boxed_Value &bv) {
  120. const std::exception &e = chai.boxed_cast<const std::exception &>(bv);
  121. CHECK(e.what() == std::string("error"));
  122. }
  123. }
  124. TEST_CASE("Throw an int") {
  125. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  126. try {
  127. chai.eval("throw(1)", chaiscript::exception_specification<int>());
  128. REQUIRE(false);
  129. } catch (int e) {
  130. CHECK(e == 1);
  131. }
  132. }
  133. TEST_CASE("Throw int or double") {
  134. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  135. try {
  136. chai.eval("throw(1.0)", chaiscript::exception_specification<int, double>());
  137. REQUIRE(false);
  138. } catch (const double e) {
  139. CHECK(e == Approx(1.0));
  140. }
  141. }
  142. TEST_CASE("Deduction of pointer return types") {
  143. int val = 5;
  144. int *val_ptr = &val;
  145. auto &val_ptr_ref = val_ptr;
  146. const auto &val_ptr_const_ref = val_ptr;
  147. auto get_val_ptr = [&]() -> int * { return val_ptr; };
  148. auto get_val_const_ptr = [&]() -> int const * { return val_ptr; };
  149. auto get_val_ptr_ref = [&]() -> int *& { return val_ptr_ref; };
  150. auto get_val_ptr_const_ref = [&]() -> int *const & { return val_ptr_const_ref; };
  151. // auto get_val_const_ptr_const_ref = [ref=std::cref(val_ptr)]() -> int const * const & { return ref.get(); };
  152. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  153. chai.add(chaiscript::fun(get_val_ptr), "get_val_ptr");
  154. chai.add(chaiscript::fun(get_val_const_ptr), "get_val_const_ptr");
  155. chai.add(chaiscript::fun(get_val_ptr_ref), "get_val_ptr_ref");
  156. chai.add(chaiscript::fun(get_val_ptr_const_ref), "get_val_ptr_const_ref");
  157. // chai.add(chaiscript::fun(get_val_const_ptr_const_ref), "get_val_const_ptr_const_ref");
  158. CHECK(chai.eval<int *>("get_val_ptr()") == &val);
  159. CHECK(*chai.eval<int *>("get_val_ptr()") == val);
  160. CHECK(chai.eval<int const *>("get_val_const_ptr()") == &val);
  161. CHECK(*chai.eval<int const *>("get_val_const_ptr()") == val);
  162. // note that we cannot maintain the references here,
  163. // chaiscript internals cannot handle that, effectively pointer to pointer
  164. CHECK(chai.eval<int *>("get_val_ptr_ref()") == &val);
  165. CHECK(*chai.eval<int *>("get_val_ptr_ref()") == val);
  166. CHECK(chai.eval<int *>("get_val_ptr_const_ref()") == &val);
  167. CHECK(*chai.eval<int *>("get_val_ptr_const_ref()") == val);
  168. // CHECK(chai.eval<int const *>("get_val_const_ptr_const_ref()") == &val);
  169. }
  170. TEST_CASE("Throw a runtime_error") {
  171. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  172. try {
  173. chai.eval("throw(runtime_error(\"error\"))",
  174. chaiscript::exception_specification<int, double, float, const std::string &, const std::exception &>());
  175. REQUIRE(false);
  176. } catch (const double) {
  177. REQUIRE(false);
  178. } catch (int) {
  179. REQUIRE(false);
  180. } catch (float) {
  181. REQUIRE(false);
  182. } catch (const std::string &) {
  183. REQUIRE(false);
  184. } catch (const std::exception &) {
  185. REQUIRE(true);
  186. }
  187. }
  188. TEST_CASE("Throw unhandled type") {
  189. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  190. try {
  191. chai.eval("throw(\"error\")", chaiscript::exception_specification<int, double, float, const std::exception &>());
  192. REQUIRE(false);
  193. } catch (double) {
  194. REQUIRE(false);
  195. } catch (int) {
  196. REQUIRE(false);
  197. } catch (float) {
  198. REQUIRE(false);
  199. } catch (const std::exception &) {
  200. REQUIRE(false);
  201. } catch (const chaiscript::Boxed_Value &) {
  202. REQUIRE(true);
  203. }
  204. }
  205. ///////////// Tests to make sure no arity, dispatch or guard errors leak up past eval
  206. int expected_eval_errors_test_one(const int &) {
  207. return 1;
  208. }
  209. TEST_CASE("No unexpected exceptions leak") {
  210. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  211. chai.add(chaiscript::fun(&expected_eval_errors_test_one), "test_fun");
  212. chai.eval("def guard_fun(i) : i.get_type_info().is_type_arithmetic() {} ");
  213. //// Dot notation
  214. // non-existent function
  215. CHECK_THROWS_AS(chai.eval("\"test\".test_one()"), chaiscript::exception::eval_error);
  216. // wrong parameter type
  217. CHECK_THROWS_AS(chai.eval("\"test\".test_fun()"), chaiscript::exception::eval_error);
  218. // wrong number of parameters
  219. CHECK_THROWS_AS(chai.eval("\"test\".test_fun(1)"), chaiscript::exception::eval_error);
  220. // guard failure
  221. CHECK_THROWS_AS(chai.eval("\"test\".guard_fun()"), chaiscript::exception::eval_error);
  222. // regular notation
  223. // non-existent function
  224. CHECK_THROWS_AS(chai.eval("test_one(\"test\")"), chaiscript::exception::eval_error);
  225. // wrong parameter type
  226. CHECK_THROWS_AS(chai.eval("test_fun(\"test\")"), chaiscript::exception::eval_error);
  227. // wrong number of parameters
  228. CHECK_THROWS_AS(chai.eval("test_fun(\"test\")"), chaiscript::exception::eval_error);
  229. // guard failure
  230. CHECK_THROWS_AS(chai.eval("guard_fun(\"test\")"), chaiscript::exception::eval_error);
  231. // index operator
  232. CHECK_THROWS_AS(chai.eval("var a = [1,2,3]; a[\"bob\"];"), chaiscript::exception::eval_error);
  233. // unary operator
  234. CHECK_THROWS_AS(chai.eval("++\"bob\""), chaiscript::exception::eval_error);
  235. // binary operator
  236. CHECK_THROWS_AS(chai.eval("\"bob\" + 1"), chaiscript::exception::eval_error);
  237. }
  238. //////// Tests to make sure that the order in which function dispatches occur is correct
  239. #include <chaiscript/utility/utility.hpp>
  240. int function_ordering_test_one(const int &) {
  241. return 1;
  242. }
  243. int function_ordering_test_two(int &) {
  244. return 2;
  245. }
  246. TEST_CASE("Function ordering") {
  247. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  248. chai.eval("def test_fun(x) { return 3; }");
  249. chai.eval("def test_fun(x) : x == \"hi\" { return 4; }");
  250. // chai.eval("def test_fun(x) { return 5; }");
  251. chai.add(chaiscript::fun(&function_ordering_test_one), "test_fun");
  252. chai.add(chaiscript::fun(&function_ordering_test_two), "test_fun");
  253. CHECK(chai.eval<int>("test_fun(1)") == 1);
  254. CHECK(chai.eval<int>("auto i = 1; test_fun(i)") == 2);
  255. CHECK(chai.eval<int>("test_fun(\"bob\")") == 3);
  256. CHECK(chai.eval<int>("test_fun(\"hi\")") == 4);
  257. }
  258. int functor_cast_test_call(const std::function<int(int)> &f, int val) {
  259. return f(val);
  260. }
  261. TEST_CASE("Functor cast") {
  262. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  263. chai.add(chaiscript::fun(&functor_cast_test_call), "test_call");
  264. chai.eval("def func(i) { return i * 6; };");
  265. int d = chai.eval<int>("test_call(func, 3)");
  266. CHECK(d == 3 * 6);
  267. }
  268. TEST_CASE("Non-ASCII characters in the middle of string") {
  269. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  270. CHECK_THROWS_AS(chai.eval<std::string>("prin\xeft \"Hello World\""), chaiscript::exception::eval_error);
  271. }
  272. TEST_CASE("Non-ASCII characters in the beginning of string") {
  273. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  274. CHECK_THROWS_AS(chai.eval<std::string>("\xefprint \"Hello World\""), chaiscript::exception::eval_error);
  275. }
  276. TEST_CASE("Non-ASCII characters in the end of string") {
  277. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  278. CHECK_THROWS_AS(chai.eval<std::string>("print \"Hello World\"\xef"), chaiscript::exception::eval_error);
  279. }
  280. TEST_CASE("BOM in string") {
  281. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  282. CHECK_THROWS_AS(chai.eval<std::string>("\xef\xbb\xbfprint \"Hello World\""), chaiscript::exception::eval_error);
  283. }
  284. int set_state_test_myfun() {
  285. return 2;
  286. }
  287. TEST_CASE("Set and restore chai state") {
  288. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  289. // save the initial state of globals and locals
  290. auto firststate = chai.get_state();
  291. std::map<std::string, chaiscript::Boxed_Value> locals = chai.get_locals();
  292. // add some new globals and locals
  293. chai.add(chaiscript::var(1), "i");
  294. chai.add(chaiscript::fun(&set_state_test_myfun), "myfun");
  295. CHECK(chai.eval<int>("myfun()") == 2);
  296. CHECK(chai.eval<int>("i") == 1);
  297. chai.set_state(firststate);
  298. // set state should have reverted the state of the functions and dropped
  299. // the 'myfun'
  300. CHECK_THROWS_AS(chai.eval<int>("myfun()"), chaiscript::exception::eval_error);
  301. // set state should not affect the local variables
  302. CHECK(chai.eval<int>("i") == 1);
  303. // After resetting the locals we expect the 'i' to be gone
  304. chai.set_locals(locals);
  305. CHECK_THROWS_AS(chai.eval<int>("i"), chaiscript::exception::eval_error);
  306. }
  307. //// Short comparisons
  308. class Short_Comparison_Test {
  309. public:
  310. Short_Comparison_Test()
  311. : value_(5) {
  312. }
  313. short get_value() const { return value_; }
  314. short value_;
  315. };
  316. TEST_CASE("Short comparison with int") {
  317. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  318. chai.add(chaiscript::user_type<Short_Comparison_Test>(), "Test");
  319. chai.add(chaiscript::constructor<Short_Comparison_Test()>(), "Test");
  320. chai.add(chaiscript::fun(&Short_Comparison_Test::get_value), "get_value");
  321. chai.eval("auto &t = Test();");
  322. CHECK(chai.eval<bool>("t.get_value() == 5"));
  323. }
  324. ///// Test lookup of type names
  325. class Type_Name_MyClass {
  326. };
  327. TEST_CASE("Test lookup of type names") {
  328. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  329. auto type = chaiscript::user_type<Type_Name_MyClass>();
  330. chai.add(type, "MyClass");
  331. CHECK(chai.get_type_name(type) == "MyClass");
  332. CHECK(chai.get_type_name<Type_Name_MyClass>() == "MyClass");
  333. }
  334. /////// make sure many chaiscript objects can exist simultaneously
  335. int simultaneous_chaiscript_do_something(int i) {
  336. return i + 2;
  337. }
  338. int simultaneous_chaiscript_do_something_else(int i) {
  339. return i * 2;
  340. }
  341. TEST_CASE("Simultaneous ChaiScript tests") {
  342. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  343. chai.add(chaiscript::fun(&simultaneous_chaiscript_do_something), "do_something");
  344. chai.add(chaiscript::var(1), "i");
  345. for (int i = 0; i < 10; ++i) {
  346. chaiscript::ChaiScript_Basic chai2(create_chaiscript_stdlib(), create_chaiscript_parser());
  347. chai2.add(chaiscript::fun(&simultaneous_chaiscript_do_something_else), "do_something_else");
  348. CHECK(chai.eval<int>("do_something(" + std::to_string(i) + ")") == i + 2);
  349. CHECK(chai2.eval<int>("do_something_else(" + std::to_string(i) + ")") == i * 2);
  350. CHECK_THROWS_AS(chai2.eval("do_something(1)"), chaiscript::exception::eval_error);
  351. CHECK_THROWS_AS(chai2.eval("i"), chaiscript::exception::eval_error);
  352. CHECK_NOTHROW(chai2.eval("do_something_else(1)"));
  353. }
  354. }
  355. /////////////// test utility functions
  356. class Utility_Test {
  357. public:
  358. void function() {}
  359. std::string function2() { return "Function2"; }
  360. void function3() {}
  361. std::string functionOverload(double) { return "double"; }
  362. std::string functionOverload(int) { return "int"; }
  363. };
  364. TEST_CASE("Utility_Test utility class wrapper") {
  365. chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module());
  366. using namespace chaiscript;
  367. /// \todo fix overload resolution for fun<>
  368. chaiscript::utility::add_class<Utility_Test>(
  369. *m,
  370. "Utility_Test",
  371. {constructor<Utility_Test()>(), constructor<Utility_Test(const Utility_Test &)>()},
  372. {{fun(&Utility_Test::function), "function"},
  373. {fun(&Utility_Test::function2), "function2"},
  374. {fun(&Utility_Test::function3), "function3"},
  375. {fun(static_cast<std::string (Utility_Test::*)(double)>(&Utility_Test::functionOverload)), "functionOverload"},
  376. {fun(static_cast<std::string (Utility_Test::*)(int)>(&Utility_Test::functionOverload)), "functionOverload"},
  377. {fun(static_cast<Utility_Test &(Utility_Test::*)(const Utility_Test &)>(&Utility_Test::operator=)), "="}
  378. });
  379. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  380. chai.add(m);
  381. CHECK(chai.eval<std::string>("auto t = Utility_Test(); t.function2(); ") == "Function2");
  382. CHECK(chai.eval<std::string>("auto t2 = Utility_Test(); t2.functionOverload(1); ") == "int");
  383. CHECK(chai.eval<std::string>("auto t3 = Utility_Test(); t3.functionOverload(1.1); ") == "double");
  384. chai.eval("t = Utility_Test();");
  385. }
  386. enum Utility_Test_Numbers {
  387. ONE,
  388. TWO,
  389. THREE
  390. };
  391. void do_something_with_enum_vector(const std::vector<Utility_Test_Numbers> &v) {
  392. CHECK(v.size() == 3);
  393. CHECK(v[0] == ONE);
  394. CHECK(v[1] == THREE);
  395. CHECK(v[2] == TWO);
  396. }
  397. TEST_CASE("Utility_Test utility class wrapper for enum") {
  398. chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module());
  399. using namespace chaiscript;
  400. chaiscript::utility::add_class<Utility_Test_Numbers>(*m,
  401. "Utility_Test_Numbers",
  402. {{ONE, "ONE"}, {TWO, "TWO"}, {THREE, "THREE"}
  403. });
  404. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  405. chai.add(m);
  406. CHECK(chai.eval<Utility_Test_Numbers>("ONE ") == 0);
  407. CHECK(chai.eval<Utility_Test_Numbers>("TWO ") == 1);
  408. CHECK(chai.eval<Utility_Test_Numbers>("THREE ") == 2);
  409. CHECK(chai.eval<bool>("ONE == 0"));
  410. chai.add(chaiscript::fun(&do_something_with_enum_vector), "do_something_with_enum_vector");
  411. chai.add(chaiscript::vector_conversion<std::vector<Utility_Test_Numbers>>());
  412. CHECK_NOTHROW(chai.eval("var a = [ONE, TWO, THREE]"));
  413. CHECK_NOTHROW(chai.eval("do_something_with_enum_vector([ONE, THREE, TWO])"));
  414. CHECK_NOTHROW(chai.eval("[ONE]"));
  415. const auto v = chai.eval<std::vector<Utility_Test_Numbers>>("a");
  416. CHECK(v.size() == 3);
  417. CHECK(v.at(1) == TWO);
  418. CHECK(chai.eval<bool>("ONE == ONE"));
  419. CHECK(chai.eval<bool>("ONE != TWO"));
  420. CHECK_NOTHROW(chai.eval("var o = ONE; o = TWO"));
  421. }
  422. ////// Object copy count test
  423. class Object_Copy_Count_Test {
  424. public:
  425. Object_Copy_Count_Test() {
  426. std::cout << "Object_Copy_Count_Test()\n";
  427. ++constructcount();
  428. }
  429. Object_Copy_Count_Test(const Object_Copy_Count_Test &) {
  430. std::cout << "Object_Copy_Count_Test(const Object_Copy_Count_Test &)\n";
  431. ++copycount();
  432. }
  433. Object_Copy_Count_Test(Object_Copy_Count_Test &&) {
  434. std::cout << "Object_Copy_Count_Test(Object_Copy_Count_Test &&)\n";
  435. ++movecount();
  436. }
  437. ~Object_Copy_Count_Test() {
  438. std::cout << "~Object_Copy_Count_Test()\n";
  439. ++destructcount();
  440. }
  441. static int &constructcount() {
  442. static int c = 0;
  443. return c;
  444. }
  445. static int &copycount() {
  446. static int c = 0;
  447. return c;
  448. }
  449. static int &movecount() {
  450. static int c = 0;
  451. return c;
  452. }
  453. static int &destructcount() {
  454. static int c = 0;
  455. return c;
  456. }
  457. };
  458. Object_Copy_Count_Test object_copy_count_create() {
  459. return Object_Copy_Count_Test();
  460. }
  461. TEST_CASE("Object copy counts") {
  462. chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module());
  463. m->add(chaiscript::user_type<Object_Copy_Count_Test>(), "Object_Copy_Count_Test");
  464. m->add(chaiscript::constructor<Object_Copy_Count_Test()>(), "Object_Copy_Count_Test");
  465. m->add(chaiscript::constructor<Object_Copy_Count_Test(const Object_Copy_Count_Test &)>(), "Object_Copy_Count_Test");
  466. m->add(chaiscript::fun(&object_copy_count_create), "create");
  467. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  468. chai.add(m);
  469. chai.eval(" { auto i = create(); } ");
  470. CHECK(Object_Copy_Count_Test::copycount() == 0);
  471. CHECK(Object_Copy_Count_Test::constructcount() == 1);
  472. CHECK(Object_Copy_Count_Test::destructcount() == 2);
  473. CHECK(Object_Copy_Count_Test::movecount() == 1);
  474. }
  475. ///////////////////// Object lifetime test 1
  476. class Object_Lifetime_Test {
  477. public:
  478. Object_Lifetime_Test() { ++count(); }
  479. Object_Lifetime_Test(const Object_Lifetime_Test &) { ++count(); }
  480. ~Object_Lifetime_Test() { --count(); }
  481. static int &count() {
  482. static int c = 0;
  483. return c;
  484. }
  485. };
  486. TEST_CASE("Object lifetime tests") {
  487. chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module());
  488. m->add(chaiscript::user_type<Object_Lifetime_Test>(), "Object_Lifetime_Test");
  489. m->add(chaiscript::constructor<Object_Lifetime_Test()>(), "Object_Lifetime_Test");
  490. m->add(chaiscript::constructor<Object_Lifetime_Test(const Object_Lifetime_Test &)>(), "Object_Lifetime_Test");
  491. m->add(chaiscript::fun(&Object_Lifetime_Test::count), "count");
  492. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  493. chai.add(m);
  494. CHECK(chai.eval<int>("count()") == 0);
  495. CHECK(chai.eval<int>("auto i = 0; { auto t = Object_Lifetime_Test(); } return i;") == 0);
  496. CHECK(chai.eval<int>("i = 0; { auto t = Object_Lifetime_Test(); i = count(); } return i;") == 1);
  497. CHECK(chai.eval<int>("i = 0; { auto t = Object_Lifetime_Test(); { auto t2 = Object_Lifetime_Test(); i = count(); } } return i;") == 2);
  498. CHECK(chai.eval<int>("i = 0; { auto t = Object_Lifetime_Test(); { auto t2 = Object_Lifetime_Test(); } i = count(); } return i;") == 1);
  499. CHECK(chai.eval<int>("i = 0; { auto t = Object_Lifetime_Test(); { auto t2 = Object_Lifetime_Test(); } } i = count(); return i;") == 0);
  500. }
  501. //// Object lifetime tests 2
  502. template<typename T>
  503. struct Object_Lifetime_Vector2 {
  504. Object_Lifetime_Vector2()
  505. : x(0)
  506. , y(0) {
  507. }
  508. Object_Lifetime_Vector2(T px, T py)
  509. : x(px)
  510. , y(py) {
  511. }
  512. Object_Lifetime_Vector2(const Object_Lifetime_Vector2 &cp) noexcept
  513. : x(cp.x)
  514. , y(cp.y) {
  515. }
  516. Object_Lifetime_Vector2 &operator+=(const Object_Lifetime_Vector2 &vec_r) {
  517. x += vec_r.x;
  518. y += vec_r.y;
  519. return *this;
  520. }
  521. Object_Lifetime_Vector2 operator+(const Object_Lifetime_Vector2 &vec_r) { return Object_Lifetime_Vector2(*this += vec_r); }
  522. Object_Lifetime_Vector2 &operator=(const Object_Lifetime_Vector2 &ver_r) {
  523. x = ver_r.x;
  524. y = ver_r.y;
  525. return *this;
  526. }
  527. T x;
  528. T y;
  529. };
  530. Object_Lifetime_Vector2<float> Object_Lifetime_Vector2_GetValue() {
  531. return Object_Lifetime_Vector2<float>(10, 15);
  532. }
  533. TEST_CASE("Object lifetime test 2") {
  534. chaiscript::ChaiScript_Basic _script(create_chaiscript_stdlib(), create_chaiscript_parser());
  535. // Registering stuff
  536. _script.add(chaiscript::user_type<Object_Lifetime_Vector2<float>>(), "Object_Lifetime_Vector2f");
  537. _script.add(chaiscript::constructor<Object_Lifetime_Vector2<float>()>(), "Object_Lifetime_Vector2f");
  538. _script.add(chaiscript::constructor<Object_Lifetime_Vector2<float>(float, float)>(), "Object_Lifetime_Vector2f");
  539. _script.add(chaiscript::constructor<Object_Lifetime_Vector2<float>(const Object_Lifetime_Vector2<float> &)>(), "Object_Lifetime_Vector2f");
  540. _script.add(chaiscript::fun(&Object_Lifetime_Vector2<float>::x), "x");
  541. _script.add(chaiscript::fun(&Object_Lifetime_Vector2<float>::y), "y");
  542. _script.add(chaiscript::fun(&Object_Lifetime_Vector2<float>::operator+), "+");
  543. _script.add(chaiscript::fun(&Object_Lifetime_Vector2<float>::operator+=), "+=");
  544. _script.add(chaiscript::fun(&Object_Lifetime_Vector2<float>::operator=), "=");
  545. _script.add(chaiscript::fun(&Object_Lifetime_Vector2_GetValue), "getValue");
  546. _script.eval(R"(
  547. var test = 0.0
  548. var test2 = Object_Lifetime_Vector2f(10,10)
  549. test = getValue().x
  550. print(test)
  551. print(test2.x)
  552. )");
  553. CHECK(_script.eval<std::string>("to_string(test)") == "10");
  554. CHECK(_script.eval<std::string>("to_string(test2.x)") == "10");
  555. }
  556. ///// Non-polymorphic base class conversions
  557. class Non_Poly_Base {
  558. };
  559. class Non_Poly_Derived : public Non_Poly_Base {
  560. };
  561. int myfunction(Non_Poly_Base *) {
  562. return 2;
  563. }
  564. TEST_CASE("Test Derived->Base with non-polymorphic classes") {
  565. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  566. chai.add(chaiscript::base_class<Non_Poly_Base, Non_Poly_Derived>());
  567. Non_Poly_Derived d;
  568. chai.add(chaiscript::var(&d), "d");
  569. chai.add(chaiscript::fun(&myfunction), "myfunction");
  570. CHECK(chai.eval<int>("myfunction(d)") == 2);
  571. }
  572. struct TestCppVariableScope {
  573. void print() { std::cout << "Printed" << std::endl; }
  574. };
  575. TEST_CASE("Variable Scope When Calling From C++") {
  576. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  577. chai.add(chaiscript::user_type<TestCppVariableScope>(), "Test");
  578. chai.add(chaiscript::constructor<TestCppVariableScope()>(), "Test");
  579. chai.add(chaiscript::fun(&TestCppVariableScope::print), "print");
  580. chai.eval(R"(var t := Test();
  581. def func()
  582. {
  583. t.print();
  584. }
  585. )");
  586. CHECK_THROWS(chai.eval("func()"));
  587. chai.eval("dump_object(t)");
  588. auto func = chai.eval<std::function<void()>>("func");
  589. CHECK_THROWS(func());
  590. }
  591. TEST_CASE("Variable Scope When Calling From C++ 2") {
  592. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  593. chai.eval("var obj = 2;");
  594. auto func = chai.eval<std::function<void()>>("fun(){ return obj; }");
  595. CHECK_THROWS(func());
  596. }
  597. void ulonglong(unsigned long long i) {
  598. std::cout << i << '\n';
  599. }
  600. void longlong(long long i) {
  601. std::cout << i << '\n';
  602. }
  603. TEST_CASE("Test long long dispatch") {
  604. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  605. chai.add(chaiscript::fun(&longlong), "longlong");
  606. chai.add(chaiscript::fun(&ulonglong), "ulonglong");
  607. chai.eval("longlong(15)");
  608. chai.eval("ulonglong(15)");
  609. }
  610. struct Returned_Converted_Config {
  611. int num_iterations;
  612. int something_else;
  613. std::string a_string;
  614. std::function<int(const std::string &)> a_function;
  615. };
  616. TEST_CASE("Return of converted type from script") {
  617. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  618. chai.add(chaiscript::constructor<Returned_Converted_Config()>(), "Returned_Converted_Config");
  619. chai.add(chaiscript::fun(&Returned_Converted_Config::num_iterations), "num_iterations");
  620. chai.add(chaiscript::fun(&Returned_Converted_Config::something_else), "something_else");
  621. chai.add(chaiscript::fun(&Returned_Converted_Config::a_string), "a_string");
  622. chai.add(chaiscript::fun(&Returned_Converted_Config::a_function), "a_function");
  623. chai.add(chaiscript::vector_conversion<std::vector<Returned_Converted_Config>>());
  624. auto c = chai.eval<std::vector<Returned_Converted_Config>>(R"(
  625. var c = Returned_Converted_Config();
  626. c.num_iterations = 5;
  627. c.something_else = c.num_iterations * 2;
  628. c.a_string = "string";
  629. c.a_function = fun(s) { s.size(); }
  630. print("making vector");
  631. var v = [];
  632. print("adding config item");
  633. v.push_back_ref(c);
  634. print("returning vector");
  635. v;
  636. )");
  637. std::cout << typeid(decltype(c)).name() << std::endl;
  638. std::cout << "Info: " << c.size() << " " << &c[0] << std::endl;
  639. std::cout << "num_iterations " << c[0].num_iterations << '\n'
  640. << "something_else " << c[0].something_else << '\n'
  641. << "a_string " << c[0].a_string << '\n'
  642. << "a_function " << c[0].a_function("bob") << '\n';
  643. chai.add(chaiscript::user_type<Returned_Converted_Config>(), "Returned_Converted_Config");
  644. }
  645. int get_value_a(const std::map<std::string, int> &t_m) {
  646. return t_m.at("a");
  647. }
  648. TEST_CASE("Map conversions") {
  649. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  650. chai.add(chaiscript::map_conversion<std::map<std::string, int>>());
  651. chai.add(chaiscript::fun(&get_value_a), "get_value_a");
  652. const auto c = chai.eval<int>(R"(
  653. var m = ["a": 42];
  654. get_value_a(m);
  655. )");
  656. CHECK(c == 42);
  657. }
  658. TEST_CASE("Parse floats with non-posix locale") {
  659. #ifdef CHAISCRIPT_MSVC
  660. std::setlocale(LC_ALL, "en-ZA");
  661. #else
  662. std::setlocale(LC_ALL, "en_ZA.utf8");
  663. #endif
  664. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  665. const double parsed = chai.eval<double>("print(1.3); 1.3");
  666. CHECK(parsed == Approx(1.3));
  667. const std::string str = chai.eval<std::string>("to_string(1.3)");
  668. CHECK(str == "1.3");
  669. }
  670. bool FindBitmap(int &ox, int &oy, long) {
  671. ox = 1;
  672. oy = 2;
  673. return true;
  674. }
  675. TEST_CASE("Mismatched numeric types only convert necessary params") {
  676. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  677. chai.add(chaiscript::fun(&FindBitmap), "FindBitmap");
  678. int x = 0;
  679. int y = 0;
  680. chai.add(chaiscript::var(&x), "x");
  681. chai.add(chaiscript::var(&y), "y");
  682. chai.eval("if ( FindBitmap ( x, y, 0) ) { print(\"found at \" + to_string(x) + \", \" + to_string(y))}");
  683. CHECK(x == 1);
  684. CHECK(y == 2);
  685. }
  686. TEST_CASE("type_conversion to bool") {
  687. auto module = std::make_shared<chaiscript::Module>();
  688. struct T {
  689. operator bool() const { return true; }
  690. };
  691. module->add(chaiscript::type_conversion<T, bool>());
  692. }
  693. TEST_CASE("Make sure ChaiScript object still compiles / executes") {
  694. chaiscript::ChaiScript chai;
  695. }
  696. struct Count_Tracer {
  697. int count = 0;
  698. template<typename T>
  699. void trace(const chaiscript::detail::Dispatch_State &, const chaiscript::eval::AST_Node_Impl<T> *) {
  700. ++count;
  701. }
  702. };
  703. TEST_CASE("Test count tracer") {
  704. using Parser_Type = chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Tracer<Count_Tracer>, chaiscript::optimizer::Optimizer_Default>;
  705. chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library(), std::make_unique<Parser_Type>());
  706. Parser_Type &parser = dynamic_cast<Parser_Type &>(chai.get_parser());
  707. const auto count = parser.get_tracer().count;
  708. chai.eval("");
  709. CHECK(parser.get_tracer().count > count);
  710. }
  711. TEST_CASE("Test stdlib options") {
  712. const auto test_has_external_scripts = [](chaiscript::ChaiScript_Basic &chai) {
  713. CHECK_NOTHROW(chai.eval("`use`"));
  714. CHECK_NOTHROW(chai.eval("`eval_file`"));
  715. };
  716. const auto test_no_external_scripts = [](chaiscript::ChaiScript_Basic &chai) {
  717. CHECK_THROWS(chai.eval("`use`"));
  718. CHECK_THROWS(chai.eval("`eval_file`"));
  719. };
  720. const auto test_has_load_modules = [](chaiscript::ChaiScript_Basic &chai) { CHECK_NOTHROW(chai.eval("`load_module`")); };
  721. const auto test_no_load_modules = [](chaiscript::ChaiScript_Basic &chai) { CHECK_THROWS(chai.eval("`load_module`")); };
  722. SECTION("Defaults") {
  723. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  724. test_has_external_scripts(chai);
  725. test_has_load_modules(chai);
  726. }
  727. SECTION("Load_Modules, External_Scripts") {
  728. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),
  729. create_chaiscript_parser(),
  730. {},
  731. {},
  732. {chaiscript::Options::Load_Modules, chaiscript::Options::External_Scripts});
  733. test_has_external_scripts(chai);
  734. test_has_load_modules(chai);
  735. }
  736. SECTION("No_Load_Modules, No_External_Scripts") {
  737. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),
  738. create_chaiscript_parser(),
  739. {},
  740. {},
  741. {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts});
  742. test_no_external_scripts(chai);
  743. test_no_load_modules(chai);
  744. }
  745. SECTION("No_Load_Modules, Load_Modules") {
  746. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),
  747. create_chaiscript_parser(),
  748. {},
  749. {},
  750. {chaiscript::Options::No_Load_Modules, chaiscript::Options::Load_Modules});
  751. test_no_external_scripts(chai);
  752. test_no_load_modules(chai);
  753. }
  754. SECTION("No_External_Scripts, External_Scripts") {
  755. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),
  756. create_chaiscript_parser(),
  757. {},
  758. {},
  759. {chaiscript::Options::No_External_Scripts, chaiscript::Options::External_Scripts});
  760. test_no_external_scripts(chai);
  761. test_no_load_modules(chai);
  762. }
  763. SECTION("No_External_Scripts, Load_Modules") {
  764. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),
  765. create_chaiscript_parser(),
  766. {},
  767. {},
  768. {chaiscript::Options::No_External_Scripts, chaiscript::Options::Load_Modules});
  769. test_no_external_scripts(chai);
  770. test_has_load_modules(chai);
  771. }
  772. SECTION("External_Scripts, No_Load_Modules") {
  773. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),
  774. create_chaiscript_parser(),
  775. {},
  776. {},
  777. {chaiscript::Options::External_Scripts, chaiscript::Options::No_Load_Modules});
  778. test_has_external_scripts(chai);
  779. test_no_load_modules(chai);
  780. }
  781. }
  782. void uservalueref(int &&) {}
  783. void usemoveonlytype(std::unique_ptr<int> &&) {}
  784. TEST_CASE("Pass r-value reference to func") {
  785. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  786. chai.add(chaiscript::fun(&uservalueref), "uservalueref");
  787. chai.add(chaiscript::fun(&usemoveonlytype), "usemoveonlytype");
  788. chai.add(chaiscript::var(std::make_unique<int>(1)), "iptr");
  789. chai.eval("usemoveonlytype(iptr)");
  790. }
  791. TEST_CASE("Use unique_ptr") {
  792. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  793. chai.add(chaiscript::fun([](int &i) { ++i; }), "inci");
  794. chai.add(chaiscript::fun([](int i) { ++i; }), "copyi");
  795. chai.add(chaiscript::fun([](int *i) { ++(*i); }), "derefi");
  796. chai.add(chaiscript::fun([](const std::unique_ptr<int> &i) { ++(*i); }), "constrefuniqptri");
  797. chai.add(chaiscript::fun([](std::unique_ptr<int> &i) { ++(*i); }), "refuniqptri");
  798. chai.add(chaiscript::fun([](std::unique_ptr<int> &&i) { ++(*i); }), "rvaluniqptri");
  799. chai.add(chaiscript::var(std::make_unique<int>(1)), "iptr");
  800. CHECK(chai.eval<int>("iptr") == 1);
  801. chai.eval("inci(iptr)");
  802. CHECK(chai.eval<int>("iptr") == 2);
  803. chai.eval("copyi(iptr)");
  804. CHECK(chai.eval<int>("iptr") == 2);
  805. chai.eval("derefi(iptr)");
  806. CHECK(chai.eval<int>("iptr") == 3);
  807. chai.eval("constrefuniqptri(iptr)");
  808. CHECK(chai.eval<int>("iptr") == 4);
  809. chai.eval("refuniqptri(iptr)");
  810. CHECK(chai.eval<int>("iptr") == 5);
  811. chai.eval("rvaluniqptri(iptr)");
  812. CHECK(chai.eval<int>("iptr") == 6);
  813. }
  814. class Unique_Ptr_Test_Class {
  815. public:
  816. Unique_Ptr_Test_Class() = default;
  817. Unique_Ptr_Test_Class(const Unique_Ptr_Test_Class &) = default;
  818. Unique_Ptr_Test_Class(Unique_Ptr_Test_Class &&) = default;
  819. Unique_Ptr_Test_Class &operator=(const Unique_Ptr_Test_Class &) = default;
  820. Unique_Ptr_Test_Class &operator=(Unique_Ptr_Test_Class &&) = default;
  821. virtual ~Unique_Ptr_Test_Class() = default;
  822. int getI() const { return 5; }
  823. };
  824. std::unique_ptr<Unique_Ptr_Test_Class> make_Unique_Ptr_Test_Class() {
  825. return std::make_unique<Unique_Ptr_Test_Class>();
  826. }
  827. TEST_CASE("Call methods through unique_ptr") {
  828. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  829. chai.add(chaiscript::var(std::make_unique<Unique_Ptr_Test_Class>()), "uptr");
  830. chai.add(chaiscript::fun(make_Unique_Ptr_Test_Class), "make_Unique_Ptr_Test_Class");
  831. chai.add(chaiscript::fun(&Unique_Ptr_Test_Class::getI), "getI");
  832. CHECK(chai.eval<int>("uptr.getI()") == 5);
  833. CHECK(chai.eval<int>("var uptr2 = make_Unique_Ptr_Test_Class(); uptr2.getI()") == 5);
  834. }
  835. class Unique_Ptr_Test_Base_Class {
  836. public:
  837. int getI() const { return 5; }
  838. };
  839. class Unique_Ptr_Test_Derived_Class : public Unique_Ptr_Test_Base_Class {
  840. };
  841. std::unique_ptr<Unique_Ptr_Test_Derived_Class> make_Unique_Ptr_Test_Derived_Class() {
  842. return std::make_unique<Unique_Ptr_Test_Derived_Class>();
  843. }
  844. TEST_CASE("Call methods on base class through unique_ptr<derived>") {
  845. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  846. chai.add(chaiscript::var(std::make_unique<Unique_Ptr_Test_Derived_Class>()), "uptr");
  847. chai.add(chaiscript::fun(make_Unique_Ptr_Test_Derived_Class), "make_Unique_Ptr_Test_Derived_Class");
  848. chai.add(chaiscript::fun(&Unique_Ptr_Test_Base_Class::getI), "getI");
  849. chai.add(chaiscript::base_class<Unique_Ptr_Test_Base_Class, Unique_Ptr_Test_Derived_Class>());
  850. CHECK(chai.eval<int>("uptr.getI()") == 5);
  851. CHECK(chai.eval<int>("var uptr2 = make_Unique_Ptr_Test_Derived_Class(); uptr2.getI()") == 5);
  852. }
  853. class A {
  854. public:
  855. A() = default;
  856. A(const A &) = default;
  857. A(A &&) = default;
  858. A &operator=(const A &) = default;
  859. A &operator=(A &&) = default;
  860. virtual ~A() = default;
  861. };
  862. class B : public A {
  863. public:
  864. B() = default;
  865. };
  866. TEST_CASE("Test typed chaiscript functions to perform conversions") {
  867. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  868. //-------------------------------------------------------------------------
  869. chai.add(chaiscript::user_type<A>(), "A");
  870. chai.add(chaiscript::user_type<B>(), "B");
  871. chai.add(chaiscript::base_class<A, B>());
  872. chai.add(chaiscript::fun([](const B &) {}), "CppFunctWithBArg");
  873. chai.add(chaiscript::fun([]() -> std::shared_ptr<A> { return (std::shared_ptr<A>(new B())); }), "Create");
  874. chai.eval(R"(
  875. var inst = Create() // A*
  876. // it prints "A"
  877. inst.type_name().print()
  878. // Ok it is casted using conversion
  879. CppFunctWithBArg(inst)
  880. // Define a function with B as argument
  881. def ChaiFuncWithBArg(B inst)
  882. {
  883. print("ok")
  884. }
  885. // don't work
  886. ChaiFuncWithBArg(inst)
  887. )");
  888. }
  889. struct Reference_MyClass {
  890. Reference_MyClass(double &t_x)
  891. : x(t_x) {
  892. }
  893. double &x;
  894. };
  895. TEST_CASE("Test reference member being registered") {
  896. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  897. // Note, C++ will not allow us to do this:
  898. // chai.add(chaiscript::fun(&Reference_MyClass::x) , "x");
  899. chai.add(chaiscript::fun([](Reference_MyClass &r) -> decltype(auto) { return (r.x); }), "x");
  900. chai.add(chaiscript::fun([](const Reference_MyClass &r) -> decltype(auto) { return (r.x); }), "x");
  901. double d;
  902. chai.add(chaiscript::var(Reference_MyClass(d)), "ref");
  903. chai.eval("ref.x = 2.3");
  904. CHECK(d == Approx(2.3));
  905. }
  906. // starting with C++20 u8"" strings cannot be compared with std::string
  907. // and the support for std::u8strings is still terrible.
  908. TEST_CASE("Test unicode matches C++") {
  909. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  910. CHECK("\U000000AC" == chai.eval<std::string>(R"("\U000000AC")"));
  911. CHECK("\xF0\x9F\x8D\x8C" == chai.eval<std::string>(R"("\xF0\x9F\x8D\x8C")"));
  912. CHECK("\U0001F34C" == chai.eval<std::string>(R"("\U0001F34C")"));
  913. CHECK("\u2022" == chai.eval<std::string>(R"("\u2022")"));
  914. }
  915. const int add_3(const int &i) {
  916. return i + 3;
  917. }
  918. TEST_CASE("Test returning by const non-reference") {
  919. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  920. // Note, C++ will not allow us to do this:
  921. // chai.add(chaiscript::fun(&Reference_MyClass::x) , "x");
  922. chai.add(chaiscript::fun(&add_3), "add_3");
  923. auto v = chai.eval<int>("add_3(12)");
  924. CHECK(v == 15);
  925. }
  926. struct MyException : std::runtime_error {
  927. using std::runtime_error::runtime_error;
  928. int value = 5;
  929. };
  930. void throws_a_thing() {
  931. throw MyException("Hello World");
  932. }
  933. TEST_CASE("Test throwing and catching custom exception") {
  934. chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
  935. chai.add(chaiscript::user_type<MyException>(), "MyException");
  936. chai.add(chaiscript::base_class<std::runtime_error, MyException>()); // be sure to register base class relationship
  937. chai.add(chaiscript::fun(&throws_a_thing), "throws_a_thing");
  938. chai.add(chaiscript::fun(&MyException::value), "value");
  939. const auto s = chai.eval<std::string>("fun(){ try { throws_a_thing(); } catch (MyException ex) { return ex.what(); } }()");
  940. CHECK(s == "Hello World");
  941. // this has an explicit clone to prevent returning a pointer to the `value` from inside of MyException
  942. const auto i
  943. = chai.eval<int>("fun(){ try { throws_a_thing(); } catch (MyException ex) { var v = clone(ex.value); print(v); return v; } }()");
  944. CHECK(i == 5);
  945. }
  946. TEST_CASE("Test ability to get 'use' function from default construction") {
  947. chaiscript::ChaiScript chai;
  948. const auto use_function = chai.eval<std::function<chaiscript::Boxed_Value(const std::string &)>>("use");
  949. }
  950. TEST_CASE("Throw an exception when trying to add same conversion twice") {
  951. struct my_int {
  952. int value;
  953. my_int(int val)
  954. : value(val) {
  955. }
  956. };
  957. chaiscript::ChaiScript chai;
  958. chai.add(chaiscript::type_conversion<int, my_int>([](int x) {
  959. std::cout << "My_int type conversion 1\n";
  960. return my_int(x);
  961. }));
  962. CHECK_THROWS_AS(chai.add(chaiscript::type_conversion<int, my_int>([](int x) {
  963. std::cout << "My_int type conversion 2\n";
  964. return my_int(x);
  965. })),
  966. chaiscript::exception::conversion_error);
  967. }
  968. TEST_CASE("Test if non copyable/movable types can be registered") {
  969. struct Noncopyable {
  970. Noncopyable() { str = "test"; }
  971. Noncopyable(const Noncopyable &) = delete;
  972. Noncopyable &operator=(const Noncopyable &) = delete;
  973. std::string str;
  974. };
  975. struct Nonmovable {
  976. Nonmovable() { str = "test"; }
  977. Nonmovable(Nonmovable &&) = delete;
  978. Nonmovable &operator=(Nonmovable &&) = delete;
  979. std::string str;
  980. };
  981. struct Nothing {
  982. Nothing() { str = "test"; }
  983. Nothing(Nothing &&) = delete;
  984. Nothing &operator=(Nothing &&) = delete;
  985. Nothing(const Nothing &) = delete;
  986. Nothing &operator=(const Nothing &) = delete;
  987. std::string str;
  988. };
  989. chaiscript::ChaiScript chai;
  990. chai.add(chaiscript::user_type<Noncopyable>(), "Noncopyable");
  991. chai.add(chaiscript::constructor<Noncopyable()>(), "Noncopyable");
  992. chai.add(chaiscript::user_type<Nonmovable>(), "Nonmovable");
  993. chai.add(chaiscript::constructor<Nonmovable()>(), "Nonmovable");
  994. chai.add(chaiscript::user_type<Nothing>(), "Nothing");
  995. chai.add(chaiscript::constructor<Nothing()>(), "Nothing");
  996. }