HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Short string class

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
classstringshort

Problem

On one of the projects I'm working on, we have many objects that store (small) strings, loaded from database. I know std::string's have small string optimization (SSO), but the overhead, in my opinion, is too big; most strings are < 16 characters, and on my platform sizeof(std::string) is 32, wasting 50% of memory. Also less data == less work.

This is not meant as a replacement for std::string, but to be used in places where one can guarantee the size of string won't exceed capacity.
Basic functionality is provided to allow interoperability (to some degree) with std::string.

How it works:

It stores remaining capacity (a very clever trick by Andrei Alexandrescu, nicely explained here). When creating/appending larger strings than the capacity, data is truncated instead of throwing an exception, for performance reasons.

```
#pragma once

#include
#include // ostream operator // memcpy

template
class ShortString {
public:
using size_type = std::size_t;
using value_type = T;

constexpr ShortString() noexcept
: _buffer{}
{
set_size(0u);
}

template
constexpr ShortString(const T(&str)[N]) noexcept
: _buffer{}
{
auto size = N >= Size ? Size - 1 : N - 1;
std::memcpy(_buffer, str, size);
set_size(size);
}

template
ShortString(const std::basic_string& str) noexcept
: _buffer{}
{
auto size = str.size() >= Size ? Size : str.size();
std::memcpy(_buffer, str.c_str(), size);
set_size(size);
}

template
constexpr ShortString(const ShortString& rhs) noexcept
: _buffer{}
{
auto size = N >= Size ? Size : N;
std::memcpy(_buffer, rhs.c_str(), size);
set_size(size);
}

template
constexpr ShortString& operator = (const T(&str)[N]) noexcept {
auto size = N >= Size ? Size - 1 : N - 1;
std::memcpy(_buffer, str, size);
set_size(size);
return *th

Solution

I feel like the implementation of the class would be simpler if backed by a std::array instead of value_type _buffer[Size];, because then you could use the standard algorithms (aka iterators) easier, and get bounds checking in debug builds (at least in MSVC).

Initializing the buffer via : _buffer{} is unnecessarily slowing you down for no reason in most of the constructors, since you proceed to fill in the buffer regardless.

compare should be named operator==, so it's clear which comparison it's doing. I'd assumed it was operator>, also weird.

You're missing a very significant amount of the std::basic_string interface. Prefer to match the existing design patterns.

Also I strongly disagree with the exception throwing vs truncation. Definitely throw exceptions. At least assert. There's no performance penalty for exceptions here on most compilers, except when it's hit.

Context

StackExchange Code Review Q#148757, answer score: 5

Revisions (0)

No revisions yet.