关于Nodejs addons的讨论

关于Nodejs addons的讨论

在编写扩展的C++类时候,发现要导出的函数必须是静态的,不然FunctionTemplate::New(Connect)->GetFunction()会报错。静态就静态,接着说,在静态方法(Connect)里面使用我定义的另外一个类。例如我定义成员变量SqlConn m_conn;这时候在Connect静态函数调用m_conn.connect()这当然不行,应该静态函数不需要调用非静态的函数。我这时候把m_conn定义为静态的。这时候就有问题了。每当我new一个新的对象,m_conn连接都会被重新赋值。也就是只能存在一个连接。请问大家有什么好的办法吗?


4 回复

关于Node.js Addons的讨论

在Node.js中,使用C++编写Node.js Addons是一种常见的需求,特别是在需要高性能计算或直接与底层系统交互时。本文将讨论如何在编写Node.js Addons时处理静态方法和非静态成员变量的问题。

问题背景

假设我们有一个类 SqlConn,它负责数据库连接,并且我们希望在Node.js Addon中使用这个类。具体来说,我们希望在静态方法中调用非静态成员变量,但遇到了一些限制。

示例代码

首先,让我们看一个简单的示例代码,展示如何在Node.js Addon中定义一个类和静态方法:

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

class SqlConn {
public:
    void connect(const v8::FunctionCallbackInfo<v8::Value>& args) {
        // 这里是连接数据库的逻辑
        printf("Connecting to database...\n");
    }
};

static void Connect(const v8::FunctionCallbackInfo<v8::Value>& args) {
    // 静态方法中尝试访问非静态成员变量
    SqlConn* conn = new SqlConn();
    conn->connect(args);
}

void Init(v8::Local<v8::Object> exports) {
    NODE_SET_METHOD(exports, "connect", Connect);
}

NODE_MODULE(addon, Init)

在这个例子中,Connect 是一个静态方法,它尝试创建一个新的 SqlConn 对象并调用其 connect 方法。然而,这种方法会导致每次调用 connect 时都创建新的连接对象,这可能不是我们期望的行为。

解决方案

为了实现一个持久的连接,我们可以考虑将 SqlConn 对象作为模块级别的静态变量。这样,每次调用 connect 方法时,都会使用同一个连接对象。

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

class SqlConn {
public:
    void connect(const v8::FunctionCallbackInfo<v8::Value>& args) {
        // 这里是连接数据库的逻辑
        printf("Connecting to database...\n");
    }
};

static SqlConn* globalConn = nullptr;

static void Connect(const v8::FunctionCallbackInfo<v8::Value>& args) {
    if (!globalConn) {
        globalConn = new SqlConn();
    }
    globalConn->connect(args);
}

void Init(v8::Local<v8::Object> exports) {
    NODE_SET_METHOD(exports, "connect", Connect);
}

NODE_MODULE(addon, Init)

在这个改进后的版本中,我们引入了一个全局的 SqlConn 对象 globalConn。在 Connect 静态方法中,我们检查 globalConn 是否已经初始化。如果没有,则创建一个新的 SqlConn 对象并将其赋值给 globalConn。之后的每次调用 connect 方法时,都会使用同一个连接对象。

总结

通过将非静态成员变量提升为模块级别的静态变量,我们可以确保在多次调用静态方法时,使用的是同一个对象实例。这种方法可以避免每次都创建新的连接对象,从而提高性能和资源利用率。

希望这个示例能够帮助你更好地理解和解决在Node.js Addon中使用静态方法和非静态成员变量的问题。如果你有其他问题或需要进一步的帮助,请随时提问!


为什么将m_conn定义为静态来保持对m_conn的引用?new一个新对象然后交给nodejs来管理,通过Unwrap重新获得此对象的引用。

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

SqlConn *conn = new SqlConn(); conn->Wrap(args.This()); return args.This(); }

后续的操作例如

Handle<Value> Query(const Arguments& args) {
    HandleScope scope;
    //...........
    SqlConn* conn = ObjectWrap::Unwrap<SqlConn>(args.This());
    //..........
}

SqlConn继承ObjectWrap,或者新建ObjectWrap的子类作为SqlConn的容器

这种方法不太好,应该类不能通用。

在Node.js Addons中,确实需要将导出给JavaScript的函数声明为静态函数(即类的方法而不是实例方法),因为这些函数是在V8引擎的上下文中被调用的,而V8并不直接支持访问非静态成员。

针对你的问题,你可以通过创建一个类来管理你的数据库连接。这样每个对象实例都有自己的连接实例,而不是共享同一个静态连接。下面是一个简单的例子:

// sqlconnection.h
class SqlConnection {
public:
    void connect();
};

// addon.cpp
#include "sqlconnection.h"

class MyAddon : public node::ObjectWrap {
public:
    static void Init(v8::Local<v8::Object> exports);
    SqlConnection* m_conn;

private:
    explicit MyAddon();
    ~MyAddon();

    static NAN_METHOD(New);
    static NAN_METHOD(Connect);
};

NAN_MODULE_INIT(MyAddon::Init) {
    v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
    tpl->SetClassName(Nan::New("MyAddon").ToLocalChecked());
    tpl->InstanceTemplate()->SetInternalFieldCount(1);

    Nan::SetPrototypeMethod(tpl, "connect", Connect);

    constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked());
    Nan::Set(exports, Nan::New("MyAddon").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked());
}

NAN_METHOD(MyAddon::New) {
    if (info.IsConstructCall()) {
        MyAddon* obj = new MyAddon();
        obj->Wrap(info.This());
        info.GetReturnValue().Set(info.This());
    } else {
        v8::Local<v8::Function> cons = Nan::New(constructor());
        info.GetReturnValue().Set(Nan::NewInstance(cons).ToLocalChecked());
    }
}

NAN_METHOD(MyAddon::Connect) {
    MyAddon* obj = ObjectWrap::Unwrap<MyAddon>(info.Holder());
    obj->m_conn->connect();
}

NAN_METHOD(CreateAddon) {
    info.GetReturnValue().Set(MyAddon::constructor().GetFunction()->NewInstance());
}

NODE_MODULE(addon, MyAddon::Init)

在这个例子中,MyAddon 类中的 m_conn 成员变量不再是静态的,每个 MyAddon 对象实例都有自己独立的 SqlConnection 实例。因此,当你创建多个 MyAddon 对象时,它们各自拥有不同的连接实例,不会互相影响。

这种方法不仅解决了连接共享的问题,还使得代码更加模块化和易于维护。

回到顶部