用std::vector<std::string>替换命令行参数int argc和char** argv

3

这篇帖子中,我找到了一个临时解决方案来解决我的另一个问题,现在我想知道是否可以用std::vector<std::string>变量/对象替换int argc, char** argv

考虑以下虚构代码:

#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;
}

在这里,CloseLibrary 是一个我无法访问源代码的封闭库,而该库中的 someFunction 函数需要 int argc,char ** argv 命令行参数。但由于某些原因,我不能在我的代码中使用双指针 char**

出于某种原因,我需要类似于这篇文章中提出的东西,但我不知道如何使用它。我可以这样编写代码吗:

#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;
}

也许有更加规范的方法来完成这个任务?如果您能帮我找到正确的方法并理解解决方案,我将不胜感激。非常感谢您的帮助。

P.S.1. 在函数体中使用char* argv[]方法是可以的,但在输入中不行。我不知道为什么pybind11会这样做!

P.S.2. 在pybind11 gitter上, 有人建议:

void run(const std::vector<std::string>& args) {
    for(auto&& e : args) std::cout << e << '\n';
}

P.S.3. 在 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
}

5
std::vector args(argv, argv + argc); 是什么意思? - André
@André,我想应该没问题。能否在帖子中详细说明一下? - Foad S. Farimani
我可能误解了你的问题。你是想用std::vector args(argv, argv+argc)“简单”迭代遍历你的参数吗?还是你想要相反的方式:从给定的std::vector < std::string > 构建一个char** argv,int argc? - André
@André,我的问题有两个。1.如何以除了char** argvchar* arg[]之外的任何形式获取命令行参数的第二个参数。我最初认为可以使用某种形式的std::vector<std::string>,但我不确定是否可能。也许是char* argv_void* argv_。2.如何将第二个参数转换/解析为char* argv[]的形式,因为这在函数体中是可能的。 - Foad S. Farimani
@Foad,您的问题不是很清楚。请详细说明您需要调用的函数的确切签名以及为什么有这些限制。 - Acorn
@Acorn,这些函数的确非常多。我曾试图在此帖子中提供简明的表示方式。另外,char** varchar* var[]不被允许的原因是由于pybind11的限制,如此处所述。我在原始帖子中链接了所有这些参考资料。此外,随着我进一步研究并尝试添加新信息到**P.S.**中,问题正在逐渐展开。 - Foad S. Farimani
2个回答

8
你可以使用构造函数从给定的范围初始化向量,argv 参数充当起始迭代器,argv+argc 充当结束迭代器。
例如,我通常以以下方式开始我的主函数:
int main( int argc, char* argv[] )
{
    std::vector< std::string > args( argv, argv + argc );

    for ( auto s : args )
    {
        std::cout << s << std::endl;
    }
}

请注意,这也将捕获第一个参数(argv[0]),该参数通常(但不一定)在启动应用程序时保存应用程序的名称。
在您的情况下,您希望相反,从std::vector< std::string >构建连续的char*数组。 我会做类似以下的操作:
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 
}

然后您可以将它们传递给一个接受 argc、argv 的函数。

someFunction( rargs.size(), rargs.data() );

值得注意的是,这是std::vector的迭代器构造函数。argv参数充当“begin”迭代器,指向char*数组中的第一项,而argv + argc则充当“end”迭代器,指向char*数组中的末尾。 - Arthur Tacca
请稍等,char* argv []也不允许作为主函数的输入参数。 :( - Foad S. Farimani
我还必须将 int argc,char ** argv 传递给 someFunction。这是我无法改变的! - Foad S. Farimani
1
在函数参数的上下文中,char**char*[] 是完全等价的。可以随意使用任何一个,本答案中的代码仍然可以工作。 - Arthur Tacca
2
@ArthurTacca:实际上,char*[]会“退化”为char**。你总是会传递一个指针,而不是一个数组。这个区别很重要,因为在被调用的函数中,你无法使用sizeof确定数组的长度(因为你看到的是一个指针,而不是一个数组)。这就是为什么标准一定要让你正确地获取argv的原因——argc告诉你它的大小,并且它是以零结尾的。 - DevSolar
@DevSolar 我曾经以数学家的思路考虑问题,认为说“x等同于y”与“y等同于x”是一样的。但现在回过头来看,我同意我表述反了,感谢你指出这一点。实际上,char **衰减为char * []是没有意义的,因为一般来说指针可能不是来自数组。 - Arthur Tacca

2

就这样吧...回到你最初的问题,即无法使用char**与pybind11一起使用。下面是从你发布的片段中拼凑出来的一个完整的工作示例。是的,它不太好看,但使用指针工作从来都不是容易的。

#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");
}

编译后可以按预期使用:

$ 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
>>> 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接