在 C++ 中使用 execvp 的最佳实践

Best practice to use execvp in C++

一开始我是这样写的

char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", argv);

但是,GCC 弹出了这个 警告,"C++ forbids converting a string constant to char*."

然后,我将代码更改为

const char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", argv);

结果,GCC弹出这个错误,"invalid conversion from const char** to char* const*."

然后,我将代码更改为

const char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", (char* const*)argv);

终于可以编译了,没有任何警告和错误,但我觉得这有点麻烦,我在网上找不到任何人写过这样的东西。

在 C++ 中使用 execvp 有更好的方法吗?

execvpe 需要一个 char *const argv[] 作为第二个参数。也就是说,它需要一个指向非常量数据的常量指针列表。 C 中的字符串文字是 const,因此警告问题和将 argv 转换为 char* const* 是一个 hack,因为现在允许 execvp 写入 argv 中的字符串。我看到的解决方案是为每个项目分配一个可写缓冲区,或者只使用 execlp 而不是与 const char* args 一起使用并允许传递字符串文字。

你遇到了一个真正的问题,因为我们面临着两个不兼容的约束:

  1. 一个来自 C++ 标准的要求你必须使用 const char*:

    In C, string literals are of type char[], and can be assigned directly to a (non-const) char*. C++03 allowed it as well (but deprecated it, as literals are const in C++). C++11 no longer allows such assignments without a cast.

  2. 另一个来自遗留 C 函数原型,需要一个(非常量)char* 数组:

    int execv(const char *path, char *const argv[]);
    

因此一定有一个 const_cast<> 某处,我找到的唯一解决方案是包装 execvp 函数。

这是此解决方案的完整 运行 C++ 演示。不便之处是你有一些胶水代码要写一次,但好处是你得到了更安全更干净的C++11代码(最后的nullptr被勾选) .

#include <cassert>
#include <unistd.h>

template <std::size_t N>
int execvp(const char* file, const char* const (&argv)[N])
{
  assert((N > 0) && (argv[N - 1] == nullptr));

  return execvp(file, const_cast<char* const*>(argv));
}

int main()
{
  const char* const argv[] = {"-al", nullptr};
  execvp("ls", argv);
}

你可以编译这个演示:

g++ -std=c++11 demo.cpp 

您可以在 CPP Reference example for std::experimental::to_array 中看到类似的方法。

这是 execvp() 的声明(不能保证不修改其参数,为了向后兼容)和字符串文字作为常量数组的 C++ 解释之间的冲突 char .

如果演员关系到你,你剩下的选择是复制参数列表,像这样:

#include <unistd.h>
#include <cstring>
#include <memory>
int execvp(const char *file, const char *const argv[])
{
    std::size_t argc = 0;
    std::size_t len = 0;

    /* measure the inputs */
    for (auto *p = argv;  *p;  ++p) {
        ++argc;
        len += std::strlen(*p) + 1;
    }
    /* allocate copies */
    auto const arg_string = std::make_unique<char[]>(len);
    auto const args = std::make_unique<char*[]>(argc+1);
    /* copy the inputs */
    len = 0;                    // re-use for position in arg_string
    for (auto i = 0u;  i < argc;  ++i) {
        len += std::strlen(args[i] = std::strcpy(&arg_string[len], argv[i]))
            + 1; /* advance to one AFTER the nul */
    }
    args[argc] = nullptr;
    return execvp(file, args.get());
}

(您可能认为 std::unique_ptr 有点矫枉过正,但是如果 execvp() 失败,此函数 正确清理,并且函数 returns).

演示:

int main()
{
    const char *argv[] = { "printf", "%s\n", "one", "two", "three", nullptr };
    return execvp("printf", argv);
}
one
two
three