136 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include <chaiscript/chaiscript.hpp>
 | 
						|
 | 
						|
class BaseClass
 | 
						|
{
 | 
						|
  public:
 | 
						|
    BaseClass()
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    BaseClass(const BaseClass &) = default;
 | 
						|
 | 
						|
    virtual ~BaseClass() {}
 | 
						|
 | 
						|
    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<chaiscript::Boxed_Value> &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<typename Param>
 | 
						|
    void tie(const chaiscript::Boxed_Value &t_func, Param &t_param)
 | 
						|
    {
 | 
						|
      t_param = chaiscript::boxed_cast<Param>(t_func);
 | 
						|
    }
 | 
						|
 | 
						|
    std::function<std::string (const ChaiScriptDerived&, float, double)> m_doSomethingImpl;
 | 
						|
    std::function<bool (ChaiScriptDerived&, const std::string &t_val)> 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 (const std::vector<chaiscript::Boxed_Value> &)>(), "ChaiScriptDerived");
 | 
						|
  chai.add(chaiscript::base_class<BaseClass, ChaiScriptDerived>());
 | 
						|
  chai.add(chaiscript::user_type<BaseClass>(), "BaseClass");
 | 
						|
  chai.add(chaiscript::user_type<ChaiScriptDerived>(), "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<ChaiScriptDerived&>("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<std::string>("myderived.doSomething(2,4.3)");
 | 
						|
  assert(retval == "new8.6");
 | 
						|
 | 
						|
  // The whole process is fully orthogonal
 | 
						|
}
 | 
						|
 | 
						|
 |