How to pass command line arguments as a string to an embedded Python script made from C ++?

I have a C ++ program that provides a Python interface for executing embedded Python scripts for users.

The user inserts the path to run the Python script and command line arguments. Then the script is executed through

boost::python::exec_file(filename, main_globals, main_globals) 

To pass Python script command line arguments, we need to set them via the Python C-API function

 PySys_SetArgv(int args, char** argv) 

before calling exec_file() .

But for this, you need to tokenize the user line containing the command line arguments to get a list of arguments, and then pass them back to the Python interpreter via PySys_SetArgv . And this is more than just a waste of time, because in this way the main C ++ program should take responsibility for tokenizing the command line, without knowing the logic behind, which is determined only by the user script.

The metacode will have a much more pleasant and understandable approach:

 string command_line_args = '-v -p "filename" -t="anotherfile" --list="["a", "b"]" --myFunnyOpt' exec_file( filename, command_line_args, ...) 

I spent hours looking at the Boost documentation and the Python C-API, but I did not find anything useful. Do you know if there is a way to achieve this, i.e. Pass a whole line of command line arguments to an inline Python script from C ++?


Update:

As Steve suggested in the comments below, I solved my problem by denoting the input line by following https://stackoverflow.com/a/146996/...

In my case, I used:

 // defining the separators std::string escape_char = "\\"; // the escape character std::string sep_char = " "; // empty space as separator std::string quote_char = ""; // empty string --> we don't want a quote char' boost::escaped_list_separator<char> sep( escape_char, sep_char, quote_char ); 

because I wanted to be able to parse tuples containing strings, for example:

 '--option-two=("A", "B")' 

and if you use:

 escaped_list_separator<char> sep('\\', ' ', '"'); 

as in the original message, you are not getting correctly marked lines.

+4
source share
1 answer

Since you are not negative when executing an external file, you can use a helper program to force the shell command to parse for you. Your helper program may be:

 #include <stdio.h> int main (int argc, char *argv[]) { for (int i = 1; i < argc; ++i) printf("%s\n", argv[i]); return 0; } 

And then you can have code that sends your only line of arguments to a helper program (possibly using popen ) and reads the parsed arguments, each argument on a separate line.

 unparsed_line.insert(0, "./parser_helper "); FILE *helper = popen(unparsed_line.c_str(), "r"); std::vector<std::string> args; std::vector<const char *> argv; std::string arg; while (fgetstring(arg, helper)) { args.push_back(arg); argv.push_back(args.rbegin()->c_str()); } pclose(helper); 

The fgetstring subroutine is what I wrote, it is like the intersection between fgets and std::getline . It reads from FILE * one line at a time, filling out the std:string argument.

 static bool fgetstring (std::string &s, FILE *in) { bool ok = false; std::string r; char buf[512]; while (fgets(buf, sizeof(buf), in) != 0) { ++ok; r += buf; if (*r.rbegin() == '\n') { r.resize(r.size()-1); break; } } if (ok) s = r; return ok; } 

It seems I remember a post on SO that had a similar procedure, but I could not find it. I will update the message if I find it later.

+1
source

Source: https://habr.com/ru/post/1414181/


All Articles