Basically I am working on integrating an existing C++ file with our javascript / node using their NAPI, functionality. I have it working on a test C++ file so I know I have the setup working. However on the actual C++ file it is designed to run from the command line with argc and argv from the command line. Basically I just need to invoke the main method in C++ from inside of my other function, which means there is no command line. So I have to pass in values for argc and argv. argc is just an int, that is easy enough, but argc is a char ** type, which from my research looks like it is an array of character arrays aka strings?
This is my current code at the bottom of my c++ file
void Init(Env env, Object exports, Object module) {
exports.Set("main", Function::New(env, main(2,{"test","test2"})));
}
NODE_API_MODULE(addon, Init)
The argc value is working fine
I am trying to create a temporary / test value for argv to pass in but I am having an issue figuring out how to make an array of type char ** with my values.
argv
is an array of pointers to strings (actually, NUL-terminated character arrays), where element 0 is the name of the program, elements 1 ... argc-1
are the program arguments, and element argc
must be NULL
1.
There's no array literal in C++, so you have to create explicitly an array variable to pass it to a function. Even worse, main
is allowed to modify the passed arguments, so you cannot even build an array of string literals, as they are read only; thus, you have to explicitly allocate read/write space for each argument. A barebones solution can be to have single buffers for each argument and build the pointer array out of them:
char argv0[] = "test_program";
char argv1[] = "arg1";
char argv2[] = "arg2";
char *argv[] = {argv0, argv1, argv2, NULL};
main(3, argv);
A more flexible one (especially if you have to build your arguments dynamically) can be to use an std::vector<std::string>
:
std::vector<std::string> args = { "test_program", "arg1", "arg2" };
// ... here you may add other arguments dynamically...
args.push_back("arg3"); // whatever
// build the pointers array
std::vector<char *> argv;
for(std::string &s: args) argv.push_back(&s[0]);
argv.push_back(NULL);
main(argv.size()-1, argv.data());
Now, coming to your code:
void Init(Env env, Object exports, Object module) {
exports.Set("main", Function::New(env, main(2,{"test","test2"})));
}
NODE_API_MODULE(addon, Init)
besides the fact that you cannot build argv
to pass to main
like that, you are trying to invoke main
and pass its result as second argument to Function::New
, while Function::New
wants some callable type (e.g. a function pointer) to register as handler for the export named main
! Copying from the Function::New
documentation:
/// Callable must implement operator() accepting a const CallbackInfo& /// and return either void or Value.
So, as a simple example, you could export your main as a parameterless JS function that returns nothing (undefined
, I guess?) by registering a callback like this:
void MainCallback(const CallbackInfo& info) {
char argv0[] = "test_program";
char argv1[] = "arg1";
char argv2[] = "arg2";
char *argv[] = {argv0, argv1, argv2, NULL};
main(3, argv);
}
void Init(Env env, Object exports, Object module) {
exports.Set("main", Function::New(env, MainCallback));
}
NODE_API_MODULE(addon, Init)
Finally, as others said, technically in C++ main
is somewhat magic - it's undefined behavior to invoke it from inside the program; in practice, on any platform that I know of that can also run node.js, main
is a perfectly regular function that happens to be invoked by the C runtime at startup, so I don't think that this will cause you any problem.
NULL
= null pointer; NUL = string terminator, i.e. '\0'
.