如何自动包含目录中的所有标题

Posted

技术标签:

【中文标题】如何自动包含目录中的所有标题【英文标题】:How to auto-include all headers in directory 【发布时间】:2010-03-12 18:21:54 【问题描述】:

我正在做一本 C++ 书籍的练习。对于每个练习,我都想尽量减少必须编写的样板代码。我已经以某种方式设置了我的项目,但它似乎不正确,并且需要进行太多更改。

现在我有一个 main.cpp 文件,其中包含以下内容:

#include "e0614.h"
int main()

    E0614 ex;
    ex.solve();

每次我从练习中创建一个新类时,我都必须来修改这个文件以更改包含的标题的名称以及我正在实例化的类。

所以我的问题是:

    我可以在目录中包含所有标题,这样至少我不必更改#include 行吗? 更好的是,我是否可以重写我的解决方案,这样我什至不必触摸main.cpp,而不需要一个包含每个练习的所有代码的文件?

更新:

我最终按照 Poita_ 的建议通过脚本生成 main.cpp

由于我使用的是 IDE (Visual Studio),因此我希望将其与它集成,因此对如何进行了一些研究。对于那些对如何做感兴趣的人,请继续阅读(这很简单,但并不完全简单)。

Visual Studio 允许您通过“工具”->“外部工具”菜单使用外部工具,并包含一堆预定义的变量,例如 $(ItemFileName),这些变量可以传递给该工具。因此,在本例中,我使用了一个简单的批处理文件,并通过它传递了 Visual Studio 中当前选定文件的名称。

要将该工具添加到工具栏,请右键单击工具栏,选择自定义 -> 命令 -> 工具,然后选择“外部命令 X”并将其拖动到工具栏。将 X 替换为与您创建的工具相对应的编号。我的安装包含工具 -> 外部工具中列出的 5 个默认预先存在的工具,因此我创建的工具编号为 6。您必须弄清楚这个数字,因为它没有显示。然后,您可以为快捷方式分配一个图标(它是如下所示的 BuildMain 命令):

【问题讨论】:

抱歉,这里几乎没有样板文件。 - 您所要求的 - 包括所有可能的标题 - 可能会为您节省 2 秒的打字时间(一次性成本),并且如果运气好的话,可以让简单的代码编译十分钟(每次重新编译时)。 @UncleBens:主要目标并不是真正包含所有头文件,而是避免修改 main.cpp。为了简单起见,我选择了它作为问题标题,并且我认为在此过程中可能会有一些#ifdef 警卫。 【参考方案1】:

    没有。如果您想这样做,则必须将它们全部包含在内。

    没有。至少,不会以真正节省打字的方式。

当然,你可以编写一个脚本来为你创建 main.cpp...

【讨论】:

【参考方案2】:

如果您使用make 构建代码,您应该能够做到这一点。

我可以在目录中包含所有标题,这样至少我不必更改#include 行吗?

将包含行更改为 #include <all_headers.h> 之类的内容。现在,您可以让您的 Makefile 自动生成 all_headers.h,目标如下:

all_headers.h:
    for i in `ls *.h`; do echo "#include <$i>" >>all_headers.h; done

确保在“make clean”时all_headers.h 被删除。

更好的是,我可以重写我的解决方案,这样我什至不必接触 main.cpp, 没有一个包含每个练习的所有代码的文件?

如果你用 typedef 抽象出你的类,你就可以做到这一点。在您的示例中,将您的班级名称从E0614 更改为myClass(或其他名称)。现在,在上面的 for 循环下方添加一行到您的 Makefile,上面写着 echo "typedef "$MY_TYPE" myClass;" &gt;&gt;all_headers.h。构建程序时,使用类似make MY_TYPE=E0614 的内容调用“make”,您的 typedef 将自动填充您要测试的类。

【讨论】:

该构建规则存在问题:除非您手动 make cleantrash all_headers.h,否则它不会获取新标头。考虑到他正在逐步完成书中的练习,这是一个很大的障碍。 我不会称其为“重大”障碍。如果他要添加新的源文件,他可能每次都在做make rebuild(使用像rebuild: clean build 这样的目标)。【参考方案3】:

如果您使用的是 Unix 系统,则可以有一个指向最新练习的软链接。

ln -s e0615.h latest.h

当然,将你的类命名为 E 而不是 E0614

附:据我所知,你不能这样做#include xxx*

【讨论】:

【参考方案4】:

不要使用您为每个练习修改的一个 main.cpp。这个解决方案利用了make的内置规则,所以你只需要输入make e0614,它就会生成e0614.cpp,编译并链接它。您可以自定义每个 .cpp 文件(它们不会像下面写的那样重新生成)并在完成练习时保留所有这些历史以供参考,而不是在从一个移动到另一个时将其删除下一个。 (您还应该使用源代码管理,例如Mercurial。)

生成文件

e%.cpp:
        ./gen_ex_cpp $@ > $@

您可以使用脚本生成样板代码,因为您也不希望它变得乏味。这些脚本有多种选择——我使用多种语言,包括 C++、Python 和 shell——但下面的 Python 很短,在这里应该足够简单明了。

示例生成脚本

#!/usr/bin/python
import sys
args = sys.argv[1:]
if not args:
  sys.exit("expected filename")
name = args.pop(0).partition(".")[0]
if args:
  sys.exit("unexpected args")
upper_name = name.upper()
print """
#include "%(name)s.hpp"
int main() 
  %(upper_name)s ex;
  ex.solve();
  return 0;

""" % locals()

【讨论】:

请注意,随着这个 Python 脚本的增长,它将被重构为具有 main 函数并使用 optparse(或类似)模块。【参考方案5】:

制作一个包含所有你想要的头文件名称的主包含文件。

即使可以包含 * 也是一个非常糟糕的主意。

【讨论】:

主文件在这里并没有真正的帮助。这只是意味着他将所有头文件都包含在主文件中,而不是在 main.cpp 中。它在其他情况下可能会有所帮助,但不是在这里。【参考方案6】:

您可以通过使用连接对类名使用条件编译。

// Somewhere in your other files
define CLASS_NUMBER E0614

// in main.cpp
#define ENTERCLASSNUMBER(num) \
##num## ex;

// in main()
ENTERCLASSNUMBER(CLASS_NUMBER)

虽然不知道包含。如上所述,脚本可能是最佳选择。

【讨论】:

【参考方案7】:

编写一个生成文件规则以将可执行文件的名称作为 -DHEADERFILE=something 参数传递给编译器应该一点也不难。比如:

%.exe : %.h %.cpp main.cpp
    gcc -o $< -DHEADER_FILE=$<F $>

OTOH,我不知道#include 是否对文件名进行宏扩展。

【讨论】:

【参考方案8】:
sed -i 's/\<\\([eE]\\)[0-9]+\\>/\19999/' main.cpp

将 9999 替换为所需的号码。可能有更好的方法。

【讨论】:

【参考方案9】:

为什么不使用对象机制?

您可以为此使用示例策略。

class BaseExercise

public:
  static bool Add(BaseExercise* b)  Collection().push_back(b); return true; 
  static size_t Solve() 
    size_t nbErrors = 0;
    for(collections_type::const_iterator it = Collection().begin(), end = Collection().end(); it != end; ++it)
      nbErrors += it->solve();
    return nbErrors;
  

  size_t solve() const
  
    try 
      this->solveImpl();
      return 0;
     catch(std::exception& e) 
      std::cout << mName << " - end - " << e.what() << std::endl;
      return 1;
    
  
protected:
  explicit BaseExercise(const char* name): mName(name)
  
  
private:
  typedef std::vector<BaseExercise*> collection_type;
  static collection_type& Collection()  collection_type MCollection; return MCollection; 

  virtual void solveImpl() const = 0;

  const char* mName;
; // class BaseExercise

template <class T>
class BaseExerciseT: public BaseExercise

protected:
  explicit BaseExerciseT(const char* b): BaseExercise(b)  
    static bool MRegistered = BaseExercise::Add(this);
  
;

好的,这就是基础。

// Exercise007.h
#include "baseExercise.h"

class Exercise007: public BaseExerciseT<Exercise007>

public:
  Exercise007(): BaseExerciseT<Exercise007>("Exercise007") 
private:
  virtual void solveImpl() const  ... 
;

// Exercise007.cpp
Exercise007 gExemplar007;

对于主要的

// main.cpp
#include "baseExercise.h"

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

  size_t nbErrors = BaseExercise::Solve();
  if (nbErrors) std::cout << nbErrors << " errors" << std::endl;
  return nbErrors;

在这里,您不需要任何脚本;)

【讨论】:

【参考方案10】:

试试这个:-

#ifndef a_h
#define a_h

#include <iostream>
#include <conio.h>
#incl....as many u like
class a
f1();//leave it blank
int d;

#endif //save this as a.h

稍后 将此包含在您的主程序中,即 cpp 文件中

#include "a.h"

...你的程序

【讨论】:

以上是关于如何自动包含目录中的所有标题的主要内容,如果未能解决你的问题,请参考以下文章

包含目录中的所有文件?

如何使用或运算符递归计算目录中的所有代码行以包含多种文件类型

C#返回位于其中包含文件夹的目录中的所有文件[重复]

如何使用 SWIG 包装许多 .h 文件并包含任何依赖项?

如何根据列中的值自动填充谷歌表格中的数据

使用 jQuery 预加载目录中的所有图像