#if !defined(MOVING_STRING_HPP) #define MOVING_STRING_HPP #include #include #include #include #include namespace moving { class string; struct string_tmp_ref { string *ptr; string_tmp_ref(string *p) : ptr(p) { } }; class string_tmp { friend class string; private: string *ptr; string_tmp(string *s) throw () : ptr(s) { } string_tmp(string_tmp &from) throw () : ptr(from.ptr) { from.ptr = nullptr; } public: string_tmp(string_tmp_ref from) throw () : ptr(from.ptr) { } ~string_tmp(); operator string_tmp_ref() { string_tmp_ref result(ptr); ptr = nullptr; return result; } friend string_tmp operator+(string const &a, string const &b) throw (std::bad_alloc); friend string_tmp operator+(string_tmp a, string const &b) throw (std::bad_alloc); friend std::ostream &operator<<(std::ostream &os, string_tmp const &) throw (); }; class string { private: enum { DEFAULT_BUF_SIZE = 64 }; private: char *buf; size_t buf_size; size_t str_length; public: string(char const *str = nullptr) throw (std::bad_alloc) : buf(nullptr), buf_size(0), str_length(0) { if (str) { size_t len = strlen(str); ensure_buf_size_at_least(len + 1); memcpy(buf, str, len + 1); str_length = len; } else { ensure_buf_size_at_least(1); buf[0] = '\0'; } } string(string const &from) throw (std::bad_alloc) : buf(nullptr), buf_size(0), str_length(0) { std::cerr << "copying: " << from << std::endl; internal_assign(from); } string(string_tmp from) throw () : buf(nullptr), buf_size(0), str_length(0) { std::cerr << "moving: " << from << std::endl; internal_move(from); } ~string() throw () { if (buf) { delete [] buf; } } string &operator=(string const &from) throw (std::bad_alloc) { if (this == &from) { return *this; } internal_assign(from); return *this; } string &operator=(string_tmp from) throw () { internal_move(from); return *this; } string &operator+=(string const &with) throw (std::bad_alloc) { ensure_buf_size_at_least(str_length + with.str_length + 1); memcpy(buf + str_length, with.buf, with.str_length + 1); str_length += with.str_length; return *this; } friend string_tmp operator+(string const &a, string const &b) throw (std::bad_alloc); friend string_tmp operator+(string_tmp a, string const &b) throw (std::bad_alloc); friend std::ostream &operator<<(std::ostream &os, string const &str) throw (); char const *c_str() const throw () { return (buf) ? buf : ""; } private: void ensure_buf_size_at_least(size_t least_size) throw (std::bad_alloc) { if (buf_size >= least_size) { return; // ok, no need to resize } size_t buf_size_new = buf_size; if (buf_size_new == 0) { buf_size_new = DEFAULT_BUF_SIZE; } while (buf_size_new < least_size || buf_size_new < str_length) { buf_size_new <<= 1; } char *buf_new = new char[buf_size_new]; // may throw std::bad_alloc if (buf) { memcpy(buf_new, buf, str_length + 1); #ifdef DEFENSIVE_PROGRAMMING memset(buf_new + str_length, '\0', buf_size_new - str_length); #endif delete [] buf; } #ifdef DEFENSIVE_PROGRAMMING else { memset(buf_new, '\0', buf_size_new); } #endif buf = buf_new; buf_size = buf_size_new; } void internal_assign(string const &from) throw (std::bad_alloc) { ensure_buf_size_at_least(from.str_length + 1); memcpy(buf, from.buf, from.str_length + 1); str_length = from.str_length; } void internal_move(string_tmp with) throw () { if (buf) { delete [] buf; } buf = with.ptr->buf; buf_size = with.ptr->buf_size; str_length = with.ptr->str_length; with.ptr->buf = nullptr; with.ptr->buf_size = 0; with.ptr->str_length = 0; } }; inline string_tmp::~string_tmp() { if (ptr) { std::cerr << "destruct string_tmp=\"" << *ptr << "\"" << std::endl; delete ptr; } } inline string_tmp operator+(string const &a, string const &b) throw (std::bad_alloc) { std::cerr << "operator+(string const & a, string const &b) "; std::cerr << "[with a=\"" << a << "\" b=\"" << b << "\"]" << std::endl; string_tmp result(new string(a)); *(result.ptr) += b; return result; } inline string_tmp operator+(string_tmp a, string const &b) throw (std::bad_alloc) { std::cerr << "operator+(string_tmp a, string const &b) "; std::cerr << "[with a=\"" << a << "\" b=\"" << b << "\"]" << std::endl; *a.ptr += b; return a; } inline std::ostream &operator<<(std::ostream &os, string const &str) throw () { os << str.c_str(); return os; } inline std::ostream &operator<<(std::ostream &os, string_tmp const &str) throw () { os << *str.ptr; return os; } } // namespace moving #endif // MOVING_STRING_HPP