我正与同事讨论,我认为这是一个很好的问题,可以放在SO上。
在设计和API时,您的函数应何时接受文件路径以及何时接受流?有没有指导方针?
void do_something(const std::filesystem::path &file_path);
void do_something(std::istream &stream);
路径:
流:
我想可以在库中添加一个函数来帮助"打开文件,其中包括:
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;
}
答案 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()));
}