在 C++ 中解析 main 函数的大量输入的正确方法是什么
What is the right way to parse large amount of input for main function in C++
假设有30个数字我必须输入到一个可执行文件中,因为输入量很大,通过命令行输入它们是不合理的。一种标准方法是将它们保存到单个 XML 文件中,然后使用 XML 解析器(如 tinyxml2)来解析它们。问题是,如果我使用 tinyxml2 直接解析输入,我将拥有一个非常臃肿的主函数,这似乎是 contradict 常见的良好做法。
例如:
int main(int argc, char **argv){
int a[30];
tinyxml2::XMLDocument doc_xml;
if (doc_xml.LoadFile(argv[1])){
std::cerr << "failed to load input file";
}
else {
tinyxml2::XMLHandle xml(&doc_xml);
tinyxml2::XMLHandle a0_xml =
xml.FirstChildElement("INPUT").FirstChildElement("A0");
if (a0_xml.ToElement()) {
a0_xml.ToElement()->QueryIntText(&a[0]);
}
else {
std::cerr << "A0 missing";
}
tinyxml2::XMLHandle a1_xml =
xml.FirstChildElement("INPUT").FirstChildElement("A1");
if (a1_xml.ToElement()) {
a1_xml.ToElement()->QueryIntText(&a[1]);
}
else {
std::cerr << "A1 missing";
}
// parsing all the way to A29 ...
}
// do something with a
return 0;
}
但另一方面,如果我写一个额外的class只是为了解析这些特定类型的输入以缩短主函数,它似乎也不对,因为这个额外的class 将无用,除非它与此主要功能一起使用,因为它不能在其他地方重复使用。
int main(int argc, char **argv){
int a[30];
ParseXMLJustForThisExeClass ParseXMLJustForThisExeClass_obj;
ParseXMLJustForThisExeClass_obj.Run(argv[1], a);
// do something with a
return 0;
}
最好的处理方法是什么?
请注意,除了读取 XML 文件外,您还可以通过标准输入传递大量数据。使用例如mycomplexcmd | hexdump -C
,其中 hexdump
通过管道从标准输入读取。
现在剩下的问题:有理由使用您的多功能示例(这里它们是构造函数还是普通函数并不重要).这与为什么您希望任何函数更小——可读性几乎相同。也就是说,我不知道 "common good practice",而且我见过很多 main()
.
的终端实用程序
想象一个新人正在阅读 main()
的第一个变体。他们会费尽心机地找出所有这些句柄、查询、子项、父项——而他们只想查看 // do something with a
之后的部分。这是因为他们不知道这是否与他们的问题相关。但在第二个变体中,他们会很快弄清楚 "Aha, it's the parsing logic, it's not what I am looking for".
也就是说,您当然可以通过详细的评论打破逻辑。但是现在想象出了问题,有人正在调试代码,他们将问题归咎于这个函数 (好吧,这很有趣,因为函数是 main()
,也许他们刚刚开始调试)。该错误非常微妙,不清楚,并且正在检查函数中的所有内容。现在,因为你正在处理可变语言,所以你经常会发现自己处于你认为 "oh, may be it's something with this variable, where it's being changed?"; 的境地。然后你首先通过这个大函数查找变量的每一次使用,然后是可能导致变量发生变化的块的条件;然后你弄清楚这个与条件相关的另一个大块是做什么的,它可以被提取到一个单独的函数中,在那里使用了哪些变量;在您弄清楚它在做什么的那一刻,您已经忘记了之前看到的一半内容!
当然有时候大函数是不可避免的。但如果你问这个问题,那可能不是你的情况。
经验法则:你看到一个函数做两个不同的事情,几乎没有共同点,你想把它分解成 2 个独立的函数。在您的情况下,它正在解析 XML 和 "doing something with a"。尽管如果第二部分是几行,可能不值得提取——推测一下。不用担心开销,编译器擅长优化。您可以使用 LTO,也可以将 .cpp
文件中的函数仅声明为静态 (非 class 静态),并根据优化选项编译器可以内联代码。
P.S.: 你似乎处于学习'n'玩一些Haskell非常有用的状态。您不必将它用于真正严肃的项目,但您获得的见解可以应用到任何地方。它迫使你进行更好的设计,特别是当有必要破坏一个功能时你会很快开始感觉到 (除了许多其他事情).
假设有30个数字我必须输入到一个可执行文件中,因为输入量很大,通过命令行输入它们是不合理的。一种标准方法是将它们保存到单个 XML 文件中,然后使用 XML 解析器(如 tinyxml2)来解析它们。问题是,如果我使用 tinyxml2 直接解析输入,我将拥有一个非常臃肿的主函数,这似乎是 contradict 常见的良好做法。
例如:
int main(int argc, char **argv){
int a[30];
tinyxml2::XMLDocument doc_xml;
if (doc_xml.LoadFile(argv[1])){
std::cerr << "failed to load input file";
}
else {
tinyxml2::XMLHandle xml(&doc_xml);
tinyxml2::XMLHandle a0_xml =
xml.FirstChildElement("INPUT").FirstChildElement("A0");
if (a0_xml.ToElement()) {
a0_xml.ToElement()->QueryIntText(&a[0]);
}
else {
std::cerr << "A0 missing";
}
tinyxml2::XMLHandle a1_xml =
xml.FirstChildElement("INPUT").FirstChildElement("A1");
if (a1_xml.ToElement()) {
a1_xml.ToElement()->QueryIntText(&a[1]);
}
else {
std::cerr << "A1 missing";
}
// parsing all the way to A29 ...
}
// do something with a
return 0;
}
但另一方面,如果我写一个额外的class只是为了解析这些特定类型的输入以缩短主函数,它似乎也不对,因为这个额外的class 将无用,除非它与此主要功能一起使用,因为它不能在其他地方重复使用。
int main(int argc, char **argv){
int a[30];
ParseXMLJustForThisExeClass ParseXMLJustForThisExeClass_obj;
ParseXMLJustForThisExeClass_obj.Run(argv[1], a);
// do something with a
return 0;
}
最好的处理方法是什么?
请注意,除了读取 XML 文件外,您还可以通过标准输入传递大量数据。使用例如mycomplexcmd | hexdump -C
,其中 hexdump
通过管道从标准输入读取。
现在剩下的问题:有理由使用您的多功能示例(这里它们是构造函数还是普通函数并不重要).这与为什么您希望任何函数更小——可读性几乎相同。也就是说,我不知道 "common good practice",而且我见过很多 main()
.
想象一个新人正在阅读 main()
的第一个变体。他们会费尽心机地找出所有这些句柄、查询、子项、父项——而他们只想查看 // do something with a
之后的部分。这是因为他们不知道这是否与他们的问题相关。但在第二个变体中,他们会很快弄清楚 "Aha, it's the parsing logic, it's not what I am looking for".
也就是说,您当然可以通过详细的评论打破逻辑。但是现在想象出了问题,有人正在调试代码,他们将问题归咎于这个函数 (好吧,这很有趣,因为函数是 main()
,也许他们刚刚开始调试)。该错误非常微妙,不清楚,并且正在检查函数中的所有内容。现在,因为你正在处理可变语言,所以你经常会发现自己处于你认为 "oh, may be it's something with this variable, where it's being changed?"; 的境地。然后你首先通过这个大函数查找变量的每一次使用,然后是可能导致变量发生变化的块的条件;然后你弄清楚这个与条件相关的另一个大块是做什么的,它可以被提取到一个单独的函数中,在那里使用了哪些变量;在您弄清楚它在做什么的那一刻,您已经忘记了之前看到的一半内容!
当然有时候大函数是不可避免的。但如果你问这个问题,那可能不是你的情况。
经验法则:你看到一个函数做两个不同的事情,几乎没有共同点,你想把它分解成 2 个独立的函数。在您的情况下,它正在解析 XML 和 "doing something with a"。尽管如果第二部分是几行,可能不值得提取——推测一下。不用担心开销,编译器擅长优化。您可以使用 LTO,也可以将 .cpp
文件中的函数仅声明为静态 (非 class 静态),并根据优化选项编译器可以内联代码。
P.S.: 你似乎处于学习'n'玩一些Haskell非常有用的状态。您不必将它用于真正严肃的项目,但您获得的见解可以应用到任何地方。它迫使你进行更好的设计,特别是当有必要破坏一个功能时你会很快开始感觉到 (除了许多其他事情).