#include class BaseClass { public: BaseClass() = default; BaseClass(const BaseClass &) = default; 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; 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(R"(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 }