#include <iostream>
#include <sstream>
// ============================================== Rudimentary TDD-Support
#define SHOW_(expect, ...)\
do {\
std::ostringstream result;\
result.copyfmt(std::cout);\
result << (__VA_ARGS__);\
const bool ok = (result.str() == expect);\
std::cout << (ok ? '*' : '!') << ' '\
<< __FUNCTION__ << ':' << __LINE__ << '\t'\
<< #__VA_ARGS__ << " --> " << result.str();\
if (!ok) {\
std::cout << " != " << expect;\
}\
std::cout << std::endl;\
}\
while (0)
// =======================================================================
#include "my_static_vector.h"
void constructor_test() {
my::static_vector<int, 10> obj1;
SHOW_("48", sizeof obj1);
my::static_vector<double, 3> obj2;
SHOW_("32", sizeof obj2);
my::static_vector<int, 5> obj3{5, 9, 12};
SHOW_("5", obj3.at(0));
SHOW_("9", obj3.at(1));
SHOW_("12", obj3.at(2));
}
void max_size_test() {
my::static_vector<int, 10> obj1;
SHOW_("10", obj1.max_size());
my::static_vector<double, 3> obj2;
SHOW_("3", obj2.max_size());
}
void push_pop_back_test() {
my::static_vector<int, 10> obj1;
obj1.push_back(123);
SHOW_("123", obj1.back());
obj1.push_back(-1);
SHOW_("-1", obj1.back());
obj1.pop_back();
SHOW_("123", obj1.back());
}
int main() {
constructor_test();
// max_size_test();
// push_pop_back_test();
std::cout << "*** TESTING COMPLETED ***" << std::endl;
}
#ifndef MY_STATIC_VECTOR_H
#define MY_STATIC_VECTOR_H
#include <cassert>
#include <initializer_list>
namespace my {
/*
* A class much like `std::vector` but with pre-allocated space
* in a maximum size. As for `std::vector` this container is empty
* at the beginning. Need to be added before they can be accessed.
*/
template <typename T, std::size_t N>
class static_vector {
public:
using value_type = T;
using size_type = decltype(N);
// c'tors, d'tors and assignment
static_vector() : filled_{data_} {} // implemented
static_vector(std::initializer_list<value_type> const& init)
: filled_{data_}
{
assert(init.size() <= max_size()); // check "breach of contract"
for (auto &e : init) {
if (size() == max_size()) break; // <------ for "robustness"
push_back(e);
}
}
static_vector(const static_vector&) =default;
static_vector(static_vector&&) =default;
~static_vector() =default;
static_vector& operator=(const static_vector&) =default;
static_vector& operator=(static_vector&&) =default;
static constexpr size_type max_size() {
return N;
}
size_type size() const {
assert_in_bounds();
return filled_ - data_;
}
size_type empty() const {
return (size() == 0);
}
value_type* data() { return data_; }
value_type const* data() const { return data_; }
value_type& front() { assert_not_empty(); return data_[0]; }
value_type const& front() const { assert_not_empty(); return data_[0]; }
value_type& back() { assert_not_empty(); return filled_[-1]; }
value_type const& back() const { assert_not_empty(); return filled_[-1]; }
// mimicking the behavior of `std::vector` the index operator as
// implemented below causes undefined behavior for out of bounds
// indices
value_type& operator[](size_type index) { assert_in_bounds(); return data_[index]; }
value_type const& operator[](size_type index) const { assert_in_bounds(); return data_[index]; }
#if 1
// mimicking the behavior of vectors `at` should NOT cause undefined
// behavior (contrary to `operator[]` which may do so) but the proposed
// solution is not REALLY a good one; though without exceptions there
// is not much wigleroom for somehow "curing" the problem as any
// solution chosen will have a tendency to cover up a problem that
// would have better be detected earlier ...
value_type& at(size_type index) {assert_not_empty(); return data_[index % size()]; }
value_type const& at(size_type index) const { assert_not_empty(); return data_[index % size()]; }
#endif
void push_back(T const& item_to_append) {
assert_not_full();
*(filled_++) = item_to_append;
assert_not_empty();
}
void pop_back() {
assert_not_empty();
--filled_;
assert_not_full();
}
#if 0
void resize(size_type new_size) {
static_assert(!"not yet implemented");
}
void clear() {
resize(0);
}
#endif
// providing iterators (we can simply use pointers here)
using iterator = value_type*;
using const_iterator = value_type const*;
iterator begin() { return data_; }
iterator end() { return filled_; }
const_iterator begin() const { return data_; }
const_iterator end() const { return filled_; }
const_iterator cbegin() const { return data_; }
const_iterator cend() const { return filled_; }
private:
// assertions for early error detection in unit tests
// do NOT call public member functions here EXCEPT size()
// so that the below may used freely;
void assert_in_bounds() const {
assert(filled_ >= data_);
assert(filled_ <= data_ + N);
}
void assert_not_empty() const {
assert(size() > 0);
}
void assert_not_full() const {
assert(size() < max_size());
}
value_type data_[N];
value_type* filled_;
};
} // namespace
#endif // include guard