【问题标题】:RAII char ** to be used as exec argument vectorRAII char ** 用作 exec 参数向量
【发布时间】:2016-05-11 11:23:26
【问题描述】:

我目前正在创建一个应用来启动外部应用。 启动外部应用程序的签名是:

int launchApp(int argc, char** argv); // argc = amount of arguments, argv = arguments

要向std::vector<char *> 结构添加参数,我使用以下 lambda:

auto addArgument = [](std::vector<char *> & lArguments,
                       const std::string & sArgument)
{
   auto cstr = new char[sArgument.size() + 1];
   std::copy(sArgument.cbegin(), sArgument.cend(), cstr);
   cstr[sArgument.size()] = '\0';
   lArguments.push_back(cstr);
};

并启动外部应用程序:

std::vector<char *> lArguments;
addArgument(lArguments, "Argument 1");
addArgument(lArguments, "Argument 2");
launchApp(lArguments.size(),static_cast<char**>(lArguments.data());
//... Clean up arguments 

我将如何以 RAII 方式执行此操作? 我正在考虑改用std::vector&lt;std::vector&lt;char&gt;&gt;。但是,我怎样才能将底层原始数据(char**)传递给launchApp()static_cast&lt;char**&gt;(lArguments.data()) 不行...

【问题讨论】:

  • 我想知道std::vector&lt;std::unique_ptr&lt;char[]&gt;&gt; 是否可以工作...std::unique_ptr 可以没有超出它所拥有的原始指针的开销,但我不知道假设您可以简单地使用reinterpret_cast 是否安全向量data 指针。

标签: c++ c++11 memory-management raii


【解决方案1】:

我通常分两部分进行:

  1. 构建一个包含实际参数的std::vector&lt;std::string&gt;
  2. 构建一个std::vector&lt;const char*&gt;,其中每个元素都是字符串向量中对应元素的.data()

第一个向量和其中包含的字符串处理您的分配、调整大小等,而第二个向量仅充当由第一个向量管理的内存的索引。第二个向量的生命周期应该比第一个向量的生命周期短,这可以通过将两者包装在一个类中来保证。

Example:

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

int main() {
    std::vector<std::string> args = {"echo", "hello", "world"};
    std::vector<const char*> argv;
    argv.reserve(args.size());
    for (auto& arg : args) {
        argv.push_back(arg.data());
    }

    execvp("echo", const_cast<char**>(argv.data()));    
}

(注意丑陋的const_cast。这取决于你如何看待它,这要么是因为exec* 系列函数不遵循 const 正确性,要么是因为 std::string::data() 没有非常量版本(在 C++17 之前))。


或者,with the class to wrap it all up nicely

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

class ExecArguments {
 public:
  ExecArguments(std::initializer_list<std::string> arguments)
   : args(arguments) {
       for (auto& arg : args) {
           argv.push_back(const_cast<char*>(arg.data()));
       }
   }

   char** data() {
       return const_cast<char**>(argv.data());
   }

 private:
  std::vector<std::string> args;
  std::vector<char*> argv;
};

int main() {
    ExecArguments args{"echo", "hello", "world"};

    execvp(args.data()[0], args.data());    
}

【讨论】:

  • 谢谢,我喜欢这个答案,但是,不幸的是,您必须创建另一个具有额外开销的向量才能将其作为参数传递。
  • @A.Fagrell 是的,有一点。尽管第二个向量的开销非常小,尤其是如果您以适当的大小开始创建它。
【解决方案2】:
  • 将您的参数收集为常规字符串。
  • 使用一个内部类:
    • 为原始指针数组提供透明的隐式视图,并且
    • 同时负责管理这个指针数组。

这个内部类的一个实例由一些访问方法返回。它的生命周期跨越调用语句。请参阅下面的示例:

   #include <iostream>
   #include <vector>

   class CollectArgs : public std::vector<std::string> {
     // just for providing the raw view and taking care of its memory
     class RawArgs {
       std::vector<char const*>  m_argv;
     public:
       RawArgs(std::vector<char const*>  argv) {
         std::swap(argv, m_argv);
       }
       ~RawArgs() {}

     public:
       operator char const* const*() const { return  m_argv.data(); }
     };

   public:
     RawArgs raw_args() const {
       std::vector<char const*>  argv;
       for(std::string const &arg : *this)  argv.push_back(arg.c_str());
       return  argv;
     }
   };

   // the application launcher
   void call(unsigned  argc, char const *const *argv) {
     for(unsigned  i = 0; i < argc; i++) {
       std::cout << argv[i] << std::endl;
     }
   }

   int main() {
     CollectArgs  args;
     args.push_back("Arg1");
     args.push_back("Arg2");

     // create the raw view and have it destroyed immediately after this statement
     call(args.size(), args.raw_args());
   }

【讨论】:

    猜你喜欢
    • 2019-11-04
    • 2019-08-12
    • 1970-01-01
    • 2013-10-09
    • 1970-01-01
    • 2011-07-09
    • 1970-01-01
    • 2012-01-22
    • 2014-12-28
    相关资源
    最近更新 更多