2012-12-22 10 views
5

Dokumentacja Google v8 opisuje sposób dodania globalnej funkcji do kontekstu JavaScript. Możemy zaimplementować funkcję printf jak łatwo za pomocą nowej funkcji lambda od C++ 11:v8 :: FunctionTemplate odwołujące się do innej niż globalna zmienna

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

Działa to dobrze dla każdej funkcji JavaScript globalnego, który jest albo bezpaństwowca lub referencje globalnym C++ zmienna (tj std::cout) . Ale co, jeśli chcemy, aby nasza globalna funkcja JavaScript odwoływała się do nieglobalnej zmiennej C++? Załóżmy na przykład, że tworzymy kilka różnych kontekstów JavaScript, każdy z własną globalną funkcją print, która używa innego C++ std::ostream? Jeśli szablony funkcyjne V8 stosowany std::function obiektów zamiast wskaźników funkcji, to czy możemy zrobić coś takiego:

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

Niestety v8 nie wydają się wspierać. Zakładam (mam nadzieję?), Że v8 ma sposób na robienie czegoś funkcjonalnie równoważnego, ale jestem doszukiwany przez Doxygen dla v8::FunctionTemplate. Czy ktokolwiek, kto próbowałby czegoś podobnego, chciał poddać proces destylacji w coś bardziej zrozumiałego? Chciałbym również dowiedzieć się, jak utworzyć globalną instancję obiektu JavaScript, który jest powiązany z istniejącą, nieglobalną instancją obiektu C++.

Odpowiedz

6

W odpowiedzi na moje własne pytanie ... kluczem jest uświadomienie sobie, że v8 :: Argumenty to nie tylko szereg argumentów. Zawiera również niezwykle przydatne metody: Callee() i Data(). Jeśli funkcja jest metodą obiektu JavaScript, to można, jak sądzę, użyć Callee(), aby uzyskać odbicie dowolnej instancji tego obiektu, do której została wywołana metoda. Przydatne informacje o stanie można następnie przechowywać w instancji obiektu. Można także podać uchwyt danych, który może wskazywać dowolny obiekt w C++ za pośrednictwem void*, podczas dodawania szablonu funkcji do obiektu. Ten uchwyt danych specyficzny dla funkcji może być następnie dostępny za pomocą metody Data().

Poniżej znajduje się dość pełny przykład tego, co próbowałem zrobić w pytaniu przy użyciu v8::Arguments::Data(). Mam nadzieję, że przyda się to każdemu, kto chce zrobić coś podobnego. Jeśli masz alternatywną strategię, którą lubisz (i jestem pewien, że jest to więcej niż jeden sposób), możesz dodać ją w innej odpowiedzi!

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