Tutorial :V8 FunctionTemplate Class Instance



Question:

I have the following class:

class PluginManager  {  public:      Handle<Value> Register(const Arguments& args);      Handle<ObjectTemplate> GetObjectTemplate();  };    

I want the Register method to be accessible from JavaScript. I add it to the global object like this:

PluginManager pluginManagerInstance;    global->Set(String::New("register"), FunctionTemplate::New(pluginManagerInstance.Register));   

It throws the following error:

'PluginManager::Register': function call missing argument list; use '&PluginManager::Register' to create a pointer to member

I tried to do that, but it doesn't work either. And it's not correct, because I want it to call the Register method of the pluginManagerInstance.

Except for making the Register method static or global, any ideas?

Thanks.


Solution:1

You're trying to bind two things at once: the instance and the method to invoke on it, and have it look like a function pointer. That unfortunately doesn't work in C++. You can only bind a pointer to a plain function or a static method. So image you add a static "RegisterCB" method and register it as the callback:

static Handle<Value> RegisterCB(const Arguments& args);  ...FunctionTemplate::New(&PluginManager::RegisterCB)...  

Now where do you get the pluginManagerInstance from? For this purpose, most callback-registration apis in V8 have an additional "data" parameter that will get passed back to the callback. So does FunctionTemplate::New. So you actually want to bind it like this:

...FunctionTemplate::New(&PluginManager::RegisterCB,                           External::Wrap(pluginManagerInstance))...  

The data is then available through args.Data() and you can delegate to the actual method:

return ((PluginManager*)External::Unwrap(args.Data())->Register(args);  

This can surely be made a little easier with some macro.


Solution:2

You will likely need to make it static. Don't forget member functions take a hidden this parameter as the first argument. Because of this, they rarely work well as function pointer prototypes.


Solution:3

For an example take a look at the code in this tutorial. The same method mernst suggests above is used to send a pointer to this object, to the log function.

in header:

    virtual void log(const string &str);      static Handle<Value> logCallback(const Arguments &args);        Local<FunctionTemplate> makeStaticCallableFunc(InvocationCallback func);      Local<External> classPtrToExternal();        ////////////////////////////////////////////////////////////////////////      //      // Converts an External to a V8TutorialBase pointer. This assumes that the      // data inside the v8::External is a "this" pointer that was wrapped by      // makeStaticCallableFunc      //      // \parameter data Shoudld be v8::Arguments::Data()      //      // \return "this" pointer inside v8::Arguments::Data() on success, NULL otherwise      //      ////////////////////////////////////////////////////////////////////////              template <typename T>      static T *externalToClassPtr(Local<Value> data)      {          if(data.IsEmpty())              cout<<"Data empty"<<endl;          else if(!data->IsExternal())              cout<<"Data not external"<<endl;          else              return static_cast<T *>(External::Unwrap(data));            //If function gets here, one of the checks above failed          return NULL;      }  

implementation:

////////////////////////////////////////////////////////////////////////  //  // Wrap a callback function into a FunctionTemplate, providing the "this"  // pointer to the callback when v8 calls the callback func  //  // \parameter func Static callback to be used in FunctionTemplate  //  // \return Local<FunctionTemplate> containing func  //  ////////////////////////////////////////////////////////////////////////  Local<FunctionTemplate> V8TutorialBase::makeStaticCallableFunc(InvocationCallback func)  {      HandleScope scope;      Local<FunctionTemplate> funcTemplate = FunctionTemplate::New(func, classPtrToExternal());      return scope.Close(funcTemplate);  }    ////////////////////////////////////////////////////////////////////////  //  // Makes the "this" pointer be an external so that it can be accessed by  // the static callback functions  //  // \return Local<External> containing the "this" pointer  ////////////////////////////////////////////////////////////////////////  Local<External> V8TutorialBase::classPtrToExternal()  {      HandleScope scope;      return scope.Close(External::New(reinterpret_cast<void *>(this)));  }    Handle<Value> V8TutorialBase::logCallback(const Arguments &args)  {      HandleScope scope;        .....        V8TutorialBase *objPtr = externalToClassPtr<V8TutorialBase>(args.Data());      String::Utf8Value val(Local<String>::Cast(args[0]));      objPtr->log(*val);    // log is a non static member function       // or you can directly do anything that you would do in a member function using the objPtr        return v8::Null();  }  


Solution:4

If you want to call that method, you have to add parentheses:

lobal->Set( String::New("register")            , FunctionTemplate::New(pluginManagerInstance.Register()) );                                                                  ^^  

If you want to take its address, you have to add a &:

lobal->Set( String::New("register")            , FunctionTemplate::New(&PluginManager::Register) );                                    ^  

(Which is exactly what the error message says.)


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »