-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Failure on nullptr char* #107
Comments
Hi and thank you for reporting this issue. Good spot! My initial thought on seeing this was that I should move the check for Perhaps it would be best to not consider |
Regarding the nullptr ambiguity, my first intuition would be that string should be serialized including quotation marks. So for example, Of course, then there will be question of displaying string containing the " character, etc. That's would just extend a problem which already exists with \n or \t characters, which are not escaped as well. Not a big issue for me. Enclosing serialized strings in quotation marks is the more clear option, in my opinion. Not treating char* as string by default at first it hit me like something users would not expect. But after giving it some thought, it does seem to make sense. In practice, pointer to type doesn't necessarily have to point to anything meaningful, it can be used for arithmetics only. For example a pointer which functions like end() iterator of containers. I've seen char* used as "byte pointer" in some lazy code. So this might be the most correct approach technically. Some users might find it hard having to implement append() themselves. It is a bit complicated for newcomers, who only want to consume the library in simplest way possible. If you go this route, there should be at least a paragraph on this issue in docs, or an example how to implement append() for char* / const char*. However, I can imagine it would be confusing why
That alone might be reason enough not to go this way. |
I think we can support the latter with Thinking about this some more, it is actually dangerous to treat all |
The short answer: it depends, so I'm not sure we can rely on that. The long answer now. With the right overloads of template<std::size_t N>
using char_array = char[N];
template<typename T>
struct is_raw_string : std::false_type {};
template<std::size_t N>
struct is_raw_string<char_array<N>> : std::true_type {}; // needed for matching to `const T&`
template<std::size_t N>
struct is_raw_string<const char_array<N>&> : std::true_type {}; // needing for matching to `T&&`
template<typename T>
concept raw_string = is_raw_string<T>::value; we can support template<raw_string T>
[[nodiscard]] constexpr bool append(small_string_span ss, const T& value) noexcept {
return append(ss, std::string_view(value));
} There is some complication however, because the minute we add the overload for pointers: template<typename T>
concept pointer = std::is_pointer_v<T>;
template<pointer T>
[[nodiscard]] constexpr bool append(small_string_span ss, T ptr) noexcept {
if (value == nullptr) {
return append(ss, nullptr);
}
if constexpr (std::is_function_v<T>) {
constexpr std::string_view function_ptr_str = "0x????????";
return append(ss, function_ptr_str);
} else {
return append(ss, static_cast<const void*>(value));
}
} ... the call to template<std::size_t N>
using char_array = char[N];
template<typename T>
struct is_raw_string : std::false_type {};
template<std::size_t N>
struct is_raw_string<char_array<N>> : std::true_type {}; // needed for matching to `const T&`
template<std::size_t N>
struct is_raw_string<const char_array<N>&> : std::true_type {}; // needing for matching to `T&&`
template<typename T>
concept raw_string = is_raw_string<T>::value;
template<typename T>
concept pointer = std::is_pointer_v<T>;
template<typename T>
struct is_function_pointer : std::false_type {};
template<typename T>
struct is_function_pointer<T*> : std::is_function<T> {};
template<typename T>
constexpr bool is_function_pointer_v = is_function_pointer<T>::value;
template<typename T>
requires(raw_string<T> || pointer<T>)
[[nodiscard]] constexpr bool append(small_string_span ss, T&& value) noexcept {
if constexpr (std::is_pointer_v<T>) {
if (value == nullptr) {
return append(ss, nullptr);
}
if constexpr (is_function_pointer_v<T>) {
constexpr std::string_view function_ptr_str = "0x????????";
return append(ss, function_ptr_str);
} else {
return append(ss, static_cast<const void*>(value));
}
} else {
return append(ss, std::string_view(value));
}
} Oh my... But now But unfortunately, C++ decay rules are annoying. We can't stop all the decays, so I'll think about this some more, but my current preference is to keep treating |
CHECK or REQUIRE macros fail on nullptr char* or const char* value.
Compilation fails because snitch is trying to convert nullptr into string_view:
It appears snitch assumes every char* points to string. Comparison of char* against nullptr is common enough, especially with legacy code / libraries.
The text was updated successfully, but these errors were encountered: