Beispiellösung
Content |
#ifndef ARRAY_HPP #define ARRAY_HPP #include <algorithm> #include <cassert> #include <cstdlib> #include <exception> #include <initializer_list> #include <iterator> #include <new> #include <sstream> #include <string> #include <utility> template<typename T> class Array { private: using Tag = enum {AllocateStorage}; Array(Tag tag, std::size_t size) : size(size), data(size > 0? static_cast<T*>(operator new(sizeof(T) * size)) : nullptr) { } public: class Exception: public std::exception {}; class OutOfRange: public Exception { public: OutOfRange(std::size_t index, std::size_t size) { std::ostringstream os; os << "index " << index << " is out of range [0, " << size << ")"; message = os.str(); } const char* what() const noexcept override { return message.c_str(); } private: std::string message; }; Array() noexcept : size(0), data(nullptr) { } Array(std::size_t size) : Array(AllocateStorage, size) { for (std::size_t index = 0; index < size; ++index) { new (data + index) T(); } } Array(std::size_t size, const T& t) : Array(AllocateStorage, size) { for (std::size_t index = 0; index < size; ++index) { new (data + index) T(t); } } template<typename Iterator> Array(Iterator it1, Iterator it2) : Array(AllocateStorage, std::distance(it1, it2)) { std::size_t index = 0; while (it1 != it2 && index < size) { new (data + index++) T(*it1++); } size = index; // just in case } Array(std::initializer_list<T> elements) : Array(elements.begin(), elements.end()) { } Array(const Array& other) : Array(AllocateStorage, other.size) { for (std::size_t index = 0; index < size; ++index) { new (data + index) T(other.data[index]); } } friend void swap(Array& a1, Array& a2) noexcept { using std::swap; swap(a1.size, a2.size); swap(a1.data, a2.data); } Array(Array&& other) noexcept : Array() { swap(*this, other); } ~Array() { for (std::size_t index = 0; index < size; ++index) { data[index].~T(); } operator delete(data); } Array& operator=(Array other) noexcept { swap(*this, other); return *this; } std::size_t get_size() const noexcept { return size; } T& operator()(std::size_t index) { if (index >= size) throw OutOfRange(index, size); return data[index]; } const T& operator()(std::size_t index) const { if (index >= size) throw OutOfRange(index, size); return data[index]; } T* begin() noexcept { return data; } const T* begin() const noexcept { return data; } T* end() noexcept { return data + size; } const T* end() const noexcept { return data + size; } private: std::size_t size; T* data; }; #endif
#include <exception> #include <iomanip> #include <iostream> #include "array.hpp" template<typename T> void test_operation(unsigned int test, std::string description, T&& operation) { std::cout << "Test " << std::setw(2) << std::right << test << ": "; std::cout << std::setw(30) << std::left << std::move(description) << " "; try { operation(); std::cout << "ok" << std::endl; } catch (std::exception& e) { std::cout << "exception: " << e.what() << std::endl; } catch (...) { std::cout << "unknown exception" << std::endl; } } struct FailingObject { class Exception: public std::exception { const char* what() const noexcept override { return "FailingObject exception"; } }; FailingObject() { throw Exception(); } FailingObject(const FailingObject& other) { throw Exception(); } FailingObject(int i) { if (i < 0) throw Exception(); } FailingObject& operator=(const FailingObject& other) { throw Exception(); } }; int main() { unsigned int test = 0; test_operation(++test, "Array()", []() { Array<FailingObject>(); }); test_operation(++test, "Array(0)", []() { Array<FailingObject>(0); }); test_operation(++test, "Array(1)", []() { Array<FailingObject>(1); }); test_operation(++test, "Array(1, FailingObject(1))", []() { Array<FailingObject>(1, FailingObject(1)); }); test_operation(++test, "Array(it1, it2) from {1, 2}", []() { int values[] = {1, 2, 3}; Array<FailingObject>(values, values + sizeof(values)/sizeof(values[0])); }); test_operation(++test, "Array(it1, it2) from {1, -2}", []() { int values[] = {1, -2}; Array<FailingObject>(values, values + sizeof(values)/sizeof(values[0])); }); test_operation(++test, "Array{1, 2}", []() { Array<FailingObject>{1, 2}; }); test_operation(++test, "Array{1, -2}", []() { Array<FailingObject>{1, -2}; }); test_operation(++test, "Array copy constructor", []() { int values[] = {1, 2}; Array<FailingObject> array(values, values + sizeof(values)/sizeof(values[0])); Array<FailingObject> array2(array); }); test_operation(++test, "Array move constructor", []() { int values[] = {1, 2}; Array<FailingObject> array(values, values + sizeof(values)/sizeof(values[0])); Array<FailingObject>(std::move(array)); }); test_operation(++test, "Array swap", []() { int values[] = {1, 2}; Array<FailingObject> array1(values, values + sizeof(values)/sizeof(values[0])); Array<FailingObject> array2(values, values + sizeof(values)/sizeof(values[0])); using std::swap; swap(array1, array2); }); test_operation(++test, "Array assignment", []() { int values[] = {1, 2}; Array<FailingObject> array1(values, values + sizeof(values)/sizeof(values[0])); Array<FailingObject> array2; array2 = std::move(array1); }); test_operation(++test, "Array get_size", []() { int values[] = {1, 2}; Array<FailingObject> array(values, values + sizeof(values)/sizeof(values[0])); array.get_size(); }); test_operation(++test, "Array access by index", []() { int values[] = {1, 2}; Array<FailingObject> array(values, values + sizeof(values)/sizeof(values[0])); array(1); }); test_operation(++test, "Array access by index const", []() { int values[] = {1, 2}; const Array<FailingObject> array(values, values + sizeof(values)/sizeof(values[0])); array(1); }); test_operation(++test, "Array out of range", []() { int values[] = {1, 2}; Array<int> array(values, values + sizeof(values)/sizeof(values[0])); array(2); }); test_operation(++test, "Array const out of range", []() { int values[] = {1, 2}; const Array<int> array(values, values + sizeof(values)/sizeof(values[0])); array(2); }); test_operation(++test, "Array iterators", []() { int values[] = {1, 2}; Array<FailingObject> array(values, values + sizeof(values)/sizeof(values[0])); for (auto& object: array) { } }); test_operation(++test, "Array const iterators", []() { int values[] = {1, 2}; const Array<FailingObject> array(values, values + sizeof(values)/sizeof(values[0])); for (auto& object: array) { } }); std::cout << test << " tests survived." << std::endl; }
theon$ g++ --coverage -Wall -Wno-unused-variable -o testit testit.cpp theon$ ./testit Test 1: Array() ok Test 2: Array(0) ok Test 3: Array(1) exception: FailingObject exception Test 4: Array(1, FailingObject(1)) exception: FailingObject exception Test 5: Array(it1, it2) from {1, 2} ok Test 6: Array(it1, it2) from {1, -2} exception: FailingObject exception Test 7: Array{1, 2} exception: FailingObject exception Test 8: Array{1, -2} exception: FailingObject exception Test 9: Array copy constructor exception: FailingObject exception Test 10: Array move constructor ok Test 11: Array swap ok Test 12: Array assignment ok Test 13: Array get_size ok Test 14: Array access by index ok Test 15: Array access by index const ok Test 16: Array out of range exception: index 2 is out of range [0, 2) Test 17: Array const out of range exception: index 2 is out of range [0, 2) Test 18: Array iterators ok Test 19: Array const iterators ok 19 tests survived. theon$ gcov testit >/dev/null theon$ cat array.hpp.gcov -: 0:Source:array.hpp -: 0:Graph:testit.gcno -: 0:Data:testit.gcda -: 0:Runs:1 -: 0:Programs:1 -: 1:#ifndef ARRAY_HPP -: 2:#define ARRAY_HPP -: 3: -: 4:#include-: 5:#include -: 6:#include -: 7:#include -: 8:#include -: 9:#include -: 10:#include -: 11:#include -: 12:#include -: 13:#include -: 14: -: 15:template -: 16:class Array { -: 17: private: -: 18: using Tag = enum {AllocateStorage}; 19: 19: Array(Tag tag, std::size_t size) : -: 20: size(size), 21: 21: data(size > 0? 2: 22: static_cast (operator new(sizeof(T) * size)) -: 23: : 38: 24: nullptr) { 19: 25: } -: 26: public: 4: 27: class Exception: public std::exception {}; 2: 28: class OutOfRange: public Exception { -: 29: public: 2: 30: OutOfRange(std::size_t index, std::size_t size) { 4: 31: std::ostringstream os; 2: 32: os << "index " << index << " is out of range [0, " << -: 33: size << ")"; 2: 34: message = os.str(); 2: 35: } 2: 36: const char* what() const noexcept override { 2: 37: return message.c_str(); -: 38: } -: 39: private: -: 40: std::string message; -: 41: }; 4: 42: Array() noexcept : size(0), data(nullptr) { 4: 43: } 3: 44: Array(std::size_t size) : Array(AllocateStorage, size) { 2: 45: for (std::size_t index = 0; index < size; ++index) { 2: 46: new (data + index) T(); -: 47: } 1: 48: } 2: 49: Array(std::size_t size, const T& t) : Array(AllocateStorage, size) { 1: 50: for (std::size_t index = 0; index < size; ++index) { 2: 51: new (data + index) T(t); -: 52: } #####: 53: } -: 54: template 15: 55: Array(Iterator it1, Iterator it2) : 17: 56: Array(AllocateStorage, std::distance(it1, it2)) { 15: 57: std::size_t index = 0; 71: 58: while (it1 != it2 && index < size) { 32: 59: new (data + index++) T(*it1++); -: 60: } 13: 61: size = index; // just in case 13: 62: } 1: 63: Array(std::initializer_list elements) : 1: 64: Array(elements.begin(), elements.end()) { #####: 65: } 2: 66: Array(const Array& other) : Array(AllocateStorage, other.size) { 1: 67: for (std::size_t index = 0; index < size; ++index) { 2: 68: new (data + index) T(other.data[index]); -: 69: } #####: 70: } 4: 71: friend void swap(Array& a1, Array& a2) noexcept { -: 72: using std::swap; 4: 73: swap(a1.size, a2.size); 4: 74: swap(a1.data, a2.data); 4: 75: } 2: 76: Array(Array&& other) noexcept : Array() { 2: 77: swap(*this, other); 2: 78: } 23: 79: ~Array() { 27: 80: for (std::size_t index = 0; index < size; ++index) { 4: 81: data[index].~T(); -: 82: } 23: 83: operator delete(data); 23: 84: } 1: 85: Array& operator=(Array other) noexcept { 1: 86: swap(*this, other); 1: 87: return *this; -: 88: } 1: 89: std::size_t get_size() const noexcept { 1: 90: return size; -: 91: } 2: 92: T& operator()(std::size_t index) { 2: 93: if (index >= size) throw OutOfRange(index, size); 1: 94: return data[index]; -: 95: } 2: 96: const T& operator()(std::size_t index) const { 2: 97: if (index >= size) throw OutOfRange(index, size); 1: 98: return data[index]; -: 99: } 1: 100: T* begin() noexcept { 1: 101: return data; -: 102: } 1: 103: const T* begin() const noexcept { 1: 104: return data; -: 105: } 1: 106: T* end() noexcept { 1: 107: return data + size; -: 108: } 1: 109: const T* end() const noexcept { 1: 110: return data + size; -: 111: } -: 112: private: -: 113: std::size_t size; -: 114: T* data; -: 115:}; -: 116: -: 117:#endif theon$ rm -f *.gcov theon$