c++ 为继承父类创建人工argv数组,在构造函数中接受类似main的参数

q35jwt9p  于 2023-07-01  发布在  其他
关注(0)|答案(2)|浏览(121)

我正在努力处理遗留代码(计划重构,但我现在需要使用它),它的devicedrivers被设计为接受其构造函数的(argc, argv)参数。我必须使用的基类,现在我不能改变它,看起来像这样:

class LegacyDriver
{
public:
  LegacyDriver(int argc, char** argv)
  {
    if (argc < 3)
    {
      throw std::runtime_error("nope");
    }
    // some logic with the arguments interpretation
    int var1 = std::stoi(argv[1]);
    int var2 = std::stoi(argv[2]);
    std::cout << "config: " << var1 << ", " << var2 << "\n";
    // etc ...
  }
  virtual ~LegacyDriver() = default;
  // + some public interfaces, not important ...
};

然后我的新驱动程序必须扩展这个类,需要以某种方式将argc, argv参数传递给基类。到目前为止,我的同事们只是沿着:

class MyNewDriver: public LegacyDriver
{
public:
  MyNewDriver(int argc, char** argv): LegacyDriver(argc, argv)
  {}
  ~MyNewDriver() override = default;
};

但是这种设计显然是不可持续的(例如,很难测试),我想至少为新驱动程序提供一个有意义的构造函数,比如:

MyNewDriver(int param1, int param2): LegacyDriver(somehowTransformThemIntoArgcArgv)
  {}

我不能为此使用额外的MyNewDriver私有字段,因为父类必须在此之前构造。我想到了这样的hack,在全局范围内使用字符串和向量:

namespace {
std::string hackyProgramName = "Driver";
std::string hackyVar1;
std::string hackyVar2;
std::vector<const char*> hackyArgv;
auto constructArgv = [](int var1, int var2)
{
  hackyVar1 = std::to_string(var1);
  hackyVar2 = std::to_string(var2);
  hackyArgv = std::vector<const char*>({hackyProgramName.c_str(), hackyVar1.c_str(), hackyVar2.c_str()});
  return const_cast<char**>(hackyArgv.data());
};
}
class MyNewDriver: public LegacyDriver
{
public:
  MyNewDriver(int argc, char** argv): LegacyDriver(argc, argv)
  {}
  MyNewDriver(int param1, int param2): LegacyDriver(3, constructArgv(param1, param2))
  {}
  ~MyNewDriver() override = default;
};

但是它看起来太丑了,argv vector在我的驱动程序的每个新示例中都无效。
有没有更好的办法?最好是单个lambda。

envsm3lx

envsm3lx1#

因为你必须维护一个有效的argv数组,并且在构造LegacyDriver之前要做的动作,所以你可以继承一个类。

class PreDriver {
protected:
    PreDriver( int var1, int var2 ) {
        hackyVar1 = std::to_string( var1 );
        hackyVar2 = std::to_string( var2 );
        hackyArgv = std::vector<const char*>{"Driver", hackyVar1.c_str(), hackyVar2.c_str()};
    };
    constexpr int  argc()const     { return 3; }
    char**  argv() { return const_cast<char**>(hackyArgv.data()); }
private:
    std::string hackyVar1;
    std::string hackyVar2;
    std::vector<const char*>  hackyArgv;
};

class MyNewDriver : private PreDriver, public LegacyDriver {
public:
    MyNewDriver( int param1, int param2 ) : PreDriver(param1,param2), LegacyDriver( this->PreDriver::argc(), this->PreDriver::argv() ) {}
    ~MyNewDriver() override = default;
};
yqyhoc1h

yqyhoc1h2#

当你移动一个向量时,指向它的指针仍然有效。所以你可以先构造argv vector,然后将它移动到一个成员:

class ArgsHolder {
    std::vector<std::string> string_storage;
    std::vector<char*> argv_storage;
public:
    /*explicit(false)*/ ArgsHolder(std::vector<std::string> v) : string_storage(std::move(v)), argv_storage(string_storage.size() + 1) {
        // Remember the +1 for the nullptr at argv[argc]
        for (std::size_t i = 0; i < string_storage.size(); ++i)
            argv_storage[i] = string_storage[i].data();
    }
    char** argv() noexcept { return argv_storage.data(); }
    int argc() noexcept { return argv_storage.size() - 1u; }
};

class MyNewDriver: public LegacyDriver
{
  ArgsHolder args;
public:
  MyNewDriver(int argc, char** argv): LegacyDriver(argc, argv)
  {}
  MyNewDriver(ArgsHolder args): LegacyDriver(args.argc(), args.argv()), args(std::move(args)) {}
  MyNewDriver(int param1, int param2)
   : MyNewDriver(ArgsHolder({ "Driver", std::to_string(param1), std::to_string(param2) }))
  {}
  ~MyNewDriver() override = default;
};

相关问题