Date: 2020-06-29
State: Done
I've been learning some C++ (inspired by a desire to write a Gemini server) and I find the language very pleasant to use. I've used Rust in the past, and the Rust community is fairly critical on C++, so I was surprised when I found the language easy to use and pleasantly safe (I use container types and managed pointers for everything.) Occasionally I find myself having to call into a C function, and I always wrap these accesses with STL or other managed types, and almost exhaustively prefer not having to call "new" or "delete", so that I deal with unsafeness in small portions. (Similar to how Rust corrals dangerous code in "unsafe" blocks and wraps them.)
C++20 has some great goodies, but in the interests of portability I am trying to stick to C++17. C++20 has some great functions for working with time, but C++17 has only a sparse time library. To generate ISO-8601 Datetimes, I needed to reach to "strftime". Rather than allocate a raw char* buffer and use a unique_ptr to it, I decided to allocate a string, have "strftime" read into it, then return the string. I used code like this:
time_t time = chrono::system_clock::to_time_t(point); auto tm_time = gmtime(&time); // Initialize the string with a garbage character auto time_str = string('&', base_dt.size()); auto bytes = strftime(time_str.data(), time_str.size() + 1, "%FT%TZ", tm_time); if (bytes <= 0) { return nullopt; } return make_optional(time_str);
Can you spot the error?
...
...
...
It's in this line:
auto time_str = string('&', base_dt.size())
I thought this would initialize "time_str" to be a string of size "base_dt.size()" filled with the '&' character, invoking the fill constructor. The fill constructor is based on passing in a "CharT*" or another "string". Because '&' is a character and _because base_dt.size() as a size_t can be converted to a "char"_ (ugh!), instead this constructor initializes the string to a length obtained by converting '&' into a size_t, with the character that "base_dt.size()" converts into. I found that out by diving into LLDB
but that's a post for another time.
I've learned to be careful when working with C++ semantics, especially silent casts.