【问题标题】:Command line arguments for different constructors不同构造函数的命令行参数
【发布时间】:2017-01-01 19:09:19
【问题描述】:

假设我有一些 C++ 抽象类,它所有的继承类都有不同的构造函数:

class Abstract{
//don't worry, there is some pure virtual method here
}

class A : public Abstract {
public:
  A (int Afirst, std::string Asecond, int Athird) {...}
...
} 

class B : public Abstract {
public
  B (double Bfirst, std::int Bsecond) {...}
...
}

class C : public Abstract {
public 
  C (std::string Cfirst, double Csecond, int Cthird, float Cfourth) {...}
}

如您所见,所有继承的类都有(可能)不同的构造函数。

现在,我想写一个通用的main(),类似于:

int main (int argc, char *argv[]){
  if(argc < 2){
    std::cerr<<"Too few arguments!"<<std::endl;
    exit(1);
  }
  std::string type = argv[1];
  Abstract *abs;
  if(!type.compare("A"){
    if(argc < 5){
      std::cerr<<"Too few arguments for A!"<<std::endl;
      exit(1);
    }
    abs = new A(atoi(argv[2]), argv[3], argv[4]); 
  }
  //similar for B, C, D
} 

我想知道是否有最好的方法来做到这一点,例如直接将char *argv[] 传递给每个构造函数并在构造函数内部进行所有检查(并最终如here 所述抛出异常)。

【问题讨论】:

  • 您将无法避免检查参数是否与给定的构造函数相匹配,但我不会用检查代码污染类。我会编写一个工厂函数,它采用argcargv 并返回适当类的实例。这样可以保留检查代码。

标签: c++ constructor abstract-class


【解决方案1】:

你可以做这样的事情来成为通用的:

// functions to convert const char* to given type
template <typename T> T To(const char*);

template <> int To(const char* s) { return atoi(s); }
template <> const char* To(const char* s) { return s; }
template <> std::string To(const char* s) { return s; }
// ...

// Your classes:
struct Abstract { virtual ~Abstract() = default; };

struct A : Abstract { A (int, std::string, int) {}};
struct B : Abstract { B (int, int) {}};
// ...

namespace detail
{    
    // Helper functions for the factory.
    template <typename T, typename Tuple, std::size_t... Is>
    std::unique_ptr<Abstract> make_abstract(const char*argv[], std::index_sequence<Is...>)
    {
        return std::make_unique<T>(To<std::tuple_element_t<Is, Tuple>>(argv[2 + Is])...);
    }

    template <typename T, typename Tuple>
    std::unique_ptr<Abstract> make_abstract(int argc, const char*argv[])
    {
        constexpr int tuple_size = std::tuple_size<Tuple>::value;
        if (argc < tuple_size) {
            throw std::runtime_error("Too few arguments");   
        }
        return make_abstract<T, Tuple>(argv, std::make_index_sequence<tuple_size>());
    }
}

// The public factory
std::unique_ptr<Abstract> make_abstract(int argc, const char*argv[])
{
    if (argc == 1) {
        return nullptr;
    }
    const std::string name = argv[1];
    if (name == "A") {
        return detail::make_abstract<A, std::tuple<int, std::string, int>>(argc, argv);
    } else if (name == "B") {
        return detail::make_abstract<B, std::tuple<int, int>>(argc, argv);
    }
    // ...
    return nullptr;
}

【讨论】:

  • 我的编辑呢?我从here
  • @justHelloWorld:这可能是可能的,但我更喜欢为这个示例保留我的,因为它是 OP 用于转换的,让空白作为普通字符串。另外,我认为它展示了如何扩展其他类型为enum
  • 你能解释一下代码吗?在我看来它有点先进
  • 我对函数make_abstract(const char*argv[], std::index_sequence&lt;Is...&gt;)有点困惑(从未使用过std::index_sequence,很少使用std::tuple,很少使用变量号va_arg
  • index_sequence 用于迭代 tuple(在当前代码中表示构造函数的参数类型)。因此,借助可变参数模板,第一个助手将执行 return make_unique&lt;A&gt;(To&lt;int&gt;(argv[2]), To&lt;std::string&gt;(argv[3]), To&lt;int&gt;(argv[4])); 之类的操作。 std::make_index_sequence&lt;3&gt;()std::index_sequence&lt;0, 1, 2&gt;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多