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 NULL1.
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.
Notes
So, you could say it's a NULL-terminated array of NUL-terminated character arrays. Notice that here NULL = null pointer; NUL = string terminator, i.e. '\0'.
Related
I am trying to hardcode arguments that are passed to the OpenCV Command Line Parser. In the object_detection file, I have added the code as such, so that I do not have to enter the file name via command-line:
Line 40-48:
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame);
void callback(int pos, void* userdata);
int main(int argc, char** argv)
{
// This line added below:
argv[1]="-input /home/Videos/test.mp4";
CommandLineParser parser(argc, argv, keys);
parser.about("Use this script to run object detection deep learning networks using OpenCV.");
if (argc == 1 || parser.has("help"))
OpenCV skips using this file, and switches to camera instead. How can I hardcode the arguments, before they are passed to the Command Line Parser ?
and what is argc? if it's 0, it'll be ignored, and pointer in argv will be NULL, so you get UB. Also, modifying system storage supplied to main is a bad idea, it's likely an UB because you don't have proper storage available to store string.
The only proper way about this is create intermediate variables. Note that array should be NULL-terminated.
The problem is that argc,argv are an old-style C mechanism. You can't simply insert a value in the C array char**. Instead, you must create a new array with one extra element. (Your existing code overwrites argv[1])
Now there's more unusual stuff. argv is an array with argc+1 elements, the last one of which is nullptr. Since you're inserting a new element, you must create an char*[argc+2]*, copy all argc elements, append the pointer to your string, and then terminate the new array with nullptr.
You can now pass the new array and argc+1 to CommandLineParser.
The goal of argc and is to pass argument t"-input /home/Videos/test.mp4");
argv[2] = NULL;hrough the main, if you are using a GNU / UNIX distribution, just pass argument like it:
argc = 2
argv[1] = strdup("-input /home/Videos/test.mp4");
argv[2] = NULL;
CommandLineParser parser(argc, argv, keys);
parser.about("Use this script to run object detection deep learning networks using OpenCV.");
if (argc == 1 || parser.has("help"))
or just use "normal" way and pass argument through our binary, like
./a.out "-input /home/Videos/test.mp4"
Under the context of system programming in UNIX environment, while using the programming language C++, in my understanding, execl() will pass in the path of the program it will run, and a vector. When that vector is being passed in, I understand it as being passed into the entry point, which is usually the main function. In a main function, I understand that my parameters can be written as:
int main(int argc, int* argv[]){ return 0; }
With the above context in mind, when arguments are being passed into the execl(), it seems to me that it doesn't get directly passed into the main function.
Is there a "processing" stage where arguments of execl() are changed to integer data type and an array?
At the same time, if there's any faults in my understanding, please, feel free to correct them.
The signature you give for the main function is incorrect. It should be:
int main(int argc, char *argv[]);
Or:
int main(void);
As for the arguments, the arguments passed to execl should match what the called program receives.
For example, if program A execs program B like this:
execl("/path/to/progB", "progB", "-a", "1", "-x", "hello", "command", (char *)NULL);
Then in program B, argc will be 6 and argv will essentially be:
{ "progB", "-a", "1", "-x", "hello", "command" }
Your understanding is wrong. From this documentation the argument list is always a number of null-terminated char* pointers (emphasis mine):
execl(<shell path>, arg0, file, arg1, ..., (char *)0);
where is an unspecified pathname for the sh utility, file is the >> process image file, and for execvp(), where arg0, arg1, and so on correspond to the values passed to execvp() in argv[0], argv[1], and so on.
The arguments represented by arg0,... are pointers to null-terminated character strings. These strings shall constitute the argument list available to the new process image. The list is terminated by a null pointer. The argument arg0 should point to a filename string that is associated with the process being started by one of the exec functions.
So that doesn't match what you're claiming:
int main(int argc, int* argv[]){ return 0; }
// ^^^^
main takes a char*argv[] argument (not int *argv[]) just like the usual core exec sysccall usually does. On Linux, the system call is execve (requires a char*[])and all other exec* functions are implemented in terms of that.
As for execl, the argument list is required to be NULL-terminated, which allows you to count the arguments and then copy them into an array that gets passed down to execve.
The musl libc library does that rather straightforwardly: https://git.musl-libc.org/cgit/musl/tree/src/process/execl.c
I am trying to convert an exe to dll and manually call the main function from the DLL in my C++ program.
The main function in the code of this exe (generated from another C++ program) looks like this:
int main(int argc, char* argv[])
Now, in my C++ program, earlier I was passing the command line arguments to this exe as follows:
system(somexe test.txt test1.txt test2.txt);
The int argc and argv array are automatically then passed to the exe program. However, I am not sure as to how I would be passing the above parameters if I have to call this function manually. Do I have to manually generate an array everytime with these parameters, and also manually pass the number of elements in this array?
just make a string array and pass to main.int argc mean the string number in the array. argv store the string array.
for example:
int argc = 3;
char *argv[3] = {"argc1","argc2","argc3"};
main(argc,argv);
Do you know the function printf? You can do it the same way.
int doit(int n1, ...)
{
va_list arg_ptr;
int n = n1;
va_start(arg_ptr, n1);
while (n > 0)
{
va_arg(arg_ptr, int);
}
va_end(arg_ptr);
}
I have found that the easiest way to build my program argument list is as a vector of strings. However, execv expects an array of chars for the second argument. What's the easiest way to get it to accept of vector of strings?
execv() accepts only an array of string pointers. There is no way to get it to accept anything else. It is a standard interface, callable from every hosted language, not just C++.
I have tested compiling this:
std::vector<string> vector;
const char *programname = "abc";
const char **argv = new const char* [vector.size()+2]; // extra room for program name and sentinel
argv [0] = programname; // by convention, argv[0] is program name
for (int j = 0; j < vector.size()+1; ++j) // copy args
argv [j+1] = vector[j] .c_str();
argv [vector.size()+1] = NULL; // end of arguments sentinel is NULL
execv (programname, (char **)argv);
The prototype for execv is:
int execv(const char *path, char *const argv[]);
That means the argument list is an array of pointers to null-terminated c strings.
You have vector<string>. Find out the size of that vector and make an array of pointers to char. Then loop through the vector and for each string in the vector set the corresponding element of the array to point to it.
I stumbled over the same problem a while ago.
I ended up building the argument list in a std::basic_string<char const*>. Then I called the c_str() method and did a const_cast<char* const*> on the result to obtain the list in a format that execv accepts.
For composed arguments, I newed strings (ordinary strings made of ordinary chars ;) ), took their c_str() and let them leak.
The const_cast is necessary to remove an additional const as the c_str() method of the given string type returns a char const* const* iirc. Typing this, I think I could have used std::basic_string<char*> but I guess I had a reason...
I am well aware that the const-casting and memory leaking looks a bit rude and is indeed bad practise, but since execv replaces the whole process it won't matter anyway.
Yes, it can be done pretty cleanly by taking advantage of the internal array that vectors use. Best to not use C++ strings in the vector, and const_cast string literals and string.c_str()'s to char*.
This will work, since the standard guarantees its elements are stored contiguously (see https://stackoverflow.com/a/2923290/383983)
#include <unistd.h>
#include <vector>
using std::vector;
int main() {
vector<const char*> command;
// do a push_back for the command, then each of the arguments
command.push_back("echo");
command.push_back("testing");
command.push_back("1");
command.push_back("2");
command.push_back("3");
// push NULL to the end of the vector (execvp expects NULL as last element)
command.push_back(NULL);
// pass the vector's internal array to execvp
execvp(command[0], const_cast<char* const*>(command.data()));
return 1;
}
Code adapted from: How to pass a vector to execvp
Do a const_cast to avoid the "deprecated conversion from string constant to 'char*'". String literals are implemented as const char* in C++. const_cast is the safest form of cast here, as it only removes the const and does not do any other funny business. execvp() will not edit the values anyway.
If you want to avoid all casts, you have to complicate this code by copying all the values to char* types not really worth it.
Although if the number of arguments you want to pass to execv/execl is known, it's easier to write this in C.
You can't change the how execv works (not easily anyway), but you could overload the function name with one that works the way you want it to:
int execv(const string& path, const vector<string>& argv) {
vector<const char*> av;
for (const string& a : argv) {
av.push_back(a.c_str());
av.push_back(0);
return execv(path.c_str(), &av[0]);
}
Of course, this may cause some confusion. You would be better off giving it a name other than execv().
NB: I just typed this in off the top of my head. It may not work. It may not even compile ;-)
I would like to prepare an old-school argument vector (argv) to use within the function
int execve(const char *filename, char
*const argv[],char *const envp[]);
I tried it with the stl::vector class:
std::string arguments = std::string("arg1");
std::vector<char*> argv;
char argument[128];
strcpy(argument, arguments.c_str());
argv.push_back(argument);
argv.push_back('\0'); // finish argv with zero
Finally I pass the vector to execve()
execve("bashscriptXY", &argv[0], NULL)
The code compiles but ArgV gets "ignored" by execve(). So it seems to be wrong, what I'm trying. How should I build an argV in a efficient way with c++?
I think the char[128] is redundant as the string local will have the same lifetime, also, try adding the program as argv[0] like rossoft said in his answer:
const std::string arguments("arg1");
std::vector<const char*> argv;
argv.push_back("bashscriptXY");
// The string will live as long as a locally allocated char*
argv.push_back(arguments.c_str());
argv.push_back(NULL); // finish argv with zero
execve(argv[0], &argv[0], NULL);
Beware that argv[0] is always the name of the command itself. So if you want to pass 1 parameter to a program, you have to fill two elements:
argv[0] should be "bashscriptXY" (or whatever you want...)
argv[1] = "your_argument"
A few issues:
the first element of the argv array is supposed to be the program name
the argv vector can take char const* types as execve() doesn't modify the args (though the process that gets invoked might - but those will be copies in its own address space). So you can push c_str() strings onto it. (See comments)
Try this out:
// unfortunately, execve() wants an array of `char*`, not
// am array of `char const*`.
// So we we need to make sure the string data is
// actually null terminated
inline
std::string& null_terminate( std::string& s)
{
s.append( 1, 0);
return s;
}
// ...
std::string program = std::string( "bashscriptXY");
std::string arg1("arg1");
std::string arg2("arg2");
null_terminate(program);
null_terminate(arg1);
null_terminate(arg2);
std::vector<char *> argv;
argv.push_back( &program[0]);
argv.push_back( &arg1[0]);
argv.push_back( &arg2[0]);
argv.push_back( NULL); // finish argv with zero
intptr_t result = execve( program.c_str(), &argv[0], NULL);
Unless the arguments are hard-coded, you'll have to do some dynamic memory allocation. First, create an array of character pointers for the number of arguments, then allocate memory for each argument and copy the data in.
This question is very similar and includes some code solutions.