diff --git a/CMakeLists.txt b/CMakeLists.txt index 32601c1..09444f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,6 +208,18 @@ if(BUILD_TESTING) target_link_libraries(expected_eval_errors_test ${LIBS}) add_test(NAME Expected_Eval_Errors_Test COMMAND expected_eval_errors_test) + add_executable(set_state_test unittests/set_state_test.cpp) + target_link_libraries(set_state_test ${LIBS}) + add_test(NAME Set_State_Test COMMAND set_state_test) + + add_executable(simultaneous_chaiscript_test unittests/simultaneous_chaiscript_test.cpp) + target_link_libraries(simultaneous_chaiscript_test ${LIBS}) + add_test(NAME Simultaneous_Chaiscript_Test COMMAND simultaneous_chaiscript_test) + + add_executable(c_linkage_test unittests/c_linkage_test.cpp) + target_link_libraries(c_linkage_test ${LIBS}) + add_test(NAME C_Linkage_Test COMMAND c_linkage_test) + if (MULTITHREAD_SUPPORT_ENABLED) add_executable(multithreaded_test unittests/multithreaded_test.cpp) target_link_libraries(multithreaded_test ${LIBS}) diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 380b6eb..20aee70 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -140,7 +140,10 @@ /// /// \subsubsection addingobjects Adding Objects /// -/// Named objects can be created with the chaiscript::var function. +/// Named objects can be created with the chaiscript::var function. Note: adding a object +/// adds it to the current thread scope, not to a global scope. If you have multiple +/// threads that need to access the same variables you will need to add them +/// separately for each thread, from the thread itself. /// /// \code /// using namespace chaiscript; diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 5a764e5..e6f9e90 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -673,6 +673,27 @@ namespace chaiscript return functions.find(name) != functions.end(); } + /// \returns All values in the local thread state, added through the add() function + std::map get_locals() const + { + StackData &stack = get_stack_data(); + Scope &scope = stack.front(); + return scope; + } + + /// \brief Sets all of the locals for the current thread state. + /// + /// \param[in] t_locals The map set of variables to replace the current state with + /// + /// Any existing locals are removed and the given set of variables is added + void set_locals(const std::map &t_locals) + { + StackData &stack = get_stack_data(); + Scope &scope = stack.front(); + scope = t_locals; + } + + /// /// Get a map of all objects that can be seen from the current scope in a scripting context diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index c3c43e4..bacc80d 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -522,8 +522,12 @@ namespace chaiscript std::set active_loaded_modules; }; - /// \brief Returns a state object that represents the current state of the system - /// \return Current state of the system + /// \brief Returns a state object that represents the current state of the global system + /// + /// The global system includes the reserved words, global const objects, functions and types. + /// local variables are thread specific and not included. + /// + /// \return Current state of the global system /// /// \b Example: /// @@ -544,6 +548,10 @@ namespace chaiscript } /// \brief Sets the state of the system + /// + /// The global system includes the reserved words, global objects, functions and types. + /// local variables are thread specific and not included. + /// /// \param[in] t_state New state to set /// /// \b Example: @@ -563,7 +571,23 @@ namespace chaiscript m_engine.set_state(t_state.engine_state); } - /// \brief Adds a type, function or object to ChaiScript + /// \returns All values in the local thread state, added through the add() function + std::map get_locals() const + { + return m_engine.get_locals(); + } + + /// \brief Sets all of the locals for the current thread state. + /// + /// \param[in] t_locals The map set of variables to replace the current state with + /// + /// Any existing locals are removed and the given set of variables is added + void set_locals(const std::map &t_locals) + { + m_engine.set_locals(t_locals); + } + + /// \brief Adds a type, function or object to ChaiScript. Objects are added to the local thread state. /// \param[in] t_t Item to add /// \param[in] t_name Name of item to add /// \returns Reference to current ChaiScript object diff --git a/unittests/c_linkage_test.cpp b/unittests/c_linkage_test.cpp new file mode 100644 index 0000000..833e400 --- /dev/null +++ b/unittests/c_linkage_test.cpp @@ -0,0 +1,20 @@ +#include + + +extern "C" +{ + int dosomething(int i) + { + return i % 2; + } +} + +int main() +{ + + chaiscript::ChaiScript chai; + chai.add(chaiscript::fun(&dosomething), "dosomething"); + + return chai.eval("dosomething(101)") == 101 % 2?EXIT_SUCCESS:EXIT_FAILURE; + +} diff --git a/unittests/set_state_test.cpp b/unittests/set_state_test.cpp new file mode 100644 index 0000000..5c745b9 --- /dev/null +++ b/unittests/set_state_test.cpp @@ -0,0 +1,61 @@ +#include + + +int myfun() +{ + return 2; +} + +int main() +{ + + chaiscript::ChaiScript chai; + + // save the initial state of globals and locals + chaiscript::ChaiScript::State firststate = chai.get_state(); + std::map locals = chai.get_locals(); + + // add some new globals and locals + chai.add(chaiscript::var(1), "i"); + + chai.add(chaiscript::fun(&myfun), "myfun"); + + + bool didcall = chai.eval("myfun()") == 2; + + bool hadi = chai.eval("i") == 1; + + chai.set_state(firststate); + + // set state should have reverted the state of the functions and dropped + // the 'myfun' + bool didnotcall = false; + + try { + chai.eval("myfun()"); + } catch (const chaiscript::exception::eval_error &) { + didnotcall = true; + } + + // set state should not affect the local variables + bool stillhasid = chai.eval("i") == 1; + + // After resetting the locals we expect the 'i' to be gone + chai.set_locals(locals); + + + bool nolongerhasid = false; + + try { + chai.eval("i"); + } catch (const chaiscript::exception::eval_error &) { + nolongerhasid = true; + } + + if (didcall && hadi && didnotcall && stillhasid && nolongerhasid) + { + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } +} diff --git a/unittests/simultaneous_chaiscript_test.cpp b/unittests/simultaneous_chaiscript_test.cpp new file mode 100644 index 0000000..238f70a --- /dev/null +++ b/unittests/simultaneous_chaiscript_test.cpp @@ -0,0 +1,60 @@ +#include + +int dosomething(int i) +{ + return i + 2; +} + +int dosomethingelse(int i) +{ + return i * 2; +} + + + +int main() +{ + chaiscript::ChaiScript chai; + chai.add(chaiscript::fun(&dosomething), "dosomething"); + chai.add(chaiscript::var(1), "i"); + + for (int i = 0; i < 10; ++i) + { + chaiscript::ChaiScript chai2; + chai2.add(chaiscript::fun(&dosomethingelse), "dosomethingelse"); + + std::stringstream ss; + ss << i; + + if (chai.eval("dosomething(" + ss.str() + ")") != i + 2) + { + return EXIT_FAILURE; + } + + if (chai2.eval("dosomethingelse(" + ss.str() + ")") != i * 2) + { + return EXIT_FAILURE; + } + + try { + chai2.eval("dosomething(1)"); + return EXIT_FAILURE; // should not get here + } catch (const chaiscript::exception::eval_error &) { + // nothing to do, expected case + } + + try { + chai2.eval("i"); + return EXIT_FAILURE; // should not get here + } catch (const chaiscript::exception::eval_error &) { + // nothing to do, expected case + } + + try { + chai.eval("dosomethingelse(1)"); + return EXIT_FAILURE; // should not get here + } catch (const chaiscript::exception::eval_error &) { + // nothing to do, expected case + } + } +}