I have attempted to make a basic version [not complete] of the std::basic_string
type in C++, and I would like to make sure that I have done everything correctly, and efficiently, as I can be prone to leaking memory in my programs.
Note: I also created an allocator
class, which works the same as the std
equivalent, but I have not added it, as I would like to focus on the implementation of the basic_string
(and char_traits
).
template < typename _Elem > struct char_traits
{
};
template <> struct char_traits<char>
{
typedef char _Elem;
static std::size_t length(const _Elem *_Str)
{
return strlen(_Str);
}
static int compare(const _Elem *_Lhs, const _Elem *_Rhs, std::size_t _Count)
{
return strncmp(_Lhs, _Rhs, _Count);
}
};
template <> struct char_traits<wchar_t>
{
typedef wchar_t _Elem;
static std::size_t length(const _Elem *_Str)
{
return wcslen(_Str);
}
static int compare(const _Elem *_Lhs, const _Elem *_Rhs, std::size_t _Count)
{
return wcsncmp(_Lhs, _Rhs, _Count);
}
};
template < typename _Elem > struct _Char_Traits
{
};
template <> struct _Char_Traits<char>
{
typedef char _Elem;
static int va_printf(_Elem *_Dest, const _Elem *_Format, va_list _Args)
{
return vsprintf(_Dest, _Format, _Args);
}
};
template <> struct _Char_Traits<wchar_t>
{
typedef wchar_t _Elem;
static int va_printf(_Elem *_Dest, const _Elem *_Format, va_list _Args)
{
return vswprintf(_Dest, _Format, _Args);
}
};
template < typename _Elem, typename _Traits = char_traits<_Elem>, typename _Alloc = allocator<_Elem> > class basic_string
{
public:
typedef basic_string<_Elem, _Traits, _Alloc> _Myt;
typedef _Elem value_type;
typedef _Traits traits_type;
typedef _Alloc allocator_type;
typedef value_type *pointer;
typedef const value_type *const_pointer;
typedef value_type *iterator;
typedef const value_type *const_iterator;
typedef value_type &reference;
typedef const value_type &const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
basic_string()
{
__data = _Alloc().allocate(1);
_Alloc().construct(&__data[0], '\0');
}
basic_string(const_pointer _Init)
{
size_type sz = _Traits::length(_Init), i = 0;
__data = _Alloc().allocate(sz + 1);
for (i = 0; i < sz; ++i)
{
_Alloc().construct(&__data[i], _Init[i]);
}
_Alloc().construct(&__data[sz], '\0');
}
basic_string(void *_Init)
{
*this = basic_string(reinterpret_cast<const_pointer>(_Init));
}
basic_string(const _Myt &_Init)
{
if (this != &_Init)
{
*this = basic_string(_Init.c_str());
}
else
{
__data = _Alloc().allocate(1);
_Alloc().construct(&__data[0], '\0');
}
}
~basic_string()
{
for (iterator i = begin(); i != end(); ++i)
{
_Alloc().destroy(i);
}
_Alloc().deallocate(__data, size());
}
_Myt &assign(const_pointer _Rhs)
{
pointer buf = _Alloc().allocate(_Traits::length(_Rhs) + 1);
std::copy(&_Rhs[0], &_Rhs[_Traits::length(_Rhs)], &buf[0]);
_Alloc().construct(&buf[_Traits::length(_Rhs)], '\0');
std::swap(__data, buf);
for (iterator i = &buf[0]; i != &buf[_Traits::length(buf)]; ++i)
{
_Alloc().destroy(i);
}
_Alloc().deallocate(buf, _Traits::length(buf));
return *this;
}
_Myt &operator=(const_pointer _Rhs)
{
return assign(_Rhs);
}
_Myt &append(const_pointer _Rhs)
{
pointer buf = _Alloc().allocate(size() + _Traits::length(_Rhs) + 1);
std::copy(begin(), end(), &buf[0]);
std::copy(&_Rhs[0], &_Rhs[_Traits::length(_Rhs)], &buf[size()]);
_Alloc().construct(&buf[size() + _Traits::length(_Rhs)], '\0');
std::swap(__data, buf);
for (iterator i = &buf[0]; i != &buf[_Traits::length(buf)]; ++i)
{
_Alloc().destroy(i);
}
_Alloc().deallocate(buf, _Traits::length(buf));
return *this;
}
_Myt &operator+=(const_pointer _Rhs)
{
return append(_Rhs);
}
reference operator[](size_type _Base)
{
return __data[_Base];
}
reference at(size_type _Base)
{
if (_Base >= 0 && _Base < size())
{
return __data[_Base];
}
else
{
throw std::out_of_range("rocket::basic_string<>::at() invalid position");
}
}
const_reference operator[](size_type _Base) const
{
return __data[_Base];
}
const_reference at(size_type _Base) const
{
if (_Base >= 0 && _Base < size())
{
return __data[_Base];
}
else
{
throw std::out_of_range("rocket::basic_string<>::at() invalid position");
}
}
iterator begin()
{
return &__data[0];
}
iterator end()
{
return &__data[size()];
}
size_type size()
{
return _Traits::length(__data);
}
_Myt &swap(basic_string<_Elem> &_Rhs)
{
std::swap(__data, _Rhs.__data);
return *this;
}
void resize(size_type _Size)
{
if (_Size < size())
{
for (iterator i = begin(); i != end(); ++i)
{
if (i == &__data[_Size])
{
while (i != end())
{
_Alloc().destroy(i++);
}
}
}
}
else if (_Size > size())
{
reserve(_Size);
}
}
void shrink_to_fit()
{
int count = 0;
for (iterator i = begin(); i != end(); ++i, ++count)
{
if (*i == '\0')
{
resize(count);
}
}
}
void reserve(size_type _Size)
{
if (_Size < size())
{
return; // maybe resize(_Size) instead
}
pointer buf = _Alloc().allocate(_Size);
std::copy(begin(), end(), &buf[0]);
for (int i = size(); i < _Size; ++i)
{
_Alloc().construct(&buf[i], '\0');
}
std::swap(__data, buf);
for (iterator i = &buf[0]; i != &buf[_Traits::length(buf)]; ++i)
{
_Alloc().destroy(i);
}
_Alloc().deallocate(buf, _Traits::length(buf));
}
static const_pointer longest(const_pointer _Lhs, const_pointer _Rhs)
{
return _Traits::length(_Lhs) > _Traits::length(_Rhs) ? _Lhs
: _Traits::length(_Rhs) > _Traits::length(_Lhs) ? _Rhs : _Lhs;
}
static const_pointer shortest(const_pointer _Lhs, const_pointer _Rhs)
{
return _Traits::length(_Lhs) < _Traits().length(_Rhs) ? _Lhs
: _Traits::length(_Rhs) < _Traits().length(_Lhs) ? _Rhs : _Lhs;
}
bool operator==(const_pointer _Rhs)
{
return _Traits::compare(__data, _Rhs, _Traits::length(longest(*this, _Rhs))) == 0;
}
bool operator!=(const_pointer _Rhs)
{
return !(*this == _Rhs);
}
_Myt &format_s(const_pointer _Format, std::initializer_list<const_pointer> _Args)
{
int nsize = 0;
for (const_iterator const *i = _Args.begin(); i != _Args.end(); ++i)
{
nsize += _Traits::length(*i) - 2;
}
reserve(nsize + 24);
for (const_iterator const *i = _Args.begin(); i != _Args.end(); ++i)
{
_Char_Traits<_Elem>::_sprintf(__data, _Format, *i);
}
shrink_to_fit();
return *this;
}
_Myt &format(const_pointer _Format, ...)
{
reserve(_Traits::length(_Format) + 124);
va_list args;
va_start(args, _Format);
_Char_Traits<_Elem>::va_printf(__data, _Format, args);
va_end(args);
shrink_to_fit();
return *this;
}
template < typename _Ty > static const_pointer to_string(_Ty _Value)
{
return static_cast<std::basic_ostringstream<_Elem> *>(&(std::basic_ostringstream<_Elem>() << _Value))->str().c_str();
}
operator const_pointer()
{
return __data;
}
const_pointer data() const
{
return __data;
}
const_pointer c_str() const
{
return __data;
}
private:
pointer __data;
};