parse(FILE*) is a minor overload, but dispatching strerror takes too
much cost. Standard library version is not thread-safe, so some compiler
reports a warning. There are thread-safe versions defined in XSI, GNU,
and Windows. XSI/GNU versions can be detected by macros, but in some
cases, detection-by-macro written in the doc does not work.
Since errno can be obtained from the exception, users can call strerror
that is available in their env if needed. We can just report errno.
Previously a key like:
"a\u0000\u0001b" = 1
Would get written with literal control characters, rather than escapes:
"a<00><01>b" = 1
The "valid/key/quoted-unicode" test from toml-test would fail with this,
although it seems they're not run automatically(?)
Can also reproduce with something like:
% cat test.cpp
#include <toml.hpp>
#include <iostream>
int main()
{
const auto data = toml::parse("test.toml");
std::cout << data << "\n";
return 0;
}
% cat test.toml
"a\u0000\u0001b" = "a\u0000\u0001b"
% c++ -I. test.cpp
% ./a.out
"ab" = "a\u0000\u0001b"
% ./a.out | hexdump -C
00000000 22 61 00 01 62 22 20 3d 20 22 61 5c 75 30 30 30 |"a..b" = "a\u000|
00000010 30 5c 75 30 30 30 31 62 22 0a 0a |0\u0001b"..|
We are using patched libc++ which uses raw pointers for vector itrators
to improve code compilation speed. This commit fixed two compilation
issues in toml11:
* location::const_iterator deinition assumes that vector const_iterator
is struct or class type rather than raw pointer.
* `const const_itetr foo()` triggers `-Wignored-qualifiers` for primitive
types and `void` which breaks `-Wextra -Werror` compilation.
When creating the inner iterator, make sure it points into the same
vector as the outer iterator. Otherwise, attempts to reset the iterator
wind up causing it to read out-of-bounds.
Fixes#199.
- fix error messages that referred to the wrong functions.
- parse_key(): remove "detail::" from the only error message that had
it, for consistency with the other error messages in that function
The 'result' class has unwrap() and unwrap_err() member functions
overloaded for const lvalue and rvalue *this to avoid an unnecessarily
copying the to-be unwrapped object of its containing object is going to
be discarded anyway. Alas, the parse() function toml/parser.hpp file
stored the parse result in a local `const` variable so, although the
unwrap call would have been the last use of the object in each case, the
unnecessary copy would still be made. This patch removes the `const`
and adds a std::move() to actually benefit from the already implemented
optimization.
This patch addresses a static analysis issue reported by Cppcheck 2.9
where several member functions of the toml::discard_comment class
defined in the toml/comments.hpp header were implemented to deliberately
dereference the null pointer returned unconditionally by the
always-empty container's data() member function. This behavior wasn't
technically wrong because those functions all have as precondition that
the container is non-empty so they must never be called on an instance
of toml::discard_comment but we can still be more helpful without
adversely affecting code generation. Instead of dereferencing the null
pointer, this patch has these functions call an inline private helper
function which is defined to invoke __builtin_unreachable() if available
"and then" throw an exception with a helpful error message. Even at the
-O1 level, GCC will optimize the code under the assumption that the
function will never be called (i.e. no assembly is emitted), making
failure to ensure this undefined behavior exactly as if the null pointer
had been dereferenced. However, static analysis will now understand the
programmer's intent and remain silent. Furthermore, when using the -O0
or -Og levels, GCC won't optimize under this assumption so the exception
will be thrown and might be helpful for debugging. Compilers that don't
have __builtin_unreachable() won't get any help in determining that the
function must not be called and will have to figure this out by
analyzing the calling code -- which really shouldn't exist in the first
place anyway as the whole point is that these functions must not be
called.
This patch addresses a static analysis issue reported by Cppcheck 2.9
where an assertion in the toml/region.hpp header would compare two
container's (that are known to be of type std::vector<char>) begin() and
end() iterators in order to verify that they are the same. This
assertion either passes or invokes undefined behavior. Which isn't
technically wrong because calling code must always ensure that
preconditions are met and assertions therefore pass anyway but it does
make the value that is added by having the assertion in the first place
marginal. Fortunately, the assertion was easy to rewrite: Just compare
the container's address itself. This is well-defined regardless of
whether the assertion will pass or fail.
This patch addresses a static analysis issue reported by Cppcheck 2.9
where several classes in the toml/datetime.hpp header explicitly default
all their special member functions, including the default constructor,
but don't provide initializers for their data members. This might or
might not have caused any observable surprising behavior but I agree
with Cppcheck on this one in that an explicitly defaulted default
constructor should be expected to initialize all data members. So let's
do that.
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.
Instead of static_cast calls that convert int to char, literals of type
char are now used directly with the value encoded via escape sequence.
The benefits are:
- code without static_cast is much more compact and expresses intent
better
- fixed value truncation warning on some compilers (e.g. C4309 on Visual
Studio 2017)
Taking this parameter by const reference forces us to copy it (because
we know we're going to store it). Taking it by r-value reference would
suggest that we _might_ take ownership over it and would also force the
user to make a copy if they wish to retain the original value.
Taking this parameter by value however clearly gives us ownership of its
content without forcing a copy if it's implicit conversion from
`const char*` or explicitly handed over to us by the user via std::move.