命令行参数的第二个参数采用 char** argv 或 char* argv[] 以外的格式

Foa*_*oad -6 c++ segmentation-fault argv command-line-arguments

为了解决我的问题在这里,我想知道是否/如何我可以比在格式定义的命令行参数的第二个变量其他char** argvchar* argv[]原因是 pybind11 不允许函数输入中的任何一个。以下是我尝试过的方法:

方法一:

#include <stdio.h>

int main(int argc, int* argv_){
    for (int i = 0; i < argc; ++i){
        printf("%s\n", (char *)(argv_[i]));
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方法背后的基本原理是指针本质上是一个整数,通过将地址转换为char指针,人们应该能够获得字符串。提前感谢您的支持。

方法二:

#include <stdio.h>
#include <string>

int main(int argc, std::string* argv_){
    for (int i = 0; i < argc; ++i){
        printf("%s\n", argv_[i].c_str());
    }
}
Run Code Online (Sandbox Code Playgroud)

方法三:

#include <stdio.h>
#include <string>
#include <vector>

int main(int argc, std::vector<std::string> argv_){
    for (int i = 0; i < argc; ++i){
        const char* argv__ = argv_[i].c_str();
        printf("%s\n", argv_[i].c_str());
    }
}
Run Code Online (Sandbox Code Playgroud)

问题:

不幸的是,上述所有方法都会导致臭名昭著的segmentation fault.

如果您能帮助我了解问题所在(即内存泄漏在哪里)以及如何解决这些问题,我将不胜感激。

解决方法/黑客:

在评论中我被告知,如果使用任何其他形式而不是main(), main(int argc, char** argv), 或main(int argc, char* argv[]),它将不可避免地导致segmentation fault. 但是,下面的代码有效:

#include <stdio.h>

int main(int argc, long* argv_){
    for (int i = 0; i < argc; ++i){
        printf("%s\n", (char *)(argv_[i]));
    }
}
Run Code Online (Sandbox Code Playgroud)

这适用于 Ubuntu 最小g++ 7.4.0和 Windows 10 Visual Studio 2019 编译器。但是,它不与clang. 正如其他人指出的那样,这不是解决方案,而且是一种非常糟糕的做法。根据编译器、操作系统和内存的当前状态,它可能会导致未定义的行为。这不应该在任何实际代码中使用。在任何C / C ++代码的主要功能必须的形式main()main(int argc, char** argv)main(int argc, char* argv[])

Ted*_*gmo 6

main毕竟它看起来并不需要,所以你可以这样做:

#include <iostream>
#include <string>
#include <vector>

int cppmain(std::string program, std::vector<std::string> args) {
    std::cout << program << " got arguments:\n";
    for(auto& arg : args) {
        std::cout << " " << arg << "\n";
    }
    return 0;
}

int main(int argc, char* argv[]) {
    // create a string from the program name and a vector of strings from the arguments
    return cppmain(argv[0], {argv + 1, argv + argc});
}
Run Code Online (Sandbox Code Playgroud)

如果您需要调用类似于 main 的闭源函数(您无法更改),请创建一个可以pybind调用的包装函数,并让该函数调用闭源函数。

#include <cstddef>
#include <iostream>
#include <string>
#include <vector>

int closed_source_function(int argc, char* argv[]) {
    for(int i = 0; i < argc; ++i) {
        std::cout << argv[i] << '\n';
    }
    return 0;
}

int pybind_to_this(std::vector<std::string> args) {
    // create a char*[]
    std::vector<char*> argv(args.size() + 1);

    // make the pointers point to the C strings in the std::strings in the
    // std::vector
    for(size_t i = 0; i < args.size(); ++i) {
        argv[i] = args[i].data();
    }

    // add a terminating nullptr (main wants that, so perhaps the closed source
    // function wants it too)
    argv[args.size()] = nullptr;

    // call the closed source function
    return closed_source_function(static_cast<int>(args.size()), argv.data());
}
Run Code Online (Sandbox Code Playgroud)

  • 他们不是。`cppmain` 函数的输入是 `std::string` 和 `std::vector&lt;std::string&gt;`。 (3认同)

Dev*_*lar 5

让我们试着一一解决在冗长的讨论中出现的大量问题。


问题 1:为什么在使用某些非标准参数(如字符串向量或 int 指针)时会出现段错误main

int, char **C 和 C++ 标准都以这种方式定义了的参数类型。除了非标准扩展之外,您不能使用其他类型。

来自 ISO/IEC 9899(C 语言),5.1.2.2.1 程序启动

程序启动时调用的函数名为 main。实现声明没有此函数的原型。它应定义为返回类型int并且不带参数:

int main(void) { /* ... */ }

或带有两个参数(这里称为argcand argv,尽管可以使用任何名称,因为它们对于声明它们的函数是本地的):

int main(int argc, char *argv[]) { /* ... */ }

或同等学历; 或以其他一些实现定义的方式。

最后一句话允许我提到的那些扩展。我知道的一个这样的扩展是 GCC 的environ

https://www.gnu.org/software/libc/manual/html_node/Program-Arguments.html#Program-Arguments


问题 2:我如何解决这个问题?

你没有。

使用与标准或编译器扩展定义的类型不同的类型是Undefined Behavior,它可以 - 但不需要 - 导致段错误。不要调用未定义的行为。不要“绕过”标准。它不是“解决方法”,更不是“解决方案”,它是随时可能在您面前炸毁的损坏代码。


问题 3:如何使用pybindachar **作为参数的第三方函数?

你不这样做,因为这不是一个由支持的数据类型pybind


问题 4:那么,我如何通过 接口来实现这样的功能pybind呢?

您编写了一个包装函数,在前端,该函数采用(eg )支持的参数,适当地编组这些参数,然后使用编组后的参数您调用第三方后端函数。(然后,当然,如果需要,对返回类型反向执行相同的操作。)pybindstd::vector< std::string >

有关如何执行此操作的惯用示例,请参阅@TedLyngmo 的答案


问题5:我pybind可以给第三方main吗?

这是不明智的,因为这main是一个特殊的函数,被调用的代码可能会做出假设(如atexit回调),你的调用代码不遵守,不能遵守。它当然不是第三方所期望的作为库函数调用的函数。

  • @Foad:完全修改了答案。如果您的问题(和评论)的任何部分仍未得到答复,请留言。 (2认同)