【问题标题】:How can I avoid #includ-ing <sstream> thanks to a template function?由于模板功能,我怎样才能避免#includ-ing <sstream>?
【发布时间】:2016-08-05 13:20:10
【问题描述】:

我写了一个非常简单的日志类。

日志记录类有一个如下所示的函数:

template <typename T, typename... Args>
void log (T && first, Args &&... rest) {
  log (std::forward <T>    (first));
  log (std::forward <Args> (rest)...);
}

template <typename T>
void log (T && last) {
  /* use <sstream>'s std::ostringstream to convert each T to its string
     representation, then print it via a callback. */
}

由于我的日志记录类可能会在我的程序中经常使用,因此我正在尝试减少由于#includ-ing 我的记录器文件而附加到每个翻译单元上的代码量。

我已经设法将除 &lt;string&gt; 之外的所有 #include 语句从头文件中移出并移到实现文件中,除了整个 &lt;sstream&gt; 及其所有依赖项,其中是实现此模板成员函数所必需的。

我考虑过显式实例化我需要的所有重载,但如果我希望将我的记录器与字符串文字一起使用,那么我认为我必须为const char (&amp;) [n] 中的每个n 显式实例化一个模板。或#include &lt;type_traits&gt; 为std::decay。 :(

是否可以避免将&lt;sstream&gt; 拖到所有使用我的记录器的 TU 中?怎么样?

【问题讨论】:

  • 你有没有想过使用std::to_string() (C++11)?看看这是否有助于删除&lt;sstream&gt;。对于其他类,您可以实现特定于类的public 方法to_string()。
  • 也许PIMPL idiom 可能是相关的
  • @iammilind 嗯。我可以自己进行转换(使用to_string() 等等)。我真的不需要&lt;sstream&gt; 来做到这一点。对于其他任何事情,“to_string”(非?)成员函数是完全可以接受的。
  • 如果你的 log 函数所做的只是包装一个流,你为什么不直接实现 operator&lt;&lt;?
  • @uhohsomebodyneedsapupper 没有理由不这样做。坦率地说,我发现函数调用语法更好,我将主要通过宏使用它(用于行号、文件等)。

标签: c++ dependencies header-files


【解决方案1】:

在我的项目中,我合并的一种方法是使用 C++11 中的 std::to_string()。这样可以避免使用&lt;sstream&gt;。您可能只需要包含 &lt;string&gt; 标头。对于特定的类,你可以实现自己的公共方法to_string()s。

但不幸的是,Android 版本(我们一直使用到 2016 年年中)没有 std::to_string(),即使使用 C++11。在这种情况下,我们必须依赖sstream。但是,您可以通过使用下面的template 和宏技巧(玩具代码)来避免在头文件中包含&lt;sstream&gt;:

// header.h
template<typename ostringstream, typename T>
std::string my_to_string (T&& value)
{
  ostringstream oss;
  oss << value;
  return oss.str();
}

#define my_to_string(X) my_to_string<std::ostringstream>(X)

现在,在所有实现中,您必须输入 &lt;sstream&gt; 并使用如下:

// implementation.cpp
#include<sstream>
int main ()
{
  double d = 1.2345;
  std::string s = my_to_string(d);
  std::cout << "converted: " << s << "\n";
}

Demo.

【讨论】:

  • to_string 只是sprintf 的包装器(标准明确定义为这样),所以此时您不妨使用sprintf。
  • 有了这个解决方案,我需要在任何地方#include &lt;sstream&gt;,这违背了不简单地将其包含在头文件中的意义。模板实例化的有趣技巧。
  • @MaxBozzi,是的,这是正确的。有一个叫做&lt;iosfwd&gt; 的东西会在一定程度上帮助你向前声明某些std::s。但是,如果您想使用来自&lt;sstream&gt; 的对象,那么您必须以一种或另一种方式使用#include。但是如果你的实现允许to_string(),那么你可以避免&lt;sstream&gt;。 @uhoh,我只是想保留更多的c++,因此使用了to_string()。
  • 好吧——我不熟悉&lt;iosfwd&gt;,所以让我赶紧去查一下:)。我想to_string 与一些显式重载结合使用可能会有所帮助......
  • 顾名思义,iosfwd 只包含前向声明。单独使用任何流库是不够的。
猜你喜欢
  • 1970-01-01
  • 2013-07-24
  • 1970-01-01
  • 1970-01-01
  • 2013-10-08
  • 1970-01-01
  • 2022-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多