std 라이브러리의 클래스들을 직접 구현해보면
언어 기능에 대해 눈이 좀 더 뜨일 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
int main()
{
using string = my_string<char>;
string str[3] = {"Hello, ", "World!\n", "C++!\n"};
std::cout << str[0] + str[1];
std::cout << str[0].append(str[2]);
std::cout << str[2].insert(3, string(" Programming Language"));
std::cout << str[2].erase(1, 2);
std::cout << str[2].replace(0, 1, "Java");
assert(str[2] == string{"Java Programming Language!\n"});
assert(str[1] == string{str[1]});
}
|
cs |
구현하지 못한 내용
- std::basic_string는 character traits와 allocator까지 template argument로 받는데,
이 둘은 구현하지 않았다.
character traits와 allocator에서 제공하는 함수들은 별도의 전역 함수나 static 멤버 함수로 대체했다. - std::basic_string은 SSO(Short String Optimization)을 제공하지만, 구현하지 않았다.
- std::string_view와 호환되는 함수들, find를 제외한 find계열 함수들(find_first_of, find_last_pf 등)은 구현하지 않았다.
메모
계층적 호출 vs 직접적 호출
insert와 push_back, append, erase와 같은 함수들을 replace를 이용해 구현하고,
replace에선 디버그용 선행 조건 검사만 한 뒤 private 함수인 __mutate를 호출하도록 했다.
insert는 바로 __mutate를 호출하는 것이 좋을까, replace를 호출하는 것이 좋을까
append는 insert를 호출하는 것이 좋을까, replace를 호출하는 것이 좋을까
replace의 오버로드된 버전들은 바로 __mutate를 호출하는 것이 좋을까, 다른 오버로딩을 호출하는 것이 좋을까
이러한 고민을 많이했다.
이용할 수 있는 함수가 많은 상황에서,
계층적인 호출 구조를 만드냐 직접적인 호출 구조를 만드냐를 선택해야 했다.
계층적인 호출 구조를 만들면,
구현을 수정해야 할 때 읽어야 할 코드가 계층 전체다.
대신에 구현을 훨씬 캡슐화할 수 있다.
직접적인 호출 구조를 만들면,
구현을 수정해야 할 때 모든 내부 동작을 일관하는 private 함수 하나만 수정하면 된다.
그러나 그 private 함수의 영향력이 매우 큰 나머지 수정이 두려워질 수 있다.
경험상 거의 모든 함수와 클래스에 이용될 수 있는 마스터키를 갖추는 것은 좋은 선택이 아니었다.
전역 변수가 비슷한 성격을 가지고 있으나 지양되는 것을 고려하면,
계층적인 호출 구조를 선택하는 게 좋은 선택일 것이라 판단했다.
swap 활용
멤버 함수 swap을 구현하여 copy & swap idiom을 활용하면
많은 메모리 조작 함수들의 줄 수를 획기적으로 줄일 수 있었다.
r-value는 swap의 인자로 전달하지 못하므로,
this->swap(T(args...)) 형태 대신
T(args...).swap(*this) 형태로 코딩한다.
원하는대로 동작하고 컴파일 오류가 나지 않을 것이다.
this->
템플릿 클래스의 멤버에 접근할 때 this->를 표기하지 않으면 컴파일 오류가 나는 상황을 많이 보았다.
그래서 이번에 코딩할 때에도 this->를 막무가내로 적었는데,
this->가 필요했던 상황을 알게 되었다.
전역 함수와 이름이 겹치는 경우 (이름 탐색 규칙이 있어 오류는 안 날 테지만)
어떤 것을 호출하는지 명확히 하기 위해 this->를 적거나,
부모 템플릿 클래스의 멤버에 접근하기 위해서 this->를 적는 것이다.
(부모 템플릿 클래스의 어떤 특수화는 해당 멤버를 갖고 있지 않을 수 있기 때문에)
Coding Style
gcc의 코딩 스타일을 보고 모방하느라
attribute & constexpr, return type qualifier, return type, function name & function qualifier가
각기 별개의 줄이 되었다.
일관성있게 작성하니 가독성이 나쁘지 않아 보이고 소스의 가로 길이 제한을 지키는데 효과적이긴 하다.
하지만, 코드의 줄 수가 상당히 늘어나고 함수의 몸체보다 머리가 더 커지는 현상이 발생한다.
이러한 대두 함수들이 정말로 가독성을 효과적으로 늘리는지는 의문이다.
이런 스타일을 적용해본 것이 처음이기 때문에,
조금 더 적응해보고 나서 장단을 파악해야 할 것 같다.
코드
- my_string.h
#ifndef _my_string
#define _my_string
#include "my_exception.h"
#include <algorithm>
#include <cctype>
#include <iostream>
#define _check_i_is_in_size(i, strobj, emsg) \
debug_check_out_of_range((i), std::size_t{ 0 }, \
(strobj).size(), (emsg))
template <class CharT>
class my_string
{
public:
static constexpr std::size_t npos = -1;
// constructors *************************************
constexpr
my_string() noexcept;
my_string(std::size_t count, CharT ch);
my_string(const my_string& other, std::size_t pos,
std::size_t count = npos);
my_string(const CharT* str, std::size_t count);
my_string(const CharT* str);
my_string(const my_string& other);
my_string(my_string&& other) noexcept;
my_string(std::nullptr_t) = delete;
// ************************************* constructors
// destructor ***************************************
~my_string();
// *************************************** destructor
// assignment operators *****************************
my_string&
operator=(const my_string& other);
my_string&
operator=(my_string&& other) noexcept;
my_string&
operator=(const CharT* str);
my_string&
operator=(CharT ch);
// ***************************** assignment operators
// assign *******************************************
my_string&
assign(std::size_t count, CharT ch);
my_string&
assign(const my_string& str,
std::size_t pos = std::size_t{ 0 },
std::size_t count = npos);
my_string&
assign(my_string&& str) noexcept;
my_string&
assign(const CharT* str, std::size_t count);
my_string&
assign(const CharT* str);
// ******************************************* assign
// element access ***********************************
[[nodiscard]] constexpr
CharT&
at(std::size_t pos);
[[nodiscard]] constexpr
const CharT&
at(std::size_t pos) const;
[[nodiscard]] constexpr
CharT&
operator[](std::size_t index);
[[nodiscard]] constexpr
const CharT&
operator[](std::size_t index) const;
[[nodiscard]] constexpr
CharT&
front();
[[nodiscard]] constexpr
const CharT&
front() const;
[[nodiscard]] constexpr
CharT&
back();
[[nodiscard]] constexpr
const CharT&
back() const;
[[nodiscard]] constexpr
CharT*
data() noexcept;
[[nodiscard]] constexpr
const CharT*
data() const noexcept;
[[nodiscard]] constexpr
const CharT*
c_str() const noexcept;
// *********************************** element access
// capacity *****************************************
[[nodiscard]] constexpr
bool
empty() const noexcept;
[[nodiscard]] constexpr
std::size_t
size() const noexcept;
[[nodiscard]] constexpr
std::size_t
length() const noexcept;
[[nodiscard]] constexpr
std::size_t
capacity() const noexcept;
void
reserve(std::size_t new_cap);
void
shrink_to_fit();
// ***************************************** capacity
// operations ***************************************
void
clear() noexcept;
my_string&
insert(std::size_t index, std::size_t count,
CharT ch);
my_string&
insert(std::size_t index, const CharT* str);
my_string&
insert(std::size_t index, const CharT* str,
std::size_t count);
my_string&
insert(std::size_t index, const my_string& str);
my_string&
insert(std::size_t index, const my_string& str,
std::size_t index_str, std::size_t count = npos);
my_string&
replace(std::size_t pos, std::size_t count,
const my_string& str);
my_string&
replace(std::size_t pos, std::size_t count,
const my_string& str, std::size_t pos_str,
std::size_t count_str = npos);
my_string&
replace(std::size_t pos, std::size_t count,
const CharT* str, std::size_t count_str);
my_string&
replace(std::size_t pos, std::size_t count,
const CharT* str);
my_string&
replace(std::size_t pos, std::size_t count,
std::size_t count_ch, CharT ch);
void
push_back(CharT ch);
void
pop_back();
my_string&
append(std::size_t count, CharT ch);
my_string&
append(const my_string& str);
my_string&
append(const my_string& str, std::size_t pos,
std::size_t count = npos);
my_string&
append(const CharT* str);
my_string&
append(const CharT* str, std::size_t count);
my_string&
operator+=(const my_string& str);
my_string&
operator+=(CharT ch);
my_string&
operator+=(const CharT* str);
my_string&
erase(std::size_t index = 0, std::size_t count = npos);
[[nodiscard]] constexpr
int
compare(const my_string& str) const noexcept;
[[nodiscard]] constexpr
int
compare(std::size_t pos, std::size_t count,
const my_string& str) const;
[[nodiscard]] constexpr
int
compare(std::size_t pos1, std::size_t count1,
const my_string& str, std::size_t pos2,
std::size_t count2 = npos) const;
[[nodiscard]] constexpr
int
compare(const CharT* str) const;
[[nodiscard]] constexpr
int
compare(std::size_t pos, std::size_t count,
const CharT* str) const;
[[nodiscard]] constexpr
int
compare(std::size_t pos, std::size_t count1,
const CharT* str, std::size_t count2) const;
[[nodiscard]] constexpr
my_string
substr(std::size_t pos = 0, std::size_t count = npos)
const;
void
resize(std::size_t n);
void
resize(std::size_t n, CharT ch);
void
swap(my_string& rhs) noexcept;
// *************************************** operations
// search *******************************************
[[nodiscard]] constexpr
std::size_t
find(const my_string& str, std::size_t pos = 0)
const noexcept;
[[nodiscard]] constexpr
std::size_t
find(const CharT* str, std::size_t pos = 0) const;
[[nodiscard]] constexpr
std::size_t
find(const CharT* str, std::size_t pos,
std::size_t count) const;
[[nodiscard]] constexpr
std::size_t
find(CharT ch, std::size_t pos = 0)
const noexcept;
// ******************************************* search
private:
// fixed capacity construction **********************
my_string(std::size_t cap, const my_string& other);
// ********************** fixed capacity construction
// helper functions *********************************
std::size_t
_safe_cap() noexcept;
void
_set_sz(std::size_t n) noexcept;
void
_mutate(std::size_t pos, std::size_t len1,
const CharT* str, std::size_t len2);
void
_mutate(std::size_t pos, std::size_t len1, CharT ch,
std::size_t count);
// ********************************* helper functions
// data members *************************************
std::size_t sz;
std::size_t cap;
CharT* dat;
// ************************************* data members
};
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(const my_string<CharT>& lhs,
const my_string<CharT>& rhs);
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(const my_string<CharT>& lhs, const CharT* rhs);
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(const CharT* lhs, const my_string<CharT>& rhs);
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(const my_string<CharT>& lhs, CharT rhs);
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(CharT lhs, const my_string<CharT>& rhs);
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(my_string<CharT>&& lhs, my_string<CharT>&& rhs);
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(my_string<CharT>&& lhs,
const my_string<CharT>& rhs);
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(const my_string<CharT>& lhs,
my_string<CharT>&& rhs);
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(my_string<CharT>&& lhs, const CharT* rhs);
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(const CharT* lhs, my_string<CharT>&& rhs);
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(my_string<CharT>&& lhs, CharT rhs);
template <class CharT>
[[nodiscard]] constexpr
my_string<CharT>
operator+(CharT lhs, my_string<CharT>&& rhs);
template <class CharT>
[[nodiscard]] constexpr
bool
operator==(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept;
template <class CharT>
[[nodiscard]] constexpr
bool
operator==(const CharT* lhs, const my_string<CharT>& rhs);
template <class CharT>
[[nodiscard]] constexpr
bool
operator==(const my_string<CharT>& lhs, const CharT* rhs);
template <class CharT>
[[nodiscard]] constexpr
bool
operator!=(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept;
template <class CharT>
[[nodiscard]] constexpr
bool
operator!=(const CharT* lhs, const my_string<CharT>& rhs);
template <class CharT>
[[nodiscard]] constexpr
bool
operator!=(const my_string<CharT>& lhs, const CharT* rhs);
template <class CharT>
[[nodiscard]] constexpr
bool
operator<(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept;
template <class CharT>
[[nodiscard]] constexpr
bool
operator<(const CharT* lhs, const my_string<CharT>& rhs);
template <class CharT>
[[nodiscard]] constexpr
bool
operator<(const my_string<CharT>& lhs, const CharT* rhs);
template <class CharT>
[[nodiscard]] constexpr
bool
operator>(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept;
template <class CharT>
[[nodiscard]] constexpr
bool
operator>(const CharT* lhs, const my_string<CharT>& rhs);
template <class CharT>
[[nodiscard]] constexpr
bool
operator>(const my_string<CharT>& lhs, const CharT* rhs);
template <class CharT>
[[nodiscard]] constexpr
bool
operator<=(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept;
template <class CharT>
[[nodiscard]] constexpr
bool
operator<=(const CharT* lhs, const my_string<CharT>& rhs);
template <class CharT>
[[nodiscard]] constexpr
bool
operator<=(const my_string<CharT>& lhs, const CharT* rhs);
template <class CharT>
[[nodiscard]] constexpr
bool
operator>=(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept;
template <class CharT>
[[nodiscard]] constexpr
bool
operator>=(const CharT* lhs, const my_string<CharT>& rhs);
template <class CharT>
[[nodiscard]] constexpr
bool operator>=(const my_string<CharT>& lhs, const CharT* rhs);
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os,
const my_string<CharT>& str);
template <class CharT>
[[nodiscard]] constexpr
std::size_t
_strlen(const CharT* str);
template <class CharT>
[[nodiscard]] constexpr
int
_strcmp(const CharT* lhs, const CharT* rhs);
template <class CharT>
[[nodiscard]] constexpr
int
_strncmp(const CharT* lhs, const CharT* rhs, std::size_t len);
template <class CharT>
[[nodiscard]]
my_string<CharT>
__str_concat(const CharT* lhs, std::size_t len1, const CharT* rhs, std::size_t len2);
template <class T, class ... Args>
[[nodiscard]]
T*
_construct(std::size_t num_of_obj, Args&& ... args);
template <class T>
void
_destroy(T* ptr);
[[nodiscard]] constexpr
std::size_t
closest_bin(std::size_t n) noexcept;
#include "my_string.inl"
#endif
|
cs |
- my_string.inl
#include "my_string.h"
template <class CharT>
constexpr
my_string<CharT>::
my_string() noexcept
: sz{ std::size_t{ 0 } }, cap{ std::size_t{ 0 } },
dat{ nullptr }
{
}
template <class CharT>
my_string<CharT>::
my_string(std::size_t count, CharT ch)
: sz{ count }, cap{ closest_bin(sz) },
dat{ _construct<CharT>(_safe_cap(), CharT{}) }
{
std::fill_n(this->data(), this->size(), ch);
}
template <class CharT>
my_string<CharT>::
my_string(const my_string& other, std::size_t pos,
std::size_t count)
: sz{ std::min(count, other.size() -
_check_i_is_in_size(pos, other,
"pos > other.size()")) },
cap{ closest_bin(sz) },
dat{ _construct<CharT>(_safe_cap(), CharT{}) }
{
std::copy(other.data() + pos,
other.data() + pos + this->size(),
this->data());
}
template <class CharT>
my_string<CharT>::
my_string(const CharT* str, std::size_t count)
: sz{ count }, cap{ closest_bin(sz) },
dat{ _construct<CharT>(_safe_cap(), CharT{}) }
{
std::copy(str, str + this->size(), this->data());
}
template <class CharT>
my_string<CharT>::
my_string(const CharT* str)
: my_string(str, _strlen(str))
{
}
template <class CharT>
my_string<CharT>::
my_string(const my_string& other)
: my_string(other, std::size_t{ 0 }, npos)
{
}
template <class CharT>
my_string<CharT>::my_string(my_string&& other) noexcept
: my_string()
{
this->swap(other);
}
template <class CharT>
my_string<CharT>::~my_string()
{
_destroy(data());
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
operator=(const my_string& other)
{
return this->assign(other);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
operator=(my_string&& other) noexcept
{
return this->assign(std::move(other));
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
operator=(const CharT* str)
{
return this->assign(str);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
operator=(CharT ch)
{
return this->assign(&ch, 1);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
assign(std::size_t count, CharT ch)
{
return this->replace(std::size_t{ 0 }, this->size(),
count, ch);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
assign(const my_string& str, std::size_t pos,
std::size_t count)
{
return this->replace(std::size_t{ 0 }, this->size(), str,
_check_i_is_in_size(pos, str,
"pos > str.size()"),
count);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
assign(my_string&& str) noexcept
{
this->swap(str);
return *this;
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
assign(const CharT* str, std::size_t count)
{
return this->replace(std::size_t{ 0 }, this->size(),
str, count);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
assign(const CharT* str)
{
return this->assign(str, _strlen(str));
}
template <class CharT>
constexpr
CharT&
my_string<CharT>::
at(std::size_t pos)
{
return const_cast<CharT&>(
static_cast<const my_string&>(*this).at(pos));
}
template <class CharT>
constexpr
const CharT&
my_string<CharT>::
at(std::size_t pos) const
{
return (*this)[
debug_check_out_of_range(pos, std::size_t{ 0 },
this->size() - 1,
"pos >= size()")];
}
template <class CharT>
constexpr
CharT&
my_string<CharT>::
operator[](std::size_t index)
{
// The behavior is undefined if index > size() : cppreference
return const_cast<CharT&>(
static_cast<const my_string&>(*this)[index]);
}
template <class CharT>
constexpr
const CharT&
my_string<CharT>::
operator[](std::size_t index) const
{
return this->data()[index];
}
template <class CharT>
constexpr
CharT&
my_string<CharT>::
front()
{
return const_cast<CharT&>(
static_cast<const my_string&>(*this).front());
}
template <class CharT>
constexpr
const CharT&
my_string<CharT>::
front() const
{
return (*this)[std::size_t{ 0 }];
}
template <class CharT>
constexpr
CharT&
my_string<CharT>::
back()
{
return const_cast<CharT&>(
static_cast<const my_string&>(*this).back());
}
template <class CharT>
constexpr
const CharT&
my_string<CharT>::
back() const
{
// The behavior is undefined if empty() == true.
// : cppreference
return (*this)[this->size() - 1];
}
template <class CharT>
constexpr
CharT*
my_string<CharT>::
data() noexcept
{
return const_cast<CharT*>(
static_cast<const my_string&>(*this).data() );
}
template <class CharT>
constexpr
const CharT*
my_string<CharT>::
data() const noexcept
{
return this->dat;
}
template <class CharT>
constexpr
const CharT*
my_string<CharT>::
c_str() const noexcept
{
return this->data();
}
template <class CharT>
constexpr
bool
my_string<CharT>::
empty() const noexcept
{
return this->size() == std::size_t{ 0 };
}
template <class CharT>
constexpr
std::size_t
my_string<CharT>::
size() const noexcept
{
return this->sz;
}
template <class CharT>
constexpr
std::size_t
my_string<CharT>::
length() const noexcept
{
return this->size();
}
template <class CharT>
constexpr
std::size_t
my_string<CharT>::
capacity() const noexcept
{
return this->cap;
}
template <class CharT>
void
my_string<CharT>::
reserve(std::size_t new_cap)
{
if (this->capacity() < new_cap)
my_string{ new_cap, *this }.swap(*this);
}
template <class CharT>
void
my_string<CharT>::
shrink_to_fit()
{
my_string{ this->size(), *this }.swap(*this);
}
template <class CharT>
void
my_string<CharT>::
clear() noexcept
{
this->_set_sz(std::size_t{ 0 });
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
insert(std::size_t index, std::size_t count, CharT ch)
{
return this->replace(_check_i_is_in_size(index, *this,
"index > size()"),
std::size_t{ 0 }, ch, count);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
insert(std::size_t index, const CharT* str)
{
return this->insert(index, str, _strlen(str));
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
insert(std::size_t index, const CharT* str, std::size_t count)
{
return this->replace(_check_i_is_in_size(index, *this,
"index > size()"),
std::size_t{ 0 }, str, count);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
insert(std::size_t index, const my_string& str)
{
return this->insert(index, str, std::size_t{ 0 });
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
insert(std::size_t index, const my_string& str,
std::size_t index_str, std::size_t count)
{
return this->insert(_check_i_is_in_size(index, str,
"index > str.size()"),
str.data() + index_str,
std::min(count, str.size() - index_str));
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
replace(std::size_t pos, std::size_t count, const my_string& str)
{
return this->replace(pos, count, str, std::size_t{ 0 });
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
replace(std::size_t pos, std::size_t count, const my_string& str,
std::size_t pos_str, std::size_t count_str)
{
// if count_str == npos or if would extend past str.size(),
// [pos_str, str.size()) is used to replace.
return this->replace(pos, count, str.data() + pos_str,
std::min(count_str, str.size() -
_check_i_is_in_size(pos_str,
str,
"pos_str > str.size()")));
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
replace(std::size_t pos, std::size_t count, const CharT* str,
std::size_t count_str)
{
this->_mutate(_check_i_is_in_size(pos, *this,
"pos > size()"),
count, str, count_str);
return *this;
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
replace(std::size_t pos, std::size_t count, const CharT* str)
{
return this->replace(pos, count, str, _strlen(str));
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
replace(std::size_t pos, std::size_t count,
std::size_t count_ch, CharT ch)
{
this->_mutate(_check_i_is_in_size(pos, *this, "pos > size()"),
count, ch, count_ch);
return *this;
}
template <class CharT>
void
my_string<CharT>::
push_back(CharT ch)
{
this->_mutate(this->size(), std::size_t{ 0 },
ch, std::size_t{ 1 });
}
template <class CharT>
void
my_string<CharT>::
pop_back()
{
this->_mutate(this->size() - 1, std::size_t{ 1 },
nullptr, std::size_t{ 0 });
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
append(std::size_t count, CharT ch)
{
return this->insert(this->size(), count, ch);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
append(const my_string& str)
{
return this->append(str, std::size_t{ 0 });
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
append(const my_string& str, std::size_t pos,
std::size_t count)
{
return this->append(str.data() +
_check_i_is_in_size(pos, str,
"pos > str.size()"),
std::min(count, str.size() - pos));
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
append(const CharT* str)
{
return this->append(str, _strlen(str));
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
append(const CharT* str, std::size_t count)
{
return this->insert(this->size(), str, count);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
operator+=(const my_string& str)
{
return this->append(str);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
operator+=(CharT ch)
{
return this->push_back(ch);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
operator+=(const CharT* str)
{
return this->append(str);
}
template <class CharT>
my_string<CharT>&
my_string<CharT>::
erase(std::size_t index, std::size_t count)
{
this->_mutate(_check_i_is_in_size(index, *this,
"index > size()"),
std::min(count, this->size() - index),
nullptr, std::size_t{ 0 });
return *this;
}
template <class CharT>
constexpr
int
my_string<CharT>::
compare(const my_string& str) const noexcept
{
return this->compare(std::size_t{ 0 }, this->size(), str);
}
template <class CharT>
constexpr
int
my_string<CharT>::
compare(std::size_t pos, std::size_t count,
const my_string& str) const
{
return this->compare(pos, count, str, std::size_t{ 0 });
}
template <class CharT>
constexpr
int
my_string<CharT>::
compare(std::size_t pos1, std::size_t count1,
const my_string& str, std::size_t pos2,
std::size_t count2) const
{
return this->compare(_check_i_is_in_size(pos1, *this,
"pos1 > size()"),
count1, str.data() +
_check_i_is_in_size(pos2, str,
"pos2 > str.size()"),
count2);
}
template <class CharT>
constexpr
int
my_string<CharT>::
compare(const CharT* str) const
{
return this->compare(std::size_t{ 0 }, this->size(),
str);
}
template <class CharT>
constexpr
int
my_string<CharT>::
compare(std::size_t pos, std::size_t count,
const CharT* str) const
{
return this->compare(std::size_t{ 0 }, count, str,
_strlen(str));
}
template <class CharT>
constexpr
int
my_string<CharT>::
compare(std::size_t pos, std::size_t count1,
const CharT* str, std::size_t count2) const
{
auto my_len = std::min(count1, this->size() -
_check_i_is_in_size(pos, *this,
"pos > size()"));
// Firstly, compare characters in common range
int ret = _strncmp(this->data() + pos, str,
std::min(my_len, count2));
if (!ret && my_len)
// Secondly, compare lengths
ret = my_len - count2;
return ret;
}
template <class CharT>
constexpr
my_string<CharT>
my_string<CharT>::
substr(std::size_t pos, std::size_t count) const
{
return my_string{*this,
_check_i_is_in_size(pos, *this, "pos > size()"),
count};
}
template <class CharT>
void
my_string<CharT>::
resize(std::size_t n)
{
if (this->capacity() < n) this->reserve(n);
_set_sz(n);
}
template <class CharT>
void
my_string<CharT>::
resize(std::size_t n, CharT ch)
{
if (this->capacity() < n)
{
auto old_size = this->size();
this->reserve(n);
std::fill_n(this->data() + old_size,
this->size() - old_size, ch);
}
_set_sz(n);
}
template <class CharT>
void
my_string<CharT>::
swap(my_string& rhs) noexcept
{
std::swap(this->sz, rhs.sz);
std::swap(this->cap, rhs.cap);
std::swap(this->dat, rhs.dat);
}
template <class CharT>
constexpr
std::size_t
my_string<CharT>::
find(const my_string& str, std::size_t pos) const noexcept
{
return this->find(str.data(), pos, str.size());
}
template <class CharT>
constexpr
std::size_t
my_string<CharT>::
find(const CharT* str, std::size_t pos) const
{
return this->find(str, pos, _strlen(str));
}
template <class CharT>
constexpr
std::size_t
my_string<CharT>::
find(const CharT* str, std::size_t pos,
std::size_t count) const
{
std::size_t ret =
std::search(this->data() + std::min(this->size(), pos),
this->data() + this->size(), str, str + count)
- this->data();
if (ret == this->size()) ret = npos;
return ret;
}
template <class CharT>
constexpr
std::size_t
my_string<CharT>::
find(CharT ch, std::size_t pos) const noexcept
{
std::size_t ret =
std::find(this->data() + std::min(this->size(), pos),
this->data() + this->size(), ch)
- this->data();
if (ret == this->size()) ret = npos;
return ret;
}
template <class CharT>
my_string<CharT>::
my_string(std::size_t required_cap, const my_string& other)
: sz{ std::min(other.sz, required_cap) },
cap{ required_cap }, dat{ _construct<CharT>(_safe_cap(), CharT{}) }
{
std::copy(other.data(), other.data() + this->size(),
this->data());
}
template <class CharT>
std::size_t
my_string<CharT>::
_safe_cap() noexcept
{
return capacity() + 1;
}
template <class CharT>
void
my_string<CharT>::
_set_sz(std::size_t n) noexcept
{
if (data())
{
this->sz = n;
(*this)[n] = CharT{};
}
}
/**
* data in range [this->data() + pos, this->data() + pos + len1]
* is replaced with [str, str + len2].
* exception unchecked.
*/
template <class CharT>
void
my_string<CharT>::
_mutate(std::size_t pos, std::size_t len1,
const CharT* str, std::size_t len2)
{
const std::size_t how_much = this->size() - pos - len1;
const std::size_t new_sz = this->size() + len2 - len1;
if (new_sz > this->capacity())
{
my_string tmp{ closest_bin(new_sz), *this };
if (pos)
std::copy(this->data(), this->data() + pos,
tmp.data());
if (str && len2)
std::copy(str, str + len2, tmp.data() + pos);
if (how_much)
std::copy(this->data() + pos + len1,
this->data() + this->size(),
tmp.data() + pos + len2);
this->swap(tmp);
}
else
{
if (how_much)
std::move(this->data() + pos + len1,
this->data() + this->size(),
this->data() + pos + len2);
if (str && len2)
std::copy(str, str + len2, this->data() + pos);
}
this->_set_sz(new_sz);
}
/**
* data in range [this->data() + pos, this->data() + pos + len]
* is replaced with ch of count.
* exception unchecked.
*/
template <class CharT>
void
my_string<CharT>::
_mutate(std::size_t pos, std::size_t len, CharT ch,
std::size_t count)
{
const std::size_t how_much = this->size() - pos - len;
const std::size_t new_sz = this->size() + count - len;
if (new_sz > this->capacity())
{
my_string tmp{ count, ch };
if (pos)
std::copy(this->data(), this->data() + pos,
tmp.data());
if (count)
std::fill_n(tmp.data() + pos, count, ch);
if (how_much)
std::copy(this->data() + pos + len,
this->data() + this->size(),
tmp.data() + pos + count);
this->swap(tmp);
}
else
{
if (count)
std::fill_n(this->data() + pos, count, ch);
if (how_much)
std::move(this->data() + pos + len,
this->data() + this->size(),
this->data() + pos + count);
}
this->_set_sz(new_sz);
}
template <class CharT>
constexpr
my_string<CharT>
operator+(const my_string<CharT>& lhs,
const my_string<CharT>& rhs)
{
return __str_concat(lhs.data(), lhs.size(), rhs.data(), rhs.size());
}
template <class CharT>
constexpr
my_string<CharT>
operator+(const my_string<CharT>& lhs, const CharT* rhs)
{
return __str_concat(lhs.data(), lhs.size(), rhs, _strlen(rhs));
}
template <class CharT>
constexpr
my_string<CharT>
operator+(const CharT* lhs, const my_string<CharT>& rhs)
{
return __str_concat(lhs, _strlen(lhs), rhs.data(), rhs.size());
}
template <class CharT>
constexpr
my_string<CharT>
operator+(const my_string<CharT>& lhs, CharT rhs)
{
return __str_concat(lhs.data(), lhs.size(), &rhs, std::size_t{ 1 });
}
template <class CharT>
constexpr
my_string<CharT>
operator+(CharT lhs, const my_string<CharT>& rhs)
{
return __str_concat(&lhs, std::size_t{ 1 }, rhs.data(), rhs.size());
}
template <class CharT>
constexpr
my_string<CharT>
operator+(my_string<CharT>&& lhs, my_string<CharT>&& rhs)
{
return std::move(lhs.append(rhs));
}
template <class CharT>
constexpr
my_string<CharT>
operator+(my_string<CharT>&& lhs, const my_string<CharT>& rhs)
{
return std::move(lhs.append(rhs));
}
template <class CharT>
constexpr
my_string<CharT>
operator+(const my_string<CharT>& lhs, my_string<CharT>&& rhs)
{
return std::move(rhs.insert(std::size_t{ 0 }, lhs));
}
template <class CharT>
constexpr
my_string<CharT>
operator+(my_string<CharT>&& lhs, const CharT* rhs)
{
return std::move(lhs.append(rhs));
}
template <class CharT>
constexpr
my_string<CharT>
operator+(const CharT* lhs, my_string<CharT>&& rhs)
{
return std::move(rhs.insert(std::size_t{ 0 }, lhs));
}
template <class CharT>
constexpr
my_string<CharT>
operator+(my_string<CharT>&& lhs, CharT rhs)
{
return std::move(lhs.append(std::size_t{ 1 }, rhs));
}
template <class CharT>
constexpr
my_string<CharT>
operator+(CharT lhs, my_string<CharT>&& rhs)
{
return std::move(rhs.insert(std::size_t{ 0 }, std::size_t{ 1 }, lhs));
}
template <class CharT>
constexpr
bool
operator==(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept
{
return lhs.size() == rhs.size()
&& !_strncmp(lhs.data(), rhs.data(), lhs.size());
}
template <class CharT>
constexpr
bool
operator==(const CharT* lhs, const my_string<CharT>& rhs)
{
return _strcmp(lhs, rhs.data());
}
template <class CharT>
constexpr
bool
operator==(const my_string<CharT>& lhs, const CharT* rhs)
{
return rhs == lhs;
}
template <class CharT>
constexpr
bool
operator!=(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept
{
return !(lhs == rhs);
}
template <class CharT>
constexpr
bool
operator!=(const CharT* lhs, const my_string<CharT>& rhs)
{
return !(lhs == rhs);
}
template <class CharT>
constexpr
bool
operator!=(const my_string<CharT>& lhs, const CharT* rhs)
{
return !(lhs == rhs);
}
template <class CharT>
constexpr
bool
operator<(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept
{
return lhs.compare(rhs) < 0;
}
template <class CharT>
constexpr
bool
operator<(const CharT* lhs, const my_string<CharT>& rhs)
{
return rhs.compare(lhs) > 0;
}
template <class CharT>
constexpr
bool
operator<(const my_string<CharT>& lhs, const CharT* rhs)
{
return lhs.compare(rhs) < 0;
}
template <class CharT>
constexpr
bool
operator>(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept
{
return rhs < lhs;
}
template <class CharT>
constexpr
bool
operator>(const CharT* lhs,
const my_string<CharT>& rhs)
{
return rhs < lhs;
}
template <class CharT>
constexpr
bool
operator>(const my_string<CharT>& lhs, const CharT* rhs)
{
return rhs < lhs;
}
template <class CharT>
constexpr
bool
operator<=(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept
{
return !(rhs < lhs);
}
template <class CharT>
constexpr
bool
operator<=(const CharT* lhs, const my_string<CharT>& rhs)
{
return !(rhs < lhs);
}
template <class CharT>
constexpr
bool
operator<=(const my_string<CharT>& lhs, const CharT* rhs)
{
return !(rhs < lhs);
}
template <class CharT>
constexpr
bool
operator>=(const my_string<CharT>& lhs,
const my_string<CharT>& rhs) noexcept
{
return !(lhs < rhs);
}
template <class CharT>
constexpr
bool
operator>=(const CharT* lhs, const my_string<CharT>& rhs)
{
return !(lhs < rhs);
}
template <class CharT>
constexpr
bool
operator>=(const my_string<CharT>& lhs, const CharT* rhs)
{
return !(lhs < rhs);
}
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os,
const my_string<CharT>& str)
{
return os.write(reinterpret_cast<const char*>(str.data()), str.size());
}
template<class CharT, class Traits>
std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& is,
my_string<CharT>& str)
{
static constexpr std::size_t str_max_size = 1000000000;
using istream_type = std::basic_istream<CharT, Traits>;
using string_type = my_string<CharT>;
using ios_base = typename istream_type::ios_base;
using int_type = typename istream_type::int_type;
using ctype = std::ctype<CharT>;
using ctype_base = typename ctype::ctype_base;
std::size_t extracted = 0;
typename ios_base::iostate err = ios_base::goodbit;
typename istream_type::sentry cerb(is, false);
if (cerb)
{
try
{
// Avoid reallocation for common case.
str.clear();
CharT buf[128];
std::size_t len = 0;
const std::streamsize width = is.width();
const std::size_t n = width > 0 ? width : str_max_size;
const ctype& ct = std::use_facet<ctype>(is.getloc());
const int_type eof = Traits::eof();
int_type c = is.rdbuf()->sgetc();
while (extracted < n
&& !Traits::eq_int_type(c, eof)
&& !ct.is(ctype_base::space,
Traits::to_char_type(c)))
{
if (len == sizeof(buf) / sizeof(CharT))
{
str.append(buf, sizeof(buf) / sizeof(CharT));
len = 0;
}
buf[len++] = Traits::to_char_type(c);
++extracted;
c = is.rdbuf()->snextc();
}
str.append(buf, len);
if (extracted < n && Traits::eq_int_type(c, eof))
err |= ios_base::eofbit;
is.width(0);
}
catch(...)
{
is.setstate(ios_base::badbit);
}
}
if (!extracted)
err |= ios_base::failbit;
if (err)
is.setstate(err);
return is;
}
template <class CharT>
constexpr
std::size_t
_strlen(const CharT* str)
{
std::size_t ret{ 0 };
for (; *str != CharT{} ; ++ret, ++str)
;
return ret;
}
template <class CharT>
constexpr
int
_strcmp(const CharT* lhs, const CharT* rhs)
{
for(; *lhs && *lhs == *rhs; ++lhs, ++rhs)
;
return *reinterpret_cast<const unsigned*>(lhs)
- *reinterpret_cast<const unsigned*>(rhs);
}
template <class CharT>
constexpr
int
_strncmp(const CharT* lhs, const CharT* rhs, std::size_t n)
{
for(; n && *lhs && *lhs == *rhs; --n, ++lhs, ++rhs)
;
return *reinterpret_cast<const unsigned*>(lhs)
- *reinterpret_cast<const unsigned*>(rhs);
}
template <class CharT>
my_string<CharT>
__str_concat(const CharT* lhs, std::size_t len1,
const CharT* rhs, std::size_t len2)
{
my_string<CharT> ret;
ret.reserve(len1 + len2);
ret.append(lhs, len1);
ret.append(rhs, len2);
return ret;
}
template <class T, class ... Args>
T*
_construct(std::size_t num_of_obj, Args&& ... args)
{
return new T[num_of_obj]{ std::forward<Args>(args)... };
}
template <class T>
void
_destroy(T* ptr)
{
delete[] ptr;
}
constexpr
std::size_t
closest_bin(std::size_t n) noexcept
{
std::size_t ret{ n };
if (n >= 2u)
{
--n;
ret = 2u;
while(n >>= 1u)
ret <<= 1u;
}
return ret;
}
|
cs |
- my_exception.h
// if this header is included by a source,
// undef_macros.inl also should be included in the end of the source.
/**
* @brief checks if a value is in a specific range when _DEBUG macro is defined
* @param val the value which will be checked
* @param lo lower limit of the range
* @param hi higher limit of the range
* @param expr expression which will be forwarded to std::out_of_range
*
* if val is not in [lo, hi] then throws std::out_of_range with expr
*/
#ifndef debug_check_out_of_range
#include "my_format.h"
#include <algorithm>
#include <type_traits>
#include <stdexcept>
#define _fw(val) std::forward<std::remove_reference_t<decltype(val)>>(val)
#ifdef _DEBUG
#define debug_check_out_of_range(val, lo, hi, expr) \
__check_out_of_range(val, lo, hi, __FILE__, __LINE__, __FUNCTION_NAME__, expr)
#else
#define debug_check_out_of_range(val, ...) (val)
#endif
template <class T>
const T&
__check_out_of_range(const T& val, const T& lo, const T& hi,
const char* file, decltype(__LINE__) line,
const char* func, const char* expr)
{
if (std::clamp(val, lo, hi) != val)
throw std::out_of_range{_FMT("in %s:%d:%s:\n%s\n", file, line, func, expr)};
return val;
}
#endif
|
cs |
- my_format.h
#ifndef __FUNCTION_NAME__
#ifdef __GNUC__ // if compiled by gcc/g++ use __PRETTY_FUNCTION__ for function name
#define __FUNCTION_NAME__ __PRETTY_FUNCTION__
#elif _MSC_VER // elif compiled by msvc use __FUNCSIG__ for function name
#define __FUNCTION_NAME__ __FUNCSIG__
#else // else use __FUNCTION__(standard) for function name
#define __FUNCTION_NAME__ __FUNCTION__
#endif
#endif
// _FMT changes a formatted expression into a string(char*)
#ifndef _FMT
// reference source: https://stackoverflow.com/questions/40946062/c-format-string-macro
#include <vector>
#ifdef _MSC_VER
#define MINIGINE_FORMAT_PRINTF _snprintf
#else
#define MINIGINE_FORMAT_PRINTF snprintf
#endif
#define _FMT(format, ...) \
([&]() \
{ \
std::size_t size = MINIGINE_FORMAT_PRINTF(nullptr, 0, format, __VA_ARGS__) + 1; \
std::vector<char> buf( size );\
MINIGINE_FORMAT_PRINTF(buf.data(), size, format, __VA_ARGS__); \
return buf; \
}().data())
#endif
|
cs |
참고 자료
Scott Meyers, 『Effective C++』, 곽용재 옮김, (3판, 프로텍미디어, 2015)
Scott Meyers, 『Effective Modern C++』, 류광 옮김, (인사이트, 2015)
libc++’s implementation of std::string
I. Introduction
joellaity.com
GitHub - gcc-mirror/gcc
Contribute to gcc-mirror/gcc development by creating an account on GitHub.
github.com
std::basic_string - cppreference.com
(1) (2) (since C++17) The class template basic_string stores and manipulates sequences of character-like objects, which are non-array objects of trivial standard-layout type. The class is dependent neither on the character type nor on the nature of operati
en.cppreference.com
'Short Coding' 카테고리의 다른 글
C++ Smart Pointer Type Traits 구현하기 (0) | 2021.12.17 |
---|