2012-12-22 7 views
5

Die v8-Dokumentation von Google beschreibt, wie Sie einem JavaScript-Kontext eine globale Funktion hinzufügen. Wir können eine printf-ähnliche Funktion implementieren ganz problemlos die neue Lambda-Funktion von C++ 11 unter Verwendung von:v8 :: FunctionTemplate, das auf eine nicht globale Variable verweist

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

Das funktioniert gut für jede globale JavaScript-Funktion, die entweder staatenlos oder Referenzen ein globaler C++ Variable (dh std::cout) . Was aber, wenn wir möchten, dass unsere globale JavaScript-Funktion auf eine nicht-globale C++ - Variable verweist? Angenommen, wir erstellen mehrere verschiedene JavaScript-Kontexte mit jeweils einer eigenen globalen print-Funktion, die ein anderes C++ std::ostream verwendet? Wenn v8 Funktionsschablonen std::function Objekte anstelle von Funktionszeiger verwendet, würde das wir so etwas tun:

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

Leider v8 nicht, dies zu unterstützen scheint. Ich nehme (hoffe?) An, dass v8 einen Weg hat, etwas funktionell Äquivalentes zu tun, aber ich finde mich durch den Doxygen für v8::FunctionTemplate mystifiziert. Würde jemand, der etwas Ähnliches versucht hat, bereit sein, den Prozess in etwas Verständlicheres zu destillieren? Ich möchte auch lernen, wie eine globale Instanz eines JavaScript-Objekts erstellt wird, das an eine vorhandene, nicht globale Instanz eines C++ - Objekts gebunden ist.

Antwort

6

Als Antwort auf meine eigene Frage ... ist der Schlüssel zu erkennen, dass v8 :: Argumente nicht einfach ein Array von Argumenten ist. Es enthält auch die überaus nützlichen Methoden Callee() und Data(). Wenn die Funktion eine Methode eines JavaScript-Objekts ist, dann kann, glaube ich, Callee() verwendet werden, um zu erhalten, welche Instanz des Objekts die Methode aufgerufen hat. Nützliche Zustandsinformationen könnten dann in der Objektinstanz gespeichert werden. Sie können auch ein Datenhandle angeben, das auf ein beliebiges C++ - Objekt über void* verweisen kann, wenn Sie einem Objekt eine Funktionsvorlage hinzufügen. Auf dieses funktionsspezifische Datenhandle kann dann über die Methode Data() zugegriffen werden.

Im Folgenden finden Sie ein ziemlich vollständiges Beispiel dessen, was ich in der Frage mit v8::Arguments::Data() versucht habe. Hoffentlich wird dies für jeden nützlich sein, der etwas Ähnliches machen möchte. Wenn Sie eine alternative Strategie haben, die Sie mögen (und ich bin mir sicher, dass es mehr als eine Möglichkeit gibt, dies zu tun), zögern Sie nicht, es in einer anderen Antwort hinzuzufügen!

#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; 
} 
Verwandte Themen