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

Posted

技术标签:

【中文标题】用 std::vector<std::string> 替换命令行参数 int argc 和 char** argv【英文标题】:replacing the command line arguments int argc and char** argv with std::vector<std::string> 【发布时间】:2020-05-19 07:03:19 【问题描述】:

在this post 之后,我找到了my other problem 的临时解决方法,我想知道是否可以将int argc, char** argv 替换为std::vector&lt;std::string&gt; 变量/对象。

考虑虚构的代码:

#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 命令行参数。但是对于some reason,我的代码中不能有双指针char**

Here in this post 提出了类似我需要的东西,但我不知道如何使用它。我可以这样写代码吗:

#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. Here on 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

【问题讨论】:

std::vector args(argv, argv + argc) 怎么样? ? @André 我猜应该没问题。请在帖子中详细说明? 我可能误解了你的问题。你想用 std::vector args( argv, argv+argc ) “简单”地遍历你的 args 吗?或者你想做相反的事情:从给定的 std::vector 建立一个 char** argv, int argc ? @André 我的问题有两个。 1. 如何获取命令行参数的第二个参数,除了char** argvchar* arg[]以外的任何形式。我最初认为我可以使用某种形式的std::vector&lt;std::string&gt;,但我不确定这是否可能。也许char* argv_void* argv_。 2. 如何将第二个参数转换/解析为char* argv[] 的形式,因为这在函数体中是可能的。 @Foad 不清楚你到底在问什么。请详细说明您需要调用的函数的确切签名以及您有这些限制的原因。 【参考方案1】:

对于它的价值...完全回到您无法将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
>>> 

【讨论】:

【参考方案2】:

您可以使用从给定范围初始化向量的构造函数,其中argv 参数充当起始迭代器,argv+argc 充当结束迭代器。

例如,我通常以以下方式开始我的 main 函数:

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 构建一个连续的 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 as 的函数

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

【讨论】:

可能值得注意的是,这是std::vector 的迭代器构造函数。 argv 参数充当“开始”迭代器,指向char* 数组中的第一项,而argv + argc 充当“结束”迭代器,指向char* 数组中末尾的一个元素. 等等,char* argv[] 也不允许作为主函数的输入。 :( 我还必须将int argc, char** argv 提供给someFunction。这是我无法改变的! @Foad 在函数参数的上下文中,char** 完全等同于char*[]。随意使用您喜欢的任何一个,此答案中的代码仍然有效。 @ArthurTacca:实际上,char*[]“降级”为char**。您将总是传递一个指针,而从不一个数组。这种区别很重要,因为在被调用的函数中,您无法使用sizeof 确定数组的长度(因为您看到的是指针,而不是数组)。这就是为什么标准使 double 确保你得到 argv 正确 -- argc 告诉你它的大小 并且 它是零终止的。

以上是关于用 std::vector<std::string> 替换命令行参数 int argc 和 char** argv的主要内容,如果未能解决你的问题,请参考以下文章

用 boost.python 暴露 std::vector<double>

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

std::vector<bool>中的坑

vector<string> 批量 赋值 c++

从 std::vector<std::vector<float>> 转换为 float**

如何将 std::vector<std::vector<double>> 转换为 torch::Tensor?