hagino3000's blog

平成アーカイブス (更新停止)

Node.jsのモジュールをC++で書く (ObjectとArrayを受け取るメソッド)

プリミティブな値を引数で受けとる場合よりもやや面倒。JavaScriptの仕様上どんな値や型でも受けとってしまうので、C++側のチェックコードがどんどん増えていく。

動作確認用のコードがこうだとする

var Test1 = require('./build/Release/Test1');

Test1.giveMeObject({
  option1: 'This is config option!!',
  option2: true
});

Test1.giveMeArray([1,2,3,4,5,6]);

モジュールのコードはこんな感じ

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

using namespace v8;

Handle<Value> HandleObjectMethod(const Arguments& args) {
    HandleScope scope;

    // 第一引数の型がObjectかどうかをチェック
    if (!args[0]->IsObject()) {
        Local<String> msg = String::New("Argument must be Object type");
        ThrowException(Exception::TypeError(msg));
        return scope.Close(Undefined());        
    }

    // v8::Object型にキャスト
    Local<Object> obj = Local<Object>::Cast(args[0]);

    std::string strOption1 = "";
    bool bOption2 = "";
    {
        // Check Option1
        Local<Value> v = obj->Get(String::New("option1"));
        if (v.IsEmpty() || !v->IsString()) {
            Local<String> msg = String::New("Option1 is not specified or invalid type");
            ThrowException(Exception::TypeError(msg));
            return scope.Close(Undefined());                        
        } else {
            int length = v->ToString()->Length();
            char c[length];
            v->ToString()->WriteAscii(c);
            strOption1 += c;
        }
    }
    std::cout << "Option1 = " << strOption1 << std::endl;

    {
        // Check Option2
        Local<Value> v = obj->Get(String::New("option2"));
        if (v.IsEmpty() || !v->IsBoolean()) {
            Local<String> msg = String::New("Option2 is not specified or invalid type");
            ThrowException(Exception::TypeError(msg));
            return scope.Close(Undefined());                        
        } else {
            bOption2 = v->ToBoolean()->Value();
        }
    }
    std::cout << "Option2 = " << bOption2 << std::endl;

    return scope.Close(Boolean::New(true));
}

Handle<Value> HandleArrayMethod(const Arguments& args) {
    HandleScope scope;

    // 第一引数の型がArrayかどうかのチェック
    if (!args[0]->IsArray()) {
        Local<String> msg = String::New("Argument must be Array type");
        ThrowException(Exception::TypeError(msg));
        return scope.Close(Undefined());        
    }

    // v8::Arrayにキャスト
    Local<Array> arr = Local<Array>::Cast(args[0]);
    int length = arr->Length();
    std::cout << "Length:" << length << std::endl;

    for (int i = 0; i < length; i++) {
        // Arrayの各要素にアクセス
        Local<Value> v = arr->Get(i);

        // Something todo
    }

    return scope.Close(v8::Boolean::New(true));
}


void init(Handle<Object> target) {
    // メソッドのexport
    target->Set(String::NewSymbol("giveMeObject"), FunctionTemplate::New(HandleObjectMethod)->GetFunction());
    target->Set(String::NewSymbol("giveMeArray"), FunctionTemplate::New(HandleArrayMethod)->GetFunction());
}

NODE_MODULE(Test1, init)

実行結果

Option1 = This is config option!!
Option2 = 1
Length:6