diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index d3c751ee0..e44ea51ab 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -681,6 +681,13 @@ namespace chaiscript } virtual void assign(const std::shared_ptr &t_rhs) = 0; + + // Check whether the function is callable (not empty) + // Note: this conversion *should* be marked "explicit", but virtual together + // with explicit isn't allowed with MSVC (bug) + virtual operator bool() const = 0; + + virtual void clear() = 0; }; template @@ -713,6 +720,16 @@ namespace chaiscript m_f.get() = dispatch::functor(t_rhs, nullptr); } + operator bool() const override + { + return static_cast(m_f.get()); + } + + void clear() override + { + m_f.get() = nullptr; + } + protected: Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 76c31bc83..df6b89124 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -93,6 +93,50 @@ TEST_CASE("Function objects can be created from chaiscript functions") CHECK(chai.eval >("to_string")(chaiscript::var(6)) == "6"); } +TEST_CASE("Function callbacks can be set, tested and cleared") +{ + struct MyObject { + std::function callback; + }; + + using namespace chaiscript; + ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module()); + ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + utility::add_class(*m, + "MyObject", + { constructor(), + constructor() }, + { {fun(&MyObject::callback), "callback"}, + {fun(static_cast(&MyObject::operator=)), "=" } + } + ); + m->add(fun([](dispatch::Assignable_Proxy_Function const& func) { return !func; }), "empty"); + m->add(fun([](dispatch::Assignable_Proxy_Function& func) { func.clear(); }), "clear"); + chai.add(m); + + // Function object set from C++ + MyObject test; + test.callback = [](int i) { return i + 10; }; + chai.add(var(std::ref(test)), "test"); + CHECK(chai.eval("!test.callback.empty ? test.callback(13) : -1") == 23); + CHECK(chai.eval("test.callback(9)") == 19); + CHECK(chai.eval("test.callback.empty") == false); + chai.eval("test.callback.clear"); + CHECK(chai.eval("test.callback.empty") == true); + CHECK_THROWS(chai.eval("test.callback(1)") == 11); + + // Function object set from ChaiScript + chai.eval("auto o = MyObject()"); + CHECK(chai.eval("o.callback.empty") == true); + chai.eval("o.callback = fun(int i) { return i + 10 }"); + CHECK(chai.eval("o.callback.empty") == false); + CHECK(chai.eval("!o.callback.empty ? o.callback(13) : -1") == 23); + CHECK(chai.eval("o.callback(9)") == 19); + chai.eval("o.callback.clear"); + CHECK(chai.eval("o.callback.empty") == true); + CHECK_THROWS(chai.eval("o.callback(1)") == 11); + +} TEST_CASE("ChaiScript can be created and destroyed on heap") {