This commit is contained in:
Erik Frojdh 2019-08-12 14:23:12 +02:00
parent c36dfc3992
commit 8e2494729b
6 changed files with 167 additions and 156 deletions

View File

@ -4,120 +4,59 @@
#include <stdexcept> #include <stdexcept>
#include <vector> #include <vector>
#include "TypeTraits.h"
namespace sls { namespace sls {
template <typename T, size_t Capacity> class FixedCapacityContainer { template <typename T, size_t Capacity> class FixedCapacityContainer {
public: public:
using size_type = typename std::array<T, Capacity>::size_type;
using value_type = typename std::array<T, Capacity>::value_type;
using iterator = typename std::array<T, Capacity>::iterator;
using const_iterator = typename std::array<T, Capacity>::const_iterator;
FixedCapacityContainer() = default; FixedCapacityContainer() = default;
explicit FixedCapacityContainer(std::initializer_list<T> l); explicit FixedCapacityContainer(std::initializer_list<T> l) {
explicit FixedCapacityContainer(const std::vector<T> &v); current_size = l.size();
template <size_t OtherCapacity>
explicit FixedCapacityContainer(
const FixedCapacityContainer<T, OtherCapacity> &other) noexcept;
FixedCapacityContainer &operator=(const std::vector<T> &other);
bool operator==(const std::vector<T> &other) const noexcept;
bool operator!=(const std::vector<T> &other) const noexcept;
operator std::vector<T>(){return std::vector<T>(begin(), end());}
template <size_t OtherCapacity>
bool operator==(const FixedCapacityContainer<T, OtherCapacity> &other) const
noexcept;
template <size_t OtherCapacity>
bool operator!=(const FixedCapacityContainer<T, OtherCapacity> &other) const
noexcept;
T &operator[](size_t i) { return data_[i]; }
const T &operator[](size_t i) const { return data_[i]; }
constexpr size_t size() const noexcept { return size_; }
bool empty() const noexcept { return size_ == 0; }
constexpr size_t capacity() const noexcept { return Capacity; }
void push_back(const T &value);
void resize(size_t new_size);
void erase(T *ptr);
T &front() noexcept { return data_.front(); }
T &back() noexcept { return data_[size_ - 1]; }
constexpr const T &front() const noexcept { return data_.front(); }
constexpr const T &back() const noexcept { return data_[size_ - 1]; }
// iterators
T *begin() noexcept { return &data_[0]; }
T *end() noexcept { return &data_[size_]; }
const T *cbegin() const noexcept { return &data_[0]; }
const T *cend() const noexcept { return &data_[size_]; }
private:
size_t size_{0};
std::array<T, Capacity> data_;
} __attribute__((packed));
/* Member functions */
template <typename T, size_t Capacity>
FixedCapacityContainer<T, Capacity>::FixedCapacityContainer(
std::initializer_list<T> l) {
size_ = l.size();
std::copy(l.begin(), l.end(), data_.begin()); std::copy(l.begin(), l.end(), data_.begin());
} }
template <typename T, size_t Capacity> template <typename V,
FixedCapacityContainer<T, Capacity>::FixedCapacityContainer( typename = typename std::enable_if<
const std::vector<T> &v) { is_light_container<V>::value &&
std::is_same<T, typename V::value_type>::value>::type>
explicit FixedCapacityContainer(const V &v) {
if (v.size() > Capacity) { if (v.size() > Capacity) {
throw std::runtime_error( throw std::runtime_error(
"Capacity needs to be same size or larger than vector"); "Capacity needs to be same size or larger than vector");
} }
size_ = v.size(); current_size = v.size();
std::copy(v.begin(), v.end(), data_.begin()); std::copy(v.begin(), v.end(), data_.begin());
} }
template <typename T, size_t Capacity>
template <size_t OtherCapacity> template <size_t OtherCapacity>
FixedCapacityContainer<T, Capacity>::FixedCapacityContainer( explicit FixedCapacityContainer(
const FixedCapacityContainer<T, OtherCapacity> &other) noexcept { const FixedCapacityContainer<T, OtherCapacity> &other) noexcept {
static_assert(Capacity >= OtherCapacity, static_assert(Capacity >= OtherCapacity,
"Container needs to be same size or larger"); "Container needs to be same size or larger");
size_ = other.size(); current_size = other.size();
std::copy(other.cbegin(), other.cend(), data_.begin()); std::copy(other.cbegin(), other.cend(), data_.begin());
} }
template <typename T, size_t Capacity> FixedCapacityContainer &operator=(const std::vector<T> &other) {
void FixedCapacityContainer<T, Capacity>::push_back(const T &value) {
if (size_ == Capacity) {
throw std::runtime_error("Container is full");
} else {
data_[size_] = value;
++size_;
}
}
template <typename T, size_t Capacity>
void FixedCapacityContainer<T, Capacity>::resize(size_t new_size) {
if (new_size > Capacity) {
throw std::runtime_error("Cannot resize beyond capacity");
} else {
size_ = new_size;
}
}
template <typename T, size_t Capacity>
FixedCapacityContainer<T, Capacity> &FixedCapacityContainer<T, Capacity>::
operator=(const std::vector<T> &other) {
std::copy(other.begin(), other.end(), data_.begin()); std::copy(other.begin(), other.end(), data_.begin());
size_ = other.size(); current_size = other.size();
return *this; return *this;
} }
template <typename T, size_t Capacity> /** Compare FixedCapacityContainer with any other container*/
bool FixedCapacityContainer<T, Capacity>:: template <typename V>
operator==(const std::vector<T> &other) const noexcept { typename std::enable_if<is_container<V>::value, bool>::type
if (size_ != other.size()) { operator==(const V &other) const noexcept {
if (current_size != other.size()) {
return false; return false;
} else { } else {
for (size_t i = 0; i != size_; ++i) { for (size_t i = 0; i != current_size; ++i) {
if (data_[i] != other[i]) { if (data_[i] != other[i]) {
return false; return false;
} }
@ -126,54 +65,72 @@ operator==(const std::vector<T> &other) const noexcept {
return true; return true;
} }
template <typename T, size_t Capacity> template <typename V>
bool FixedCapacityContainer<T, Capacity>:: typename std::enable_if<is_container<V>::value, bool>::type
operator!=(const std::vector<T> &other) const noexcept { operator!=(const V &other) const noexcept {
return !(*this == other); return !(*this == other);
} }
template <typename T, size_t Capacity> operator std::vector<T>() { return std::vector<T>(begin(), end()); }
template <size_t OtherCapacity>
bool FixedCapacityContainer<T, Capacity>:: T &operator[](size_t i) { return data_[i]; }
operator==(const FixedCapacityContainer<T, OtherCapacity> &other) const const T &operator[](size_t i) const { return data_[i]; }
noexcept { constexpr size_type size() const noexcept { return current_size; }
if (size_ != other.size()) { bool empty() const noexcept { return current_size == 0; }
return false; constexpr size_t capacity() const noexcept { return Capacity; }
void push_back(const T &value) {
if (current_size == Capacity) {
throw std::runtime_error("Container is full");
} else { } else {
for (size_t i = 0; i != size_; ++i) { data_[current_size] = value;
if (data_[i] != other[i]) { ++current_size;
return false;
} }
} }
}
return true;
}
template <typename T, size_t Capacity> void resize(size_t new_size) {
template <size_t OtherCapacity> if (new_size > Capacity) {
bool FixedCapacityContainer<T, Capacity>:: throw std::runtime_error("Cannot resize beyond capacity");
operator!=(const FixedCapacityContainer<T, OtherCapacity> &other) const } else {
noexcept { current_size = new_size;
return !(*this == other); }
} }
template <typename T, size_t Capacity> void erase(T *ptr) {
void FixedCapacityContainer<T, Capacity>::erase(T *ptr) {
if (ptr >= begin() && ptr < end()) { if (ptr >= begin() && ptr < end()) {
size_ = static_cast<size_t>(ptr - begin()); current_size = static_cast<size_t>(ptr - begin());
} else { } else {
throw std::runtime_error("tried to erase with a ptr outside obj"); throw std::runtime_error("tried to erase with a ptr outside obj");
} }
} }
T &front() noexcept { return data_.front(); }
T &back() noexcept { return data_[current_size - 1]; }
constexpr const T &front() const noexcept { return data_.front(); }
constexpr const T &back() const noexcept { return data_[current_size - 1]; }
// iterators
iterator begin() noexcept { return data_.begin(); }
const_iterator begin() const noexcept { return data_.begin(); }
iterator end() noexcept { return &data_[current_size]; }
const_iterator end() const noexcept { return &data_[current_size]; }
const_iterator cbegin() const noexcept { return data_.cbegin(); }
const_iterator cend() const noexcept { return &data_[current_size]; }
private:
size_type current_size{};
std::array<T, Capacity> data_;
} __attribute__((packed));
/* Free function concerning FixedCapacityContainer */ /* Free function concerning FixedCapacityContainer */
template <typename T, size_t Capacity> template <typename T, size_t Capacity>
constexpr T *begin(FixedCapacityContainer<T, Capacity> &container) noexcept { typename FixedCapacityContainer<T, Capacity>::iterator
begin(FixedCapacityContainer<T, Capacity> &container) noexcept {
return container.begin(); return container.begin();
} }
template <typename T, size_t Capacity> template <typename T, size_t Capacity>
constexpr T *end(FixedCapacityContainer<T, Capacity> &container) noexcept { typename FixedCapacityContainer<T, Capacity>::iterator
end(FixedCapacityContainer<T, Capacity> &container) noexcept {
return container.end(); return container.end();
} }

View File

@ -137,4 +137,11 @@ template <typename T> T StringTo(std::string t) {
return StringTo<T>(t, unit); return StringTo<T>(t, unit);
} }
/** For types with a .str() method use this for conversion */
template <typename T>
typename std::enable_if<has_str<T>::value, std::string>::type
ToString(const T &obj) {
return obj.str();
}
} // namespace sls } // namespace sls

View File

@ -58,4 +58,25 @@ struct is_container<
decltype(std::declval<T>().empty())>, decltype(std::declval<T>().empty())>,
void>::type> : public std::true_type {}; void>::type> : public std::true_type {};
/**
* Type trait to evaluate if template parameter is
* complying with a standard container
*/
template <typename T, typename _ = void>
struct is_light_container : std::false_type {};
template <typename... Ts> struct is_light_container_helper {};
template <typename T>
struct is_light_container<
T, typename std::conditional<
false,
is_container_helper<typename T::value_type, typename T::size_type,
typename T::iterator, typename T::const_iterator,
decltype(std::declval<T>().size()),
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>,
void>::type> : public std::true_type {};
} // namespace sls } // namespace sls

View File

@ -1,8 +1,28 @@
#include "FixedCapacityContainer.h" #include "FixedCapacityContainer.h"
#include "catch.hpp" #include "catch.hpp"
#include "TypeTraits.h"
#include <vector>
#include <array>
using sls::FixedCapacityContainer; using sls::FixedCapacityContainer;
TEST_CASE("FixedCapacityContainer is a container"){
REQUIRE(sls::is_container<FixedCapacityContainer<int,7>>::value == true);
}
TEST_CASE("Construct from vector"){
std::vector<int> vec{1,2,3};
FixedCapacityContainer<int, 5> fcc{vec};
REQUIRE(fcc == vec);
}
TEST_CASE("Construct from array"){
std::array<int,3> arr{1,2,3};
FixedCapacityContainer<int, 5> fcc{arr};
REQUIRE(fcc == arr);
}
SCENARIO("FixedCapacityContainers can be sized and resized", "[support]") { SCENARIO("FixedCapacityContainers can be sized and resized", "[support]") {
GIVEN("A default constructed container") { GIVEN("A default constructed container") {

View File

@ -1,23 +1,21 @@
#include "TimeHelper.h" #include "TimeHelper.h"
#include "ToString.h" #include "ToString.h"
#include "network_utils.h"
#include "catch.hpp" #include "catch.hpp"
#include <vector>
#include <array> #include <array>
#include <vector>
// using namespace sls; // using namespace sls;
using sls::ToString;
using sls::StringTo; using sls::StringTo;
using sls::ToString;
using namespace sls::time; using namespace sls::time;
TEST_CASE("Integer conversions", "[support]") { TEST_CASE("Integer conversions", "[support]") {
REQUIRE(ToString(0) == "0"); REQUIRE(ToString(0) == "0");
REQUIRE(ToString(1) == "1"); REQUIRE(ToString(1) == "1");
REQUIRE(ToString(-1) == "-1"); REQUIRE(ToString(-1) == "-1");
REQUIRE(ToString(100) == "100"); REQUIRE(ToString(100) == "100");
REQUIRE(ToString(589633100) == "589633100"); REQUIRE(ToString(589633100) == "589633100");
} }
TEST_CASE("floating point conversions", "[support]") { TEST_CASE("floating point conversions", "[support]") {
@ -32,7 +30,6 @@ TEST_CASE("floating point conversions", "[support]"){
REQUIRE(ToString(2.35010) == "2.3501"); REQUIRE(ToString(2.35010) == "2.3501");
REQUIRE(ToString(5000) == "5000"); REQUIRE(ToString(5000) == "5000");
REQUIRE(ToString(5E15) == "5000000000000000"); REQUIRE(ToString(5E15) == "5000000000000000");
} }
TEST_CASE("conversion from duration to string", "[support]") { TEST_CASE("conversion from duration to string", "[support]") {
@ -73,7 +70,6 @@ TEST_CASE("Vector of int", "[support]"){
vec.push_back(5000); vec.push_back(5000);
REQUIRE(ToString(vec) == "[1, 172, 5000]"); REQUIRE(ToString(vec) == "[1, 172, 5000]");
} }
TEST_CASE("Vector of double", "[support]") { TEST_CASE("Vector of double", "[support]") {
@ -93,5 +89,10 @@ TEST_CASE("Vector of double", "[support]"){
TEST_CASE("Array") { TEST_CASE("Array") {
std::array<int, 3> arr{1, 2, 3}; std::array<int, 3> arr{1, 2, 3};
REQUIRE(ToString(arr) == "[1, 2, 3]"); REQUIRE(ToString(arr) == "[1, 2, 3]");
}
TEST_CASE("Convert types with str method"){
sls::IpAddr addr;
REQUIRE(ToString(addr) == "0.0.0.0");
REQUIRE(ToString(sls::IpAddr{}) == "0.0.0.0");
} }

View File

@ -4,6 +4,7 @@
#include <vector> #include <vector>
#include <sstream> #include <sstream>
#include <chrono> #include <chrono>
#include <initializer_list>
//Dummy classes only used here for testing //Dummy classes only used here for testing
class DummyWithStr { class DummyWithStr {
@ -39,3 +40,7 @@ TEST_CASE("sls::is_duration"){
REQUIRE(sls::is_duration<int>::value == false); REQUIRE(sls::is_duration<int>::value == false);
REQUIRE(sls::is_duration<std::vector<int>>::value == false); REQUIRE(sls::is_duration<std::vector<int>>::value == false);
} }
TEST_CASE("initializer list"){
REQUIRE(sls::is_light_container<std::initializer_list<int>>::value == true);
}