【问题标题】:Override the call to main()?覆盖对 main() 的调用?
【发布时间】:2026-01-11 23:25:06
【问题描述】:

我正在处理一个项目,其中我们有几个共享多个目标文件的可执行文件。我们希望将日志记录添加到所有可执行文件中,并有一个库来执行此操作。

但是,转到每个可执行文件的 main() 函数并添加相同的样板函数调用以启动日志记录似乎很笨拙。这意味着我们重新编写相同的东西,并放松可维护性和 DRY(“不要重复自己”)。如果我们能够系统地确保在调用 main 函数之前开始记录日志,那就太好了。

我突然想到 libc++ 中有一些函数可以调用 main,并且可以覆盖它们。但是,我不知道它们是什么,并且想象如果我们不小心,这可能会破坏事情。有谁知道如何做到这一点?或者,如果这太过分了,还有其他关于如何进行的建议吗?

如果有什么不同的话,我们将使用 C++11 和 g++ 4.8。

【问题讨论】:

  • 为什么不在全局变量的构造函数/析构函数中“注入”你的启动和关闭代码呢?剩下的唯一问题是多次执行此操作;然后没有指定它们初始化的顺序,所以你不应该依赖任何。
  • 在每个可执行文件中覆盖 main 函数如何比在每个可执行文件中添加一个函数调用到 main 更易于维护?
  • 这不是重复的。他没有问“我如何让代码在 main 之前运行”?他问过如何解决问题,这可能是解决方案的一部分。

标签: c++ libc++


【解决方案1】:

你不需要通过修改main().来做到这一点

您应该改为在共享对象库中的全局范围内创建一个类。此类的构造函数将在 main() 运行之前执行您想要执行的“初始化”,而它的析构函数将在 main() 之后运行。

您需要处理的问题是,对于任何其他全局范围的对象,不能保证此初始化和销毁​​的顺序是确定性的。所有这些都可以放在一个 .cpp 编译单元中。

class LoggingManager // you can make this a singleton but not necessary
{
   public:
      LoggingManager(); 

      ~LoggingManager();
};

LoggingManager::LoggingManager()
{
     // your initialisation code goes here
}

LoggingManager::~LoggingManager()
{
     // your clean-up code goes here. It should not throw
}

LoggingManager loggingManagerStaticInstance; 

请注意,存在“静态初始化”问题的小风险,这意味着实际上您的 loggingManagerStaticInstance 可能要等到您的编译单元第一次被访问后才会被加载。

实际上,这是否在main() 之后并不重要,只要初始化发生在第一次需要它之前(有点像单例),但这意味着您的编译单元可能需要包含一些保证被拉进去。

如果你想“坚持”到 gnu 或类似的,他们提供 __attribute__(constructor) 可能会解决它,尽管有一种更简单的方法可以实现一些虚拟 extern int 或返回一个从任何标题中调用的 int 的虚拟函数你确实用它来实现日志记录。

【讨论】:

  • 我暂时不投反对票,但我认为这是不正确的。全局对象是在调用其翻译单元的第一个函数之前的某个时间创建的。这很可能是在 main 被调用之后。
  • 我喜欢! @MSalters 很高兴知道。我不一定需要在 main 被调用之前开始记录;只要我跟踪我将全局对象放入的编译单元,并确保我们一开始就在其中调用了一些东西,我们就应该很好,对吧?
  • @bchurchill:是的,但通常有更好(不那么脆弱)的方法:在返回引用的getLogger() 方法中使记录器成为static 变量。这会在第一次需要时构建记录器。
  • 太棒了。好多了。