文件或流的路径

Path to file or stream?

本文关键字:路径 文件      更新时间:2023-10-16

我正在与同事讨论,我认为这是一个很好的问题。

设计和API时,您的功能何时应该接受文件路径,什么时候应该接受流?有指南吗?

void do_something(const std::filesystem::path &file_path);
void do_something(std::istream &stream);

路径:

  • Callee负责检查文件是否存在并且可访问。
  • 很难单位测试。您必须在磁盘上创建/有一个文件进行测试。

流:

  • 呼叫者负责检查文件是否存在并且可以访问。更多重复的样板代码。
  • 单位测试更容易您只需传递流对象

我想一个人可以在库中添加一个函数以"帮助"打开文件,其中一些:

std::ifstream open_input(const std::filesystem::path &file)
{
    std::ifstream stream(file);
    if (not stream) {
        throw std::invalid_argument("failed to open file: " + file.string());
    }
    return stream;
}

您说自己可以添加"助手"功能以保留iStream接口。就可检验性而言,这也是更好的解决方案,并遵守单个责任原则(SRP)。

您的辅助功能具有一个责任(从文件创建流),而您的实际功能另一个责任(它"做某事":)。

我补充说,这取决于实际上有什么作用的上下文。例如,如果它是用于对基础功能的不同访问的外墙,那么将该接口与实际路径保持相关是有意义的。您仍然会有一个单独的辅助功能和一个从立面上使用的do_something功能。

您可能有蛋糕并吃掉它:

#include <fstream>
#include <sstream>
#include <utility>
//
// some booilerplate to allow use of a polymorphic temporary
template<class Stream, std::enable_if_t<std::is_base_of<std::istream, Stream>::value> * = nullptr>
struct stream_holder
{
  stream_holder(Stream stream) : stream_(std::move(stream)) {}
  operator std::istream&() && { return stream_; }
  operator std::istream&() & { return stream_; }
  private:
  Stream stream_;
};
// helper function
template<class Stream, std::enable_if_t<std::is_base_of<std::istream, Stream>::value> * = nullptr>
auto with_this(Stream&& stream)
{
  return stream_holder<std::decay_t<Stream>>(std::forward<Stream>(stream));
}

// express logic in terms of stream
void do_something(std::istream& stream_ref);
// utility functions to create various types of stream
std::ifstream file_stream();
std::stringstream string_stream();

int main()
{
  // * composability with succinct syntax
  // * lifetime automatically managed
  // * no repetitive boilerplate
  do_something(with_this(file_stream()));
  do_something(with_this(string_stream()));
}