Copy and move constructor example, rule of 5: https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
Output example:
>test.exe
1 ''
2 'var'
3 'var2'
4 'var23'
5 'var24'
6 'var235'
7 'var246'
8 'var247'
9 'var2478'
A 'var24789'
B 'var24789A'
C 'barB'
D 'var235C'
E 'var235CD'
Exiting...
test.cpp
#include <mutex>
#include <iostream>
#include <sstream>
#include <string.h>
std::recursive_mutex g_sync_mutex;
#define sync(x) do { \
std::unique_lock<std::recursive_mutex> lock(g_sync_mutex); \
x; \
} while (false);
class BufferOverflow : public std::runtime_error
{
public:
BufferOverflow(const char* error)
: std::runtime_error(error)
{
}
};
template<typename Type, size_t Size>
struct FlexibleArray
{
Type* _buffer;
size_t _size;
size_t _nullterminator;
FlexibleArray()
: _size(Size),
_nullterminator(0)
{
// sync(std::cerr << "constructor FlexibleArray() " << _size << std::endl);
static_assert(Size > 0, "Invalid template parameter: The array size must be greater than 0!");
_buffer = new Type[Size]();
}
template<size_t OtherSize>
FlexibleArray(const Type (&buffer)[OtherSize])
: FlexibleArray()
{
_copy(buffer, OtherSize, OtherSize - 1);
}
template<typename First, typename ...Args>
FlexibleArray(const char* format, const First first, const Args... args)
: FlexibleArray()
{
this->snprintf(format, first, args...);
}
template<typename FormatType>
FlexibleArray& operator<<(const FormatType& format) {
std::ostringstream stream;
stream << format;
this->snprintf(stream.str().c_str());
return *this;
}
virtual ~FlexibleArray() {
// sync(std::cerr << "destructor ~FlexibleArray() " << _size << " '" << (_buffer ? _buffer : "") << "'" << std::endl);
delete[] _buffer;
}
void _copy(const Type* buffer, const size_t othersize, const size_t nullterminator) {
// sync(std::cerr << "copy constructor coping " << nullterminator << " '" << buffer << "'" << std::endl);
if ( nullterminator >= _size ) {
throw BufferOverflow("The buffer is not big enough to copy the string!");
}
_nullterminator = nullterminator;
memcpy(_buffer, buffer, sizeof(Type) * (_size < othersize ? _size : othersize) );
}
FlexibleArray(const FlexibleArray& other)
: FlexibleArray()
{
// sync(std::cerr << "copy size constructor " << _size << std::endl);
_copy(other._buffer, _size, other._nullterminator);
}
FlexibleArray& operator=(const FlexibleArray& other) {
// sync(std::cerr << "copy size assignment " << _size << std::endl);
if (this != &other) {
_copy(other, _size, other._nullterminator);
}
return *this;
}
template<size_t OtherSize>
FlexibleArray(const FlexibleArray<Type, OtherSize>& other)
: FlexibleArray()
{
// sync(std::cerr << "copy othersize constructor " << OtherSize << "->" << _size << std::endl);
_copy(other._buffer, OtherSize, other._nullterminator);
}
template<size_t OtherSize>
FlexibleArray& operator=(const FlexibleArray<Type, OtherSize>& other) {
// sync(std::cerr << "copy othersize assignment " << OtherSize << "->" << _size << std::endl);
if (this != reinterpret_cast<const FlexibleArray<Type, Size>*>(&other)) {
_copy(other, OtherSize, other._nullterminator);
}
return *this;
}
template<size_t OtherSize>
FlexibleArray(FlexibleArray<Type, OtherSize> && other) noexcept {
// sync(std::cerr << "move constructor " << OtherSize << "->" << _size << " '" << other._buffer << "'" << std::endl);
_size = other._size;
_nullterminator = other._nullterminator;
_buffer = other._buffer;
other._size = 0;
other._nullterminator = 0;
other._buffer = nullptr;
}
template<size_t OtherSize>
FlexibleArray& operator=(FlexibleArray<Type, OtherSize>&& other) noexcept {
// sync(std::cerr << "move assignment " << OtherSize << "->" << _size << " '" << other._buffer << "'" << std::endl);
if (this != reinterpret_cast<FlexibleArray<Type, Size>*>(&other)) {
this->~FlexibleArray();
_size = other._size;
_nullterminator = other._nullterminator;
_buffer = other._buffer;
other._size = 0;
other._nullterminator = 0;
other._buffer = nullptr;
}
return *this;
}
void clear() {
_nullterminator = 0;
_buffer[0] = Type();
}
size_t size() const {
return _size;
}
size_t lastindex() const {
return _nullterminator;
}
size_t hasspace(size_t space = 1) const {
return _nullterminator + space < _size;
}
Type lastvalue() const {
if (_nullterminator > 0) {
return _buffer[_nullterminator - 1];
}
return _buffer[_nullterminator];
}
unsigned char* bytes() const {
return reinterpret_cast<unsigned char*>(_buffer);
}
Type* buffer() const {
return _buffer;
}
operator Type*() const {
return _buffer;
}
FlexibleArray& snprintf(const char* format) {
size_t len = strlen(format);
if ( len + _nullterminator >= _size ) {
throw BufferOverflow("The buffer is not big enough to copy the string!");
}
memcpy(_buffer + _nullterminator, format, sizeof(Type) * (len+1) );
_nullterminator += len;
return *this;
}
template<size_t OtherSize>
FlexibleArray& snprintf(const Type (&format)[OtherSize]) {
if ( OtherSize + _nullterminator >= _size ) {
throw BufferOverflow("The buffer is not big enough to copy the Type!");
}
memcpy(_buffer + _nullterminator, format, sizeof(Type) * (OtherSize) );
return *this;
}
template<typename ...Args, typename First>
FlexibleArray& snprintf(const char* format, const First first, const Args... args) {
int written = ::snprintf(_buffer + _nullterminator, _size - _nullterminator, format, first, args...);
if (written < 0) {
#if defined( _WIN32 )
char errnobuffer[128];
strerror_s(errnobuffer, sizeof errnobuffer, errno);
throw BufferOverflow(errnobuffer);
#else
throw BufferOverflow(strerror(errno));
#endif
}
_nullterminator += written;
if (_nullterminator >= _size) {
_nullterminator = _size - 1;
throw BufferOverflow("The buffer is not big enough for the string formatting!");
}
return *this;
}
FlexibleArray& appendstr(Type value) {
if (_nullterminator < _size - 1) {
_buffer[_nullterminator] = value;
++_nullterminator;
_buffer[_nullterminator] = Type();
}
else {
throw BufferOverflow("The buffer is not big enough for the string append operation!");
}
return *this;
}
};
// g++ -o test -Wall -Wextra -ggdb -g3 -pthread FlexibleArray.cpp && gdb --args ./test
// valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose ./test
// procdump -accepteula -ma -e -f "" -x c:\ test.exe
int main(int argc, char* argv[]) {
std::cerr << "1 '" << "'" << std::endl;
FlexibleArray<char, 10> string1{"var"};
// 1 ''
// constructor FlexibleArray() 10
// copy constructor coping 3 'var'
std::cerr << "2 '" << string1 << "'" << std::endl;
FlexibleArray<char, 10> string2 = std::move(string1);
string2.appendstr('2');
// 2 'var'
// move constructor 10->10 'var'
std::cerr << "3 '" << string2 << "'" << std::endl;
FlexibleArray<char, 10> string3{string2};
string3.appendstr('3');
// 3 'var2'
// constructor FlexibleArray() 10
// copy size constructor 10
// copy constructor coping 4 'var2'
std::cerr << "4 '" << string3 << "'" << std::endl;
FlexibleArray<char, 10> string4{std::move(string2)};
string4.appendstr('4');
// 4 'var23'
// move constructor 10->10 'var2'
std::cerr << "5 '" << string4 << "'" << std::endl;
FlexibleArray<char, 12> string5 = string3;
string5.appendstr('5');
// 5 'var24'
// constructor FlexibleArray() 12
// copy othersize constructor 10->12
// copy constructor coping 5 'var23'
std::cerr << "6 '" << string5 << "'" << std::endl;
string3 = string4;
string3.appendstr('6');
// 6 'var235'
// copy size assignment 10
// copy constructor coping 5 'var24'
std::cerr << "7 '" << string3 << "'" << std::endl;
string3 = std::move(string4);
string3.appendstr('7');
// 7 'var246'
// move assignment 10->10 'var24'
// destructor ~FlexibleArray() 10 'var246'
std::cerr << "8 '" << string3 << "'" << std::endl;
FlexibleArray<char, 12> string6{std::move(string3)};
string6.appendstr('8');
// 8 'var247'
// move constructor 10->12 'var247'
std::cerr << "9 '" << string6 << "'" << std::endl;
string6 = std::move(string6);
string6.appendstr('9');
// 9 'var2478'
// move assignment 12->12 'var2478'
std::cerr << "A '" << string6 << "'" << std::endl;
string6 = string6;
string6.appendstr('A');
// A 'var24789'
// copy size assignment 12
std::cerr << "B '" << string6 << "'" << std::endl;
FlexibleArray<char, 10> string7{"bar"};
string7.appendstr('B');
// B 'var24789A'
// constructor FlexibleArray() 10
// copy constructor coping 3 'bar'
std::cerr << "C '" << string7 << "'" << std::endl;
string7 = std::move(string5);
string7.appendstr('C');
// C 'barB'
// move assignment 12->10 'var235'
// destructor ~FlexibleArray() 10 'barB'
std::cerr << "D '" << string7 << "'" << std::endl;
string6 = string7;
string6.appendstr('D');
// D 'var235C'
// copy othersize assignment 10->12
// copy constructor coping 7 'var235C'
std::cerr << "E '" << string6 << "'" << std::endl;
// E 'var235CD'
// destructor ~FlexibleArray() 10 'var235C'
// destructor ~FlexibleArray() 12 'var235CD'
// destructor ~FlexibleArray() 12 ''
// destructor ~FlexibleArray() 10 ''
// destructor ~FlexibleArray() 10 ''
// destructor ~FlexibleArray() 10 ''
// destructor ~FlexibleArray() 10 ''
// [01:51:37] The process has exited.
// [01:51:37] Dump count not reached.
std::cerr << "Exiting..." << std::endl;
return 0;
}