Javascript bindings

An example of an emscripten module compiled with a RESTProcess descriptor to create a callable Javascript object, with callable methods is as follows. Note the trick is to create the object as a function, then add the methods to it later. This is kind of the opposite of other programming environment, where an object has a special callable annotation added after creation.

function attachMethods(impl,object, prefix, methods) {
  for (let i=0; i<methods.size(); ++i) {
    let fullMethod=methods.get(i);
    if (fullMethod.length>prefix.length && fullMethod.startsWith(prefix)) {
      let m=fullMethod.slice(prefix.length).replace(/@/g,'$');
      if (m.indexOf('.')<0) {
        object[m]= (...args)=>{
          let arg=JSON5.stringify(args);
          let resultObj=impl.call(fullMethod,arg);
          let result=JSON5.parse(resultObj.json());
          if (typeof result==="object") {
            let r= ()=>{return result;};
            attachMethods(resultObj,r,"",resultObj.list());
            return r;
          }
          return result; 
        };
        attachMethods(impl,object[m],fullMethod+'.',methods);
      }
    }
  }
}

// get the emscripten object
let ximpl = new Module.Foo();
//Define the object as a function object, and convert all arguments to JSON.
let x = ()=>{ximpl.call("",JSON.stringify(arguments));};  
x.impl=ximpl; // stash a reference to the implementation
attachMethods(xmpl,x,"",ximpl.list());

And the C++ code that implements call and list is as follows:

struct CppWrapper
{
  RPPtr wrappedObject=make_shared<classdesc::RESTProcessVoid>();
  CppWrapper()=default;
  template <class T> CppWrapper(const T& x): wrappedObject(x) {}
  /// call method of this on registry
  CppWrapper call(const std::string& method, const string& args) {
    json_pack_t jin;
    read(args,jin);
    return wrappedObject->process("."+method, jin);
  }
  std::vector<string> list() const {
    std::vector<string> methods;
    for (auto& i: wrappedObject->list())
      {
        auto n=i.first.find('.');
        if (n==string::npos) continue;
        methods.emplace_back(i.first.substr(n+1));
      }
    return methods;
  }
};

struct JSFoo: public CppWrapper
{
JSFoo(): CppWrapper(make_shared<RESTProcessValueObject<Foo>>()) {}
};

EMSCRIPTEN_BINDINGS(Ravel) {
  class_<CppWrapper>("CppWrapper")
    .function("call",&CppWrapper::call)
    .function("list",&CppWrapper::list)
    ;

  class_<JSFoo,base<CppWrapper>>("Foo")
    .constructor<>()
    ;

  // for the return value of list()
  register_vector<std::string>("vector<string>");
}

At some point, this will be abstracted into something that can live in the Classdesc codebase.