I'm working on some C++ code that also eventually needs to call OS-level C code, for example scandir.
I'd like to use C++ for most of the codebase, which (to me) means that I'm mostly working with std::string instead of char pointers.
I have a method that accepts a string_view, so that I can pass in both std::string and char*, depending on whether the C++ or the "C interop" code needs to call it:
std::optional<std::vector<FileInfo>> scan_directory(const std::string_view directory)
{
if (directory.empty()) {
return {};
}
struct dirent **namelist;
int num = scandir(directory.data(), &namelist, nullptr, nullptr);
[...]
}
Note the call to data()
here, since scandir takes a const char *
. Now, I saw this note:
Unlike std::basic_string::data() and string literals, data() may return a pointer to a buffer that is not null-terminated. Therefore it is typically a mistake to pass data() to a routine that takes just a const CharT* and expects a null-terminated string.
That got me thinking: Is there a better/safer way? I know that in practice, the callers will be null-terminated strings, but I don't want to create a hard-to-diagnose bug later on when I'm already aware there's a potential issue here. Though I guess that there's already no guarantee that a char* is null-terminated, so I'm not making the situation any worse.
Still, curious if there is a better option.
- Should I check the string_view for a null-terminator, and if none exists, create a
char[directory.size() + 1]{0}
and copy the characters myself? - Or create two overloads, one that takes a std::string and one that takes a const char*?
I'm on g++ (GCC) 10.2.1 20201016 (Red Hat 10.2.1-6)
in C++20 mode via CMake's set(CMAKE_CXX_STANDARD 20)
.