首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >execv的C++包装器(3)

execv的C++包装器(3)
EN

Code Review用户
提问于 2017-10-06 12:36:34
回答 1查看 4K关注 0票数 2

execv(3)中调用C++ (和类似的函数)可以是成问题execv需要一个指向非const char的const指针数组,而字符串文本本身就是const,所以您必须跳过循环才能将文本转换为非const数组。当然,在C++中,您通常希望使用向量和字符串而不是数组。

由于找不到任何标准函数来处理这个问题,我编写了两个包装器函数来简化操作。在我认为测试的方式上,它们似乎是正确的,但我对C++相当陌生;更有经验的开发人员可能会看到我错过的许多陷阱。

我的第一个版本只是使用argv[0]作为路径。然后,在某些情况下,我检查并找到了用于好的理由path != argv[0],因此我重载了它;一个版本更通用,另一个版本简化了调用。

代码语言:javascript
复制
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>

#include <unistd.h>


int execv_cpp(const std::string &path,
              const std::vector<std::string> &argv);
int execv_cpp(const std::vector<std::string> &argv);


int main()
{
    // Test some failure modes.

    execv_cpp({});
    perror("execv() 1 failed");

    execv_cpp("/bin/ls", {});
    perror("execv() 2 failed");

    execv_cpp({"/foo/bar/baz"});
    perror("execv() 3 failed");

    execv_cpp({"/etc/passwd"});
    perror("execv() 4 failed");

    // Test valid calls.

    if (fork() == 0) {
        execv_cpp({"/bin/ls", "-lart"});
        perror("forked execv() unexpectedly failed");
        return 0;
    }

    usleep(50000);
    execv_cpp("/bin/ls", {"ls", "-lh"});
    perror("final execv() unexpectedly failed");

    return 0;
}


int execv_cpp(const std::string &path,
              const std::vector<std::string> &argv)
{
    /* Convert arguments to C-style and call execv. If it returns
     * (fails), clean up and pass return value to caller. */

    if (argv.size() == 0) {
        errno = EINVAL;
        return -1;
    }

    std::vector<char *> vec_cp;
    vec_cp.reserve(argv.size() + 1);
    for (auto s : argv)
        vec_cp.push_back(strdup(s.c_str()));
    vec_cp.push_back(NULL);

    int retval = execv(path.c_str(), vec_cp.data());

    int save_errno = errno;
    for (auto p : vec_cp)
        free(p);
    errno = save_errno;
    return retval;
}


int execv_cpp(const std::vector<std::string> &argv)
{
    /* Overloaded. Use first element as path for simpler call. */

    if (argv.size() == 0) {
        errno = EINVAL;
        return -1;
    }

    return execv_cpp(argv[0], argv);
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2017-10-07 08:24:43

评论不是按任何重要程度排列的。

哪个操作系统标准?

支持C++的平台不一定有execve()调用;这取决于<unistd.h>。你确定你不是在假设它包含了什么吗?

也许你不是,但你需要反复检查并记录下来。

封装单个操作系统调用是有问题的

如果你写了这个供你自己使用,因为你一直execve()就像没有明天一样:-) .那好吧。否则,开发人员可能会犹豫不决,不愿合并一段独特的代码,它只处理几十个系统调用中的一个。

此外,即使专注于执行另一个项目,我们也有:

代码语言:javascript
复制
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);

我并不是说你需要实现所有这些的“Cpp化”版本--相反,这可能是个坏主意--但你选择了不那么通用的版本。

不向全局命名空间

添加实用程序/库函数

应用程序代码使用(或者更确切地说,可能使用)全局命名空间;库代码使用专用名称空间。您所写的应该被反复使用,而不仅仅是一次--所以它本质上是库代码,对吗?将其放在某个名称空间中(例如utilunix等)这样做的一个好处是,您不必附加人工名称扩展名,即unix::exec听起来比execve_cpp好。

在第二个变体

中混淆参数

如果我看看你的第二个变体:

代码语言:javascript
复制
int execv_cpp(const std::vector<std::string> &argv);

我不清楚该向量的元素是否应该是我希望执行的二进制文件的名称。您可能认为您为客户端代码提供了更多方便,但是人们宁愿多输入几个字符,也不愿与语义歧义作斗争。

-如果您正在Cpp‘化-这样做的错误以及

在C++中,传递无效的参数通常会导致抛出异常;而且您不需要返回任何内容,因为您从不返回(而不是抛出异常)。因此,例如:

代码语言:javascript
复制
void execv_cpp(const std::vector<std::string> &argv)
{
    if (argv.size() == 0) { 
        throw std::invalid_argument(
            "At least one argument must be provided, being the path of "
            "the binary to execute");
    execv_cpp(argv[0], argv);
}

但是,当然,通过一开始就愿意接受一个空的argv,可以使调用代码容易出错。

不强迫人们使用std::vector's

向量只是一个特定的容器。没有理由假设调用代码使用的是向量;而且您甚至没有实际使用该向量--您正在将其转换为C样式的数组。

在C++中,你通常想使用向量.而不是数组。

不是这样的。你有时会想要用它们。

不强迫人们使用std::strings

当然,在C++中你通常想要使用.弦乐..。

有时,有时不是。std::string只是表示字符串的一种方式--尽管是默认的方式。记住,它在图书馆里-它不是在他的语言里。std::string涉及内存分配策略(堆栈和堆等)。这对每个人都不合适。您应该更加灵活,例如,通过对字符串类型进行模板化:

代码语言:javascript
复制
template<typename S>
void execv_cpp(const std::vector<S> &argv);

或者使用字符串视图 (C++17,C++14中的实验)。

考虑有界参数

的C++'ish接口

如果在编译时知道参数的数量,则只需使用以下内容:

代码语言:javascript
复制
template <typename Args...>
void execv_cpp(std::string_view binary, Args&&... arguments);

并在循环中的每个参数上使用std::to_string()operator<<作为字符串流。这为调用代码提供了最大的灵活性,并使二进制代码的exec()与调用任意函数基本相同:

代码语言:javascript
复制
execv_cpp(my_binary, 1, "two", std::optional<int>(3), 4.0);

现在,是的,在这种情况下,为了字符串转换结果,您将耗尽内存,但在固定参数的情况下,我会说,这是可以接受的。

不需要重复字符串

在您链接到的回答中,它明确表示const_cast的char指针是安全的。所以不需要strdup()

票数 5
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/177304

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档