Current design doesn't guarantee anything about space occupied by static_string<N>
. User can't assume that sizeof(basic_static_string<N, CharT>) == (N+1) * sizeof(CharT)
(nor sizeof(basic_static_string<N, CharT>) == N * sizeof(CharT)
as N
doesn't take final null-terminator into account) because the class stores additional size_
field. This is a good thing for std::string
compatibility which doesn't assume that stored string can't have null characters.
On the other hand, there are still situations where null-terminated cstring-wrapper could be handy. To show the example, we can have a pseudo-POD structure with standarized layout:
struct user_pod
{
std::uint32_t id;
char name[64];
std::uint32_t something;
};
Such structures requires using non-modern C functions (like std::strncpy
) to fill it. This could be replaced with something more handy:
struct user_pod
{
std::uint32_t id;
boost::cstring<63> name;
std::uint32_t something;
};
...where boost::cstring
is a working name of a hypothetical specific class with a strong guaranty that underlying object stores only a CharT
array of size N+1
(or N
, depending on further design) and therefore that objects of that class are trivially copyable.
Implementation of such class doesn't seem to be a hard work - most of existing code could be reused, only static_string_base
should be conditional. To ilustrate possible design:
Wandbox link
#include <iostream>
#include <cstring>
namespace boost {
namespace detail {
template<std::size_t N, typename CharT, typename Traits>
class static_string_base
{
using traits_type = Traits;
using value_type = typename traits_type::char_type;
using size_type = std::size_t;
public:
std::size_t size_impl() const noexcept
{
return size_;
}
private:
size_type size_ = 0;
value_type data_[N + 1]{};
};
template<std::size_t N, typename CharT, typename Traits>
class static_nullterminated_string_base
{
using traits_type = Traits;
using value_type = typename traits_type::char_type;
public:
std::size_t size_impl() const noexcept
{
return traits_type::length(data_);
}
private:
value_type data_[N + 1]{};
};
} // namespace detail
template<std::size_t N, typename CharT, bool NullTerminated = false, typename Traits = std::char_traits<CharT>>
class basic_static_string : private std::conditional_t<NullTerminated, detail::static_nullterminated_string_base<N, CharT, Traits>, detail::static_string_base<N, CharT, Traits>>
{
// ...
};
template<std::size_t N>
using static_string = basic_static_string<N, char, false, std::char_traits<char>>;
template<std::size_t N>
using cstring = basic_static_string<N, char, true, std::char_traits<char>>;
}
int main() {
constexpr auto SIZE = 64;
static_assert(sizeof(boost::static_string<SIZE>) != SIZE + 1);
static_assert(sizeof(boost::cstring<SIZE>) == SIZE + 1);
return 0;
}
The question is: could such new functionallity be in scope of this library? Would you accept PR with such extension?