【问题标题】:Using #include to include sections of code使用#include 包含代码段
【发布时间】:2013-03-11 10:39:35
【问题描述】:

我正在使用第 3 方开源应用程序,它做了一些我认为很奇怪的事情。我想听听您的意见,您是否认为这是错误的/邪恶的/可憎的/等等,或者是否有正当理由这样做。

简单地说,他们使用#include pre-proc 指令来包含包含代码片段的“头文件”。不是函数的原型。不是内联函数。只是部分代码。

这是一个简单的例子。首先是main.cpp文件:

#include <iostream>
//Other "normal" includes here...

int main(int argc, char *argv[]) {

  cout << "Initializing program..." << endl;
  #include "parseArgs.h"

  // ... remainder of the program

  cout << "Exiting." << endl;
  return 0;
}

在 parseArgs.h 头文件中,有一个小代码片段。请注意,这正是 parseArgs.h 文件中的内容。这不是函数的一部分。没有包含守卫,只有以下 4 行:

argList args(argc, argv);
if(!args.valid()) {
  cout << "Invalid arguments.";
  exit(1);
}

在实际程序中,有几个这样的#include 指令,每个都执行另一个小任务。

这看起来既危险又疯狂。我不知道他们为什么不把这些写成函数。

你的想法和意见?

【问题讨论】:

  • 我不认为这特别危险,但听起来确实很疯狂且更难维护。
  • 我不推荐它,因为它使代码难以遵循和维护,但它在技术上没有任何问题。
  • 通常人们使用函数和类似的东西来重用代码......
  • 我特别喜欢包含的代码片段如何依赖于局部变量(可能不存在,或者存在但与预期的完全不同的东西),以及 using namespace std; 声明在包含之前执行。看起来像一场等待发生的灾难。
  • 也许这样做的人来自 PHP 世界。因为这在 PHP 社区中是一种几乎可以接受的做法

标签: c++ include


【解决方案1】:

我认为您在这里谈论的是OpenFOAM。这些代码 sn-ps 解决的问题是避免重复 OpenFOAM 中的许多应用程序需要的样板代码。将此代码放入函数中并不能解决问题,因为在函数中声明的变量是其作用域的局部变量。也许可以想出一个包含这些变量作为成员的基类方案。但这只会增加另一层间接性,并不能真正解决任何问题。你仍然依赖于变量名(或者,如果你想让它干净,getter-names)。

就我个人而言,我不确定这种做法是好是坏。它就是这样,它是 OpenFOAM 代码文化的一部分(或本地术语,如果你愿意的话)。乍一看很奇怪,但很快就习惯了。

但是,除非您自己开发 OpenFOAM 应用程序/扩展,否则我强烈反对这种做法。 OpenFOAM 有点独特,因为它包含几乎数百个可执行文件,所有这些都需要一些重叠的样板代码,否则很难维护。如果您不是那种情况,请不要这样做。

【讨论】:

  • 内联函数解决了这些问题,更惯用,更安全。
  • 不,他们没有。如果您以 OP 给出的示例为例,那么缺少的是 sn-p 声明的 args 变量将被省略的部分使用。内联函数不会给你这个。而且——正如我所说——这是OpenFOAM 的特定领域语言 的一部分。我强烈建议不要在 OpenFOAM 之外这样做。
  • 困惑...inline argList make_args(int argc, char *argv[]) { return argList args(argc, argv); };为生产代码添加错误检查。
  • 你如何返回,比如说 10 个变量?当然,如果您的编译器足够新,您可以使用boost::tuplestd::tuple,但 OpenFOAM 开始时甚至还没有 C++ 标准库,而 CFront 是唯一支持模板的编译器.
  • 如果你有 10 个变量要返回,你应该使用某种状态对象。
【解决方案2】:

我不愿称其为“疯狂”,因为这种#include 用法可能是有原因的。问题是要了解这些原因并判断这是否是给定上下文中的最佳方法。我可以想象的原因是,代码是以某种方式生成的,而不是手写的。

OTOH 它确实看起来很奇怪,并且确实有一定的代码气味。我也很想知道究竟是什么原因,如果事实证明没有原因或原因错误,那么它是重构的一个很好的候选者。

【讨论】:

    【解决方案3】:

    我认为这很混乱,你最好写一个函数。

    这是我在 SO 上发现的一个问题,询问 "Why it's valid to include a header file twice in c++?" 你可能会觉得答案很有趣,我永远不会这样写我的代码,因为我认为修复错误和任何其他问题会很痛苦而且很耗时

    【讨论】:

      【解决方案4】:

      我不会将这种事情称为“疯狂”——我会使用“不寻常”、“难以理解”、“出乎意料”等术语,因此“难以阅读、调试和维护”。或者只是“WTF”-decreasing code quality

      永远不要那样做。使用函数。或者,如果它真的,真的必须是,使用一个完全相同但人们更熟悉的宏。是的,宏很糟糕,在调试时可能会变成皮塔饼。但这更糟。

      编辑: 澄清:我不喜欢宏。我尽可能避免使用它们。大多。使用函数,模板,任何东西。但是当涉及到“宏或 WTF-#include”时,请使用宏。

      【讨论】:

      • 在这种情况下,宏更糟糕。宏适用于单语句样板,但是较长的代码段会很麻烦,因为它们会有效地禁用您的调试器。
      • 是的,我知道。我是这么写的。调试宏并不好玩。所以#include 的这种使用可能比宏更容易调试,但这已经足够了。它比宏更令人困惑。 除调试以外的其他方面都比宏差
      【解决方案5】:

      如果您在团队中工作,则需要建立程序和标准以确保沟通顺畅。一旦解决了这个问题,你说的是葡萄牙语还是普通话都没有关系,只要每个人都能适应。

      这是一种心态。有些人——包括与我一起工作的老师——被规则所奴役;但是,我不能认真对待它们,特别是当其中一个为了教 OO 使用“生菜”和“水果”作为示例时。我怀疑 Kohlberg 的阶段有一点影响了某些人的思维。

      我在个人项目中使用这种技术 - 包括 source.cpp。我已经在 C 和 Lazarus (Pascal) 中完成了它,它比摆弄链接器参数和 makefile 更符合我的目的。我的代码有 cmets,变量声明是对齐的,赋值运算符也是对齐的,当学生给我代码时,我会遍历所有代码,插入空格和行,直到它看起来像我喜欢的方式,然后再分析它。我痴迷于干净的源代码组织。你看?疯子有办法!

      工具(#include、#define、lambda...)就是:工具,但当然,意外使用工具可能会迷失方向; 不好。更重要的是,当“软件的心理复杂性”(谷歌搜索)伤害到(您或您团队中的某个人)时,请立即停下来仔细研究问题所在。

      这是我的 2cts。

      【讨论】:

        猜你喜欢
        • 2018-06-07
        • 2014-02-25
        • 1970-01-01
        • 1970-01-01
        • 2019-02-06
        • 1970-01-01
        • 2010-11-27
        • 2012-06-09
        • 1970-01-01
        相关资源
        最近更新 更多