feat: Add a parse(FILE *) interface

The fstream classes are notorious for their non-existent error handling.

This adds a C-style fILE * IO (fopen(), etc.) alternative interface, so
that if a user needs reliable error handling, they can use that, albeit
more inconvenient, but more robust approach.
This commit is contained in:
Lukáš Hrázký
2022-06-30 13:53:32 +02:00
parent 594accf9a7
commit bf9c9d620d
3 changed files with 146 additions and 15 deletions

View File

@@ -2377,27 +2377,14 @@ result<Value, std::string> parse_toml_file(location& loc)
return ok(Value(std::move(data), file, comments));
}
} // detail
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
template<typename ...> class Table = std::unordered_map,
template<typename ...> class Array = std::vector>
basic_value<Comment, Table, Array>
parse(std::istream& is, std::string fname = "unknown file")
parse(std::vector<char>& letters, const std::string& fname)
{
using value_type = basic_value<Comment, Table, Array>;
const auto beg = is.tellg();
is.seekg(0, std::ios::end);
const auto end = is.tellg();
const auto fsize = end - beg;
is.seekg(beg);
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<char> letters(static_cast<std::size_t>(fsize));
is.read(letters.data(), fsize);
// append LF.
// Although TOML does not require LF at the EOF, to make parsing logic
// simpler, we "normalize" the content by adding LF if it does not exist.
@@ -2435,12 +2422,70 @@ parse(std::istream& is, std::string fname = "unknown file")
return data.unwrap();
}
} // detail
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
template<typename ...> class Table = std::unordered_map,
template<typename ...> class Array = std::vector>
basic_value<Comment, Table, Array>
parse(FILE * file, const std::string& fname)
{
const long beg = std::ftell(file);
if (beg == -1l) {
throw file_io_error(errno, "Failed to access", fname);
}
int res = std::fseek(file, 0, SEEK_END);
if (res != 0) {
throw file_io_error(errno, "Failed to seek", fname);
}
const long end = std::ftell(file);
if (end == -1l) {
throw file_io_error(errno, "Failed to access", fname);
}
const auto fsize = end - beg;
res = std::fseek(file, beg, SEEK_SET);
if (res != 0) {
throw file_io_error(errno, "Failed to seek", fname);
}
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<char> letters(static_cast<std::size_t>(fsize));
std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), file);
return detail::parse<Comment, Table, Array>(letters, fname);
}
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
template<typename ...> class Table = std::unordered_map,
template<typename ...> class Array = std::vector>
basic_value<Comment, Table, Array>
parse(std::istream& is, std::string fname = "unknown file")
{
const auto beg = is.tellg();
is.seekg(0, std::ios::end);
const auto end = is.tellg();
const auto fsize = end - beg;
is.seekg(beg);
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<char> letters(static_cast<std::size_t>(fsize));
is.read(letters.data(), fsize);
return detail::parse<Comment, Table, Array>(letters, fname);
}
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
template<typename ...> class Table = std::unordered_map,
template<typename ...> class Array = std::vector>
basic_value<Comment, Table, Array> parse(std::string fname)
{
std::ifstream ifs(fname.c_str(), std::ios_base::binary);
std::ifstream ifs(fname, std::ios_base::binary);
if(!ifs.good())
{
throw std::runtime_error("toml::parse: file open error -> " + fname);