Pergunta

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.

Foi útil?

Solução

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.

Outras dicas

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.

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();
}

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.)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top