C++20 concept matching string literal - c++

In C++20 it is possible to write a wrapper class1 which only accepts string literals.
struct string_literal
{
template<size_t N>
consteval string_literal(char const (&s)[N]) : p(s) {}
char const* p;
};
void takes_literal(string_literal lit) {
// use lit.p here
}
Is it also possible to write a concept that only matches string literals?
1 This was the original question, but per the comments and answers here: the premise is flawed: it seems that this construct does not, in fact, accept only string literals.

A char const* which points into a string literal is virtually no different from any other char const*. The fact that a literal starts its life as an array is irrelevant, as non-literal character arrays can also be created. To my knowledge, there is exactly one difference: pointers to string literals cannot be used as non-type template parameters.
That's not particularly helpful. Even if we were restricted to compile-time code execution, you can get non-literal char const*s which also cannot be used as NTTPs (by creating a std::string, which can now be done at compile-time. You can call constexpr functions on string::c_strs pointer, but you can't use the result as an NTTP).
The best you can do is to create a user-defined literal operator that returns your string_literal object. You can make it so that only this literal operator can construct such a type (besides copying, of course). But even then, they can call your operator"" directly with a non-literal, and there is nothing you can do about it.
Instead, you should re-evaluate why you need to know if it is a string literal specifically.

Whereas const char(&)[N] isn't necessary a string literal, you might create concept to match const char(&)[N]:
template <typename T>
struct is_str_literal_impl : std::false_type{};
template <std::size_t N>
struct is_str_literal_impl<const char[N]> : std::true_type{};
template <typename T>
concept concept_str_literal = is_str_literal_impl<T>::value;
void takes_literal(concept_str_literal auto& lit) {
// use lit.p here
}
Demo

Related

Null type in standard C++ library

Is there some special null type and value in standard C++ library, e.g. defined as struct null_type_t {};?
I want to use it in different places to signify that there is no value passed.
E.g. to use in next template:
template <typename T = std::null_type_t>
struct S {};
Or in structure like next:
template <typename T>
struct Optional {
constexpr Optional() = delete;
constexpr Optional(std::null_type_t) {}
template <typename OT>
constexpr Optional(OT const & val)
: has_value(true), value(val) {}
bool const has_value = false;
T const value = T();
};
Basically if std::null_type_t type or value is passed as template or function parameter then my code will do some special handling, i.e. this null type signifies that there is no value.
Of cause I can define in my code my own struct like next:
struct NullType {};
But I did this kind of definition in code many times already. And maybe there exists special standard library type for this kind of thing, which I can use everywhere instead of my definition of null struct.
There is nullptr_t type and nullptr value, that I can use in some places. But not all, because if function or template parameter is a pointer type then sometimes I want to pass null pointer to signify real value of null pointer, not the absence of value. That's why for such pointer-typed cases would be good to have some separate type like struct null_type_t {}; which doesn't interfere with pointer or any other class or built-in type.
std::optional uses std::nullopt_t for this purpose: https://en.cppreference.com/w/cpp/utility/optional/nullopt_t
So for your own optional type, you should probably implement your own nullopt_t.

understanding user defined string literals addition for c++20

I found in user defined string literal the following:
For user-defined string literals, let str be the literal without ud-suffix:
a) If the overload set includes a string literal operator template with a non-type template parameter for which str is a well-formed template argument, then the user-defined literal expression is treated as a function call operator "" X<str>(),
That sounds a bit mysterious to me. Can some one give an example how this can be used?
The following did not work at all and I can't catch the point what the non type template parameter for MyType can be. It seems not a char* nor const char*:
template < ??? >
struct MyType
{
const char* c;
constexpr MyType( const char* in ): c{in}{}
};
template < MyType t > auto operator ""_y() { return t; }
int main()
{
"Check it"_y;
}
This is confusing wording, which was copied directly from the standard:
If [the overload set] contains a literal operator template with a non-type template parameter for which str is a well-formed template-argument
The confusing bit is the question of what "for which str is a well-formed template argument" specifically applies to. A direct reading of the passage from the standard suggests that "for which" refers to the "non-type template parameter", since that is the text directly preceding the words "for which". However, if you look at how the standard says the function will be invoked, you see this:
operator "" X<str>()
str is being passed to the operator, which the implication being that an implicit conversion will take place between str and the "non-type template parameter". That is, str is a valid "template argument" of the overloaded function, not of the template parameter of the overloaded function. And thus, the "for which" part should refer to the "literal operator template with a non-type template parameter", not the "non-type template parameter".
That having been said, to make your code work, you need to do more than to just remove the template argument from MyType.
You might have noticed a certain oddity in C++ surrounding non-type template parameters (NTTP). For example, NTTPs have always been able to be pointers to things. But you could never do this:
template<const char *literal> void foo() {}
foo<"literal">();
The standard expressly forbids a pointer NTTP from being initialized with a string literal. And C++20 does not change this.
Therefore, you can't take a pointer. You have to take what the literal actually is: an array. But you can't make your code work by taking const char (&in)[] as a parameter either. A literal is not an unsized array (since an "unsized array" is not a real object type). That array parameter must be sized appropriately to the literal.
Which means that you must deduce the size from a size template parameter.
Also, other rules flat-out forbid you from ever storing a pointer to a string literal in an NTTP (directly or indirectly). So, if you want a type that represents an entire string literal in an NTTP, that NTTP type must contain an array that is sized to that size.
So the simplest, functional string literal NTTP you could build would be:
template<size_t N>
struct string_literal
{
std::array<char, N> arr_;
constexpr string_literal(const char(&in)[N]) : arr_{}
{
std::copy(in, in + N, arr_.begin());
}
};
And thanks to CTAD, you can just use template < string_literal t > auto operator ""_y() to define your UDL.
Note that this string_literal class explicitly includes the NUL terminator as part of the array.

Comparing string-like types in template function

I want to write a functor, that compares a string-like type to another. One side of the comparison is set once at initialization and reused.
The types I want to support in any case are std::basic_string, std::basic_string_view and char*, but other like std::byte* as well as std::array and std::vector are interesting, too.
My first implementation looks like this:
template<typename StringType>
class StrcmpAlgorithm {
StringType pattern;
public:
StrcmpAlgorithm(const StringType& p) : pattern(p) {}
template<typename InputString>
bool operator()(const InputString& input)
{
return input == pattern;
}
};
However, this solution is quite limited, as the usage of the equals operator limits the types I can use and might even do the wrong thing (for example when comparing with a C string).
I'm not really sure how I should approach this. Provide multiple overloads for the call operator? Use constexpr-if and check for the type?
Essentially, having the lhs of the comparison a template parameter (StringType) and the rhs a different template parameter (InputString) leads to a combinatorical problem, even if the STL already provides some of all possible comparions. Eliminating one of those would make the whole thing much easier. But for the pattern member, I need at least the possibility to store strings with different character widths, as well as having the choice between a value and a reference type.
I believe the generally best approach is template specialization, meaning that the template class will behave differently on char*, std::array, etc.
Note, that if you have std::basic_string_view, then you can convert all of the types you mentioned to a string view, and thus use the built-in comparison function. So in your case I would implement it a bit different:
template<typename T>
std::string_view toStringView(const T& object)
{
// covers containers (std::string_view, std::array, std::vector, std::string)
return std::string_view(object.data(), object.size());
}
template<typename T>
std::string_view toStringView(const T* ptr)
{
// Pointer, in this case you would need a null-terminator string.
return std::string_view(reinterpret_cast<const char*>(ptr));
}
template<typename StringType>
class StrcmpAlgorithm {
StringType pattern;
public:
StrcmpAlgorithm(StringType p) : pattern(std::move(p)) {}
template<typename InputString>
bool operator()(const InputString& input)
{
return toStringView(input) == toStringView(pattern);
}
};
Of course, this example can and should be improved to support std::wstring_view and to fail on incompatible types (such as char**).

String literal with dependent type — impossible?

Is it possible to define a user-defined string literal conversion operator such that the type of its result depends on the value of its string input?
It is easy with user-defined integer and floating point literals because they admit literal operator templates, and the actual characters of the literal are passed to it as template arguments. Example:
template <char... s> struct silly { using type = int; };
template <char... s> struct silly<'1', s...> { using type = double; };
template <char... s>
typename silly<s...>::type operator"" _silly() { return 0; }
static_assert(std::is_same<int, decltype(4321_silly)>::value, "no luck");
static_assert(std::is_same<double, decltype(1234_silly)>::value, "no luck");
No such thing seems to exist for user-defined string literals.
Is there perhaps another way to do this, either in the current standard or planned/discussed for some future revision?
No, not possible, outside of serious macro hackery. String literals are accessed via constexpr, and return type of constexpr cannot depend on values of arguments.
The proposed <char...> operator"" for string literals ran into issues with "raw or processed" issues and how to specify it, and where dropped because hashing out those issues in time for the next standard would be hard, and/or ROI would be low. (at least from my casual reading of what happened).
I do not know if it died on the vine, or is still being worked on.
The hackery would be to pass <arr[0], arr[1], arr[2]> to a template pseduo-manually, and would not (directly) involve user defined literal syntax. It has many issues.

Better way of enforcing this template?

Currently, I have a function template like this that converts a vector into a string (just a natural string, separating the elements with a comma):
//the type T must be passable into std::to_string
template<typename T>
std::string vec_to_str(const std::vector<T> &vec);
As you can see, this is only meant for vectors whose elements can be passed into the built-in std::to_string function (such as int, double, etc.)
Is it considered a good practice to document with comments the allowed T? If not, what should I do? Is it possible to enforce this in a better way?
With static_assert and some expression SFINAE, you can have a nice compile time error message:
template<typename T>
constexpr auto allowed(int) -> decltype(std::to_string(std::declval<T>()), bool())
{
return true;
}
template<typename>
constexpr bool allowed(...)
{
return false;
}
template<typename T>
std::string vec_to_str(const std::vector<T>& vec)
{
static_assert(allowed<T>(0), "Invalid value type.");
return "";
}
struct foo {};
int main()
{
std::vector<int> v_int;
vec_to_str(v_int);
std::vector<foo> v_double;
vec_to_str(v_double); // static_assert fires here
}
Since std::to_string is a C++11 feature, I guess you might be open to a C++11 solution. In this particular case, you can use the trailing return type in a sfinae manner:
template <typename T>
auto vec_to_str(const std::vector<T>& vec) -> decltype(std::to_string(std::declval<T>()));
which would fail to substitute (and eliminate that overload) if the value-type does not work for to_string. But still, that's not necessarily the most eye-pleasing way to document and enforce the rule. There is probably a pre-C++11 version of the above Sfinae trick too, but it won't be any prettier.
In general, I would say that it's fine to simply document it in the comments (possibly with a doxygen tag, like \tparam). You could use a concept-check mechanism, in the style of Boost.Concept-Check if you want.
As a side note, for this specific case, I might recommend that you rely on the std::ostream operator << instead of the to_string function, since it is more likely that custom types (e.g., a 2D vector or something) will be equipped with an overload for outputting to a stream.
Until concepts arrive, you can use decltype with an anonymous type parameter:
template<typename T,
typename = decltype(std::to_string(std::declval<T>()))>
std::string vec_to_str(const std::vector<T> &vec);