C++ Logo

std-proposals

Advanced search

Re: [std-proposals] [Idea] Null-terminated string view (c_str_view / c_wstr_view) – a simpler alternative/complement to zstring_view

From: Peter Bindels <dascandy_at_[hidden]>
Date: Fri, 6 Feb 2026 17:56:14 +0100
On Fri, Feb 6, 2026 at 3:19 PM Jason McKesson via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Fri, Feb 6, 2026 at 1:54 AM Jan Schultke via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
> >>
> >> I don’t think that we need a wrapper for char*. The only thing you
> would gain is member functions instead of free standing functions. This
> only comes down to taste and does not warrant an additional type.
> >>
> >> The primary reason I see for a cstring_view is to improve safety (as
> long as we are in C++ and not C). This only works if cstring_view does not
> only store a pointer, but also a size (or rather a capacity). Someone has
> allocated some memory for this string and this gives us some capacity
> (which might be less than what was allocated depending on how the view is
> constructed). If we access a character inside this view the
> compiler/runtime can do bounds checks (through contracts). This would
> already be much safer even if strlen is less than the capacity saved inside
> the view.
> >
> >
> > Agreed, this feature is pointless if it just wraps a pointer.
> >
> >>
> >> Since we assume a C-style null terminated string, iteration should
> definitely still be until the null character is hit (or maybe until
> strlen). Checking for capacity could be done in addition to this. (Or
> runtime checks through contracts could trigger with out-of-bounds access.)
> >
> >
> > That sounds questionable to me. std::string iteration also doesn't stop
> at nulls, and constantly dealing with nested nulls inhibits optimizations
> like copying strings with std::memcpy. When looking at the string from a
> "range perspective", its size should match the advertised size(). The
> null-terminated size is only relevant to C APIs.
>
> But the only reason to even have `cstring_view` at all is to engage
> with APIs that expect NUL-terminated strings. If you have an API that
> takes a sized string, we already have that: `string_view`.
>
> `cstring_view` should not be thought of as some kind of generalized
> replacement for `string_view`. You use it when you care about the size
> of the string being determined by a NUL-terminator.
>

You use cstring_view when you have an input that has a guaranteed null
terminator, where you want to use a view-like type (ie, as argument), and
where down the line somebody depends / wants a null terminator. Most
software consists of more than a small amount of code, and the code at the
side that knows it's null terminated needs this to transport the knowledge
of it being null terminated to wherever the final API call happens to be.
The cstring_view API is such that when/if your API changes to allowing
string_view, you can change the cstring_view arguments to string_view
arguments in-place without any API changes - string_view supports strictly
more than cstring_view, because it does not need to keep the guarantee, so
that will always work. Seeing as many new libraries (libfido2 was the most
recent one I found) still use null-termination for strings, odds are that
time will never come.

As indicated in the paper, the typical application is that you can convert
your whole application to use string for strings and cstring_view for
passing views. As long as you only do operations that retain the null
termination guarantee on that cstring_view, it remains a cstring_view. If
you need to truncate a view at its end you would lose the null termination
guarantee, so cstring_view's substr returns a regular string_view. And if
you introduce such an operation in a sequence where you had no copies, but
through this change now would need to make a copy to call whatever API is
at the end of the call sequence, you get an error at the point where you
try to convert the string_view to cstring_view. You then 1. know that you
need a null terminator and don't have one, and 2. can choose how to handle
this. You can make a copy and continue with the copy, you can modify the
function below to only need a string_view now (where the error would move
there, and maybe allow getting rid of cstring_view entirely), or you can
change your algorithm choice to avoid the copy.

One thing I will add to the paper is a precondition for the c_str()
function that we'll somehow mark as "this is a debug precondition, you can
check it but it's very expensive" that asserts that `pre(strlen(data()) ==
size())`. All other functions function to make it a drop-in string_view
replacement, except for that one - and that one is specifically where you
want that guarantee to hold. We can't have a production precondition that
takes O(n) for an O(1) operation though, so I don't know if there's a way
to specify this yet. And as noted before, in case you modify the buffer
after that is checked before the API it goes to finally uses it all
expectations are still broken. This is a type that adds expressiveness to
the type system in the situation where the buffer is not modified in the
mean time, as it is in nearly all cases. Think Rust borrow.

> More importantly, we cannot cache the length of the string because some
uses of cstring_view will mutate the string and change the length. One of
the motivations for zstring_view in the paper is to ensure the result of
strlen is cached avoiding the need to call that function (though the
compiler can maybe optimize some out). However since the length can change
in normal use we need to note when that length is invalidated, and then
figure out how to handle those cases.

The result can be cached and kept; as long as you use the cstring_view to
modify it you will retain either a cstring_view with all properties intact
and no O(n) operations like strlen, or you get a string_view in case it
cannot guarantee null termination.

If you modify the underlying buffer, all guarantees are lost.



> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2026-02-06 16:56:28