2012-12-22 18 views
5

La documentazione di v8 di Google descrive come aggiungere una funzione globale a un contesto JavaScript. Siamo in grado di implementare una funzione printf simile abbastanza facilmente usando la nuova funzione di lambda da C++ 11:v8 :: FunctionTemplate che fa riferimento a una variabile non globale

Handle<ObjectTemplate> global = ObjectTemplate::New(); 
global->Set(String::New("print"), FunctionTemplate::New(
[](const v8::Arguments &args) -> v8::Handle<v8::Value> 
{ 
    v8::String::AsciiValue ascii(args[0]); 
    std::cout << *ascii << "\n"; 
})); 
Persistent<Context> context = Context::New(NULL, global); 

Questo funziona bene per qualsiasi funzione JavaScript globale che è o senza stato o riferimenti a C globale ++ variabile (cioè std::cout) . Ma cosa succede se vogliamo che la nostra funzione JavaScript globale faccia riferimento a una variabile C++ non globale? Ad esempio, supponiamo di creare diversi diversi contesti JavaScript ciascuno con la propria funzione globale print che utilizza un diverso C++ std::ostream? Se i modelli di funzione v8 usati std::function oggetti invece di puntatori a funzione, l'avremmo fatto qualcosa di simile:

Persistent<Context> create_context(std::ostream &out) 
{ 
    Handle<ObjectTemplate> global = ObjectTemplate::New(); 
    global->Set(String::New("print"), FunctionTemplate::New(
    [&out](const v8::Arguments &args) -> v8::Handle<v8::Value> 
    { 
    v8::String::AsciiValue ascii(args[0]); 
    out << *ascii << "\n"; 
    })); 
    return Context::New(NULL, global); 
} 

Purtroppo, v8 non sembra a sostegno di questa. Presumo (spero?) Che v8 abbia un modo di fare qualcosa di funzionalmente equivalente, ma mi trovo sconcertato dal Doxygen per v8::FunctionTemplate. Chiunque avesse tentato qualcosa di simile sarebbe disposto a distillare il processo in qualcosa di più comprensibile? Vorrei anche imparare come creare un'istanza globale di un oggetto JavaScript che è associato a un'istanza esistente, non globale di un oggetto C++.

risposta

6

In risposta alla mia domanda ... la chiave è rendersi conto che v8 :: Arguments non è semplicemente una serie di argomenti. Contiene anche i metodi estremamente utili Callee() e Data(). Se la funzione è un metodo di un oggetto JavaScript, è possibile utilizzare Callee() per ottenere l'istanza di qualsiasi istanza dell'oggetto a cui è stato chiamato il metodo. Informazioni utili sullo stato potrebbero quindi essere archiviate nell'istanza dell'oggetto. È inoltre possibile fornire un handle di dati, che può puntare a qualsiasi oggetto C++ tramite void*, quando si aggiunge un modello di funzione a un oggetto. È possibile accedere a questo handle di dati specifico della funzione tramite il metodo Data().

Di seguito è riportato un esempio ragionevolmente completo di ciò che stavo cercando di fare nella domanda utilizzando v8::Arguments::Data(). Speriamo che questo sia utile a chiunque voglia fare qualcosa di simile. Se hai una strategia alternativa che ti piace (e sono certo che c'è più di un modo per farlo), sentiti libero di aggiungerla in un'altra risposta!

#include <iostream> 
#include <ostream> 
#include <v8.h> 

// add print() function to an object template 
void add_print(v8::Handle<v8::ObjectTemplate>& ot, std::ostream* out) 
{ 
    // add function template to ot 
    ot->Set(v8::String::New("print"), v8::FunctionTemplate::New(
    // parameter 1 is the function callback (implemented here as a lambda) 
    [](const v8::Arguments& args)->v8::Handle<v8::Value> 
    { 
     // recover our pointer to an std::ostream from the 
     // function template's data handle 
     v8::Handle<v8::External> data = v8::Handle<v8::External>::Cast(args.Data()); 
     std::ostream* out = static_cast<std::ostream*>(data->Value()); 

     // verify that we have the correct number of function arguments 
     if (args.Length() != 1) 
     return v8::ThrowException(v8::String::New("Too many arguments to print().")); 

     // print the ascii representation of the argument to the output stream 
     v8::String::AsciiValue ascii(args[0]); 
     *out << *ascii << "\n"; 

     // like 'return void;' only in JavaScript 
     return v8::Undefined(); 
    }, 

    // parameter 2 is the data handle with the pointer to an std::ostream 
    v8::External::New(out) 
)); 
} 

int main() 
{ 
    // create a stack-allocated handle scope 
    v8::HandleScope handle_scope; 

    // create a global template 
    v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); 

    // add a print() function using std::cout to the global template 
    add_print(global, &std::cout); 

    // create a context 
    v8::Persistent<v8::Context> context = v8::Context::New(nullptr, global); 

    // enter the created context 
    v8::Context::Scope context_scope(context); 

    // create a string containing the JavaScript source code 
    v8::Local<v8::String> source = v8::String::New("print('1 + 1 = ' + (1 + 1));"); 

    // compile the source code 
    v8::Local<v8::Script> script = v8::Script::Compile(source); 

    // run the script 
    script->Run(); 

    // dispose of the persistent context 
    context.Dispose(); 

    return 0; 
} 
Problemi correlati