文件或流的路径?

时间:2016-12-01 00:08:29

标签: c++ architecture

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

在设计和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;
}

2 个答案:

答案 0 :(得分:0)

你声明自己可以添加"帮助"函数以保留istream接口。这在可测试性方面也是更好的解决方案,并且遵循单一责任原则(SRP)。

你的帮助函数有一个责任(从文件创建流)和你的实际函数(它"做某事" :))。

我补充一点,这取决于实际做了什么的背景。例如,如果对于底层功能的不同访问是facade,那么将该接口与实际路径一起使用是有意义的。你还有一个单独的辅助函数和一个do_something函数,它们是从正面使用的。

答案 1 :(得分:0)

你可以吃蛋糕并吃掉它:

#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()));
}