在 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 一起使用并允许传递字符串文字。
你遇到了一个真正的问题,因为我们面临着两个不兼容的约束:
一个来自 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.
另一个来自遗留 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
一开始我是这样写的
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 一起使用并允许传递字符串文字。
你遇到了一个真正的问题,因为我们面临着两个不兼容的约束:
一个来自 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.另一个来自遗留 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