2012-12-22 63 views
5

Google的v8文檔介紹瞭如何將全局函數添加到JavaScript上下文中。我們可以實現一個類似printf函數很容易使用新的拉姆達功能從C++ 11:v8 :: FunctionTemplate引用非全局變量

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

這非常適用於全球任何JavaScript函數,或者是無狀態或引用全局C++變量(即std::cout) 。但是如果我們希望我們的全局JavaScript函數引用非全局C++變量呢?例如,假設我們創建了幾個不同的JavaScript上下文,每個上下文都有其自己的全局函數print,該函數使用不同的C++ std::ostream?如果V8函數模板中使用std::function對象而不是函數指針,則我們會做這樣的事:

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

不幸的是,V8似乎並不支持這一點。我認爲(希望?)v8有辦法做一些功能相當的事情,但是我發現自己被Doxygen迷住了v8::FunctionTemplate。任何嘗試過類似事情的人都願意將流程提煉成更容易理解的東西嗎?我還想了解如何創建綁定到現有的非全局C++對象實例的JavaScript對象的全局實例。

回答

6

在回答我自己的問題時......關鍵是要認識到v8 :: Arguments不僅僅是一個參數數組。它還包含極其有用的方法Callee()Data()。如果該函數是一個JavaScript對象的方法,那麼我認爲,可以使用Callee()來獲取該方法被調用的對象的任何實例。有用的狀態信息可以存儲在對象實例中。在向對象添加函數模板時,您還可以提供一個數據句柄,該句柄可指向任何通過void*的C++對象。然後可以通過Data()方法來訪問該特定於功能的數據句柄。

下面是我在使用v8::Arguments::Data()時試圖做的問題的一個比較完整的例子。希望這對任何想做類似事情的人都有用。如果您有其他策略可以選擇(並且我確信有多種方法可以做到這一點),請隨時將其添加到其他答案中!

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