diff --git a/CMakeLists.txt b/CMakeLists.txt index cefe9ec..c8ef4b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,6 +203,8 @@ if(BUILD_SAMPLES) target_link_libraries(example ${LIBS}) add_executable(memory_leak_test samples/memory_leak_test.cpp) target_link_libraries(memory_leak_test ${LIBS}) + add_executable(inheritance samples/inheritance.cpp) + target_link_libraries(inheritance ${LIBS}) endif() diff --git a/samples/inheritance.cpp b/samples/inheritance.cpp new file mode 100644 index 0000000..1e2e02b --- /dev/null +++ b/samples/inheritance.cpp @@ -0,0 +1,134 @@ +#include +#include + +class BaseClass +{ + public: + BaseClass() + { + } + + virtual ~BaseClass() = default; + + virtual std::string doSomething(float, double) const = 0; + + + void setValue(const std::string &t_val) { + if (validateValue(t_val)) + { + m_value = t_val; + } + } + + std::string getValue() const { + return m_value; + } + + protected: + virtual bool validateValue(const std::string &t_val) = 0; + + private: + std::string m_value; +}; + +class ChaiScriptDerived : public BaseClass +{ + public: + ChaiScriptDerived(const std::vector &t_funcs) + { + // using the range-checked .at() methods to give us an exception + // instead of a crash if the user passed in too-few params + tie(t_funcs.at(0), m_doSomethingImpl); + tie(t_funcs.at(1), m_validateValueImpl); + } + + std::string doSomething(float f, double d) const override + { + assert(m_doSomethingImpl); + return m_doSomethingImpl(*this, f, d); + } + + protected: + bool validateValue(const std::string &t_val) override + { + assert(m_validateValueImpl); + return m_validateValueImpl(*this, t_val); + } + + private: + template + void tie(const chaiscript::Boxed_Value &t_func, Param &t_param) + { + t_param = chaiscript::boxed_cast(t_func); + } + + std::function m_doSomethingImpl; + std::function m_validateValueImpl; +}; + +int main() +{ + chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); + chai.add(chaiscript::fun(&BaseClass::doSomething), "doSomething"); + chai.add(chaiscript::fun(&BaseClass::setValue), "setValue"); + chai.add(chaiscript::fun(&BaseClass::getValue), "getValue"); + chai.add(chaiscript::constructor &)>(), "ChaiScriptDerived"); + chai.add(chaiscript::base_class()); + chai.add(chaiscript::user_type(), "BaseClass"); + chai.add(chaiscript::user_type(), "ChaiScriptDerived"); + + std::string script = R""( + def MakeDerived() { + return ChaiScriptDerived( + // create a dynamically created array and pass it in to the constructor + [ + fun(this, f, d) { + // see here that we are calling back into the 'this' pointer + return "${this.getValue()}${f * d}"; + }, + + fun(this, new_val) { + if (new_val.size() < 5) { + true; + } else { + print("String ${new_val} is too long"); + false; + } + } + ] + ); + } + + var myderived := MakeDerived(); // avoid a copy by using reference assignment := + + )""; + + chai.eval(script); + + BaseClass &myderived = chai.eval("myderived"); + + // at this point in the code myderived is both a ChaiScript variable and a C++ variable. In both cases + // it is a derivation of BaseClass, and the implementation is provided via ChaiScript functors + // assigned in the MakeDerived() factory function + // + // Notice that our validateValue() function has a requirement that the new string be < 5 characters long + + myderived.setValue("1234"); + assert(myderived.getValue() == "1234"); + + // chaiscript defined function will print out an error message and refuse to allow the setting + myderived.setValue("12345"); + assert(myderived.getValue() == "1234"); + + + chai.eval("myderived.setValue(\"new\")"); // set the value via chaiscript + assert(myderived.getValue() == "new"); + + // call the other derived method via chaiscript and return the value to c++ land: + std::string retval = chai.eval("myderived.doSomething(2,4.3)"); + assert(retval == "new8.6"); + + // The whole process is fully orthogonal +} + +