问题
Following this post, where I have found a temporary workaround for my other problem, I want to know if I can replace the int argc, char** argv
with a std::vector<std::string>
variable/object.
Consider the imaginary code:
#include <iostream>
#include <CloseLibrary>
void someFunction(int argc, char** argv){
for (int i = 0; i < argc; ++i) {
std::cout << argv[i] << std::endl;
}
}
int myFunc(int argc, char** argv){
someFunction(argc, argv);
return 0;
}
where the CloseLibrary
is a closed library that I don't have access to the source code, and the someFunction
function from that library demands the int argc, char** argv
command line arguments. But for some reason I can't have double pointers char**
in my code.
Here in this post something like what I need is proposed, but I don't know how to use that. Can I write the code this way:
#include <iostream>
#include <CloseLibrary>
#include <vector>
void someFunction(int argc, char** argv){
for (int i = 0; i < argc; ++i) {
std::cout << argv[i] << std::endl;
}
}
int myFunc("args", [](std::vector<std::string> args){
std::vector<char *> cstrs;
cstrs.reserve(args.size());
for (auto &s : args) cstrs.push_back(const_cast<char *>(s.c_str()));
someFunction(cstrs.size(), cstrs.data());
return 0;
}
Or maybe there is a more canonical way to do this? I would appreciate it if you could help me find the correct way to do this and understand the solution. Thanks for your help in advance.
P.S.1. The char* argv[]
method is ok in the body of the function but not ok in the inputs. I don't know why pybind11 does this!
P.S.2. Here on pybind11 gitter, this was suggested:
void run(const std::vector<std::string>& args) {
for(auto&& e : args) std::cout << e << '\n';
}
P.S.3. Also suggested on pybind11 Gitter:
char** argv = new char*[vec.size()]; // just like malloc(sizeof(char*)*vec.size());
for (int i = 0; i < vec.size(), i++) {
argv[i] = new char[vec[i].size()];
memcpy(argv[i], vec[i].data(), vec[i].size()); // or strcpy
}
回答1:
You could use the constructor which initializes the vector from a given range, with the argv
parameter acts as the starting iterator and argv+argc
acting as the ending iterator.
For example, I usually start my main function with:
int main( int argc, char* argv[] )
{
std::vector< std::string > args( argv, argv + argc );
for ( auto s : args )
{
std::cout << s << std::endl;
}
}
Note that this will also capture the first argument (argv[0]) which usually (but not necessarily) hold the name of the application when it is started.
In your case, you would like to do the reverse, build up a contiguous array of char*
from a std::vector< std::string >. I would do something like:
std::vector< char* > rargs( args.size(), 0 ); // Initialize N nullptrs.
for ( int i=0; i<args.size(); ++i )
{
std::strcpy( rargs[i], args[i].c_str() ); // One-by-one strcpy them
}
And then you can pass them into a function accepting an argc, argv as
someFunction( rargs.size(), rargs.data() );
回答2:
For what it's worth ... Taking it completely back to your original problem of not being able to use char**
with pybind11, a full working example, scavenged from the pieces you posted is below. Yes, it's not pretty, but working with pointers never is.
#include <pybind11/pybind11.h>
#include <iostream>
#if PY_VERSION_HEX < 0x03000000
#define MyPyText_AsString PyString_AsString
#else
#define MyPyText_AsString PyUnicode_AsUTF8
#endif
namespace py = pybind11;
void closed_func(int argc, char** argv){
for (int i = 0; i < argc; ++i) {
std::cout << "FROM C++: " << argv[i] << std::endl;
}
}
void closed_func_wrap(py::object pyargv11) {
int argc = 0;
std::unique_ptr<char*[]> argv;
// convert input list to C/C++ argc/argv
PyObject* pyargv = pyargv11.ptr();
if (PySequence_Check(pyargv)) {
Py_ssize_t sz = PySequence_Size(pyargv);
argc = (int)sz;
argv = std::unique_ptr<char*[]>{new char*[sz]};
for (Py_ssize_t i = 0; i < sz; ++i) {
PyObject* item = PySequence_GetItem(pyargv, i);
argv[i] = (char*)MyPyText_AsString(item);
Py_DECREF(item);
if (!argv[i] || PyErr_Occurred()) {
argv = nullptr;
break;
}
}
}
// bail if failed to convert
if (!argv) {
std::cerr << "argument is not a sequence of strings" << std::endl;
return;
}
// call the closed function with the proper types
closed_func(argc, argv.get());
}
PYBIND11_MODULE(HelloEposCmd, m)
{
m.def("run", &closed_func_wrap, "runs the HelloEposCmd");
}
Which after compiling can be used as expected:
$ python - a b c d=13
>>> import HelloEposCmd
>>> import sys
>>> HelloEposCmd.run(sys.argv)
FROM C++: -
FROM C++: a
FROM C++: b
FROM C++: c
FROM C++: d=13
>>>
来源:https://stackoverflow.com/questions/60040665/replacing-the-command-line-arguments-int-argc-and-char-argv-with-stdvectors