Skip to content

Instantly share code, notes, and snippets.

@evandrocoan
Last active April 24, 2022 23:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evandrocoan/65f87210a4817fee1af3945c8683c006 to your computer and use it in GitHub Desktop.
Save evandrocoan/65f87210a4817fee1af3945c8683c006 to your computer and use it in GitHub Desktop.
Copy and move constructor example, rule of 5

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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment