Commit 871796a1 authored by Eric Cano's avatar Eric Cano
Browse files

Added a range iteratable generator.

parent 96f3b898
......@@ -184,7 +184,8 @@ set (COMMON_UNIT_TESTS_LIB_SRC_FILES
utils/RegexTest.cpp
utils/UtilsTest.cpp
UserIdentityTest.cpp
optionalTest.cpp)
optionalTest.cpp
rangeTest.cpp)
add_library (ctacommonunittests SHARED
${COMMON_UNIT_TESTS_LIB_SRC_FILES})
......
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2015 CERN
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <limits>
#include "common/exception/Exception.hpp"
namespace cta {
template <typename T>
class range: public std::initializer_list<T> {
public:
range (T begin, T end, T step=1): m_begin(begin), m_end(end), m_step(step) {}
class iterator {
private:
friend range;
enum class direction: bool { forward, backward };
iterator(T begin, T end, T step, iterator::direction dir): m_val(begin), m_limit(end), m_step(step), m_dir(dir) {}
public:
CTA_GENERATE_EXCEPTION_CLASS(DereferencingPastEnd);
T operator*() { if (m_val == m_limit) throw DereferencingPastEnd("In range::operator*(): dereferencing out of bounds"); return m_val; }
iterator & operator++() { doInc(); return *this; }
iterator operator++(int) { iterator ret(*this); doInc(); return ret; }
void doInc() {
switch (m_dir) {
// Increment/decrement variable, preventing over/underflow, and overshooting the limit.
case direction::forward:
// Prevent overflow
if (m_step > std::numeric_limits<T>::max() - m_val) {
m_val=m_limit;
} else {
m_val+=m_step;
// Prevent overshoot.
if (m_val > m_limit)
m_val=m_limit;
}
break;
case direction::backward:
// Prevent underflow
if (m_step > m_val - std::numeric_limits<T>::max() ) {
m_val=m_limit;
} else {
m_val-=m_step;
if (m_val < m_limit)
m_val = m_limit;
}
break;
}
}
bool operator==(const iterator& other) { checkIteratorsComparable(other); return m_val == other.m_val; }
bool operator!=(const iterator& other) { checkIteratorsComparable(other); return m_val != other.m_val; }
CTA_GENERATE_EXCEPTION_CLASS(NonComparableIterators);
private:
void checkIteratorsComparable(const iterator & other) const {
if (m_limit != other.m_limit || m_step != other.m_step || m_dir != other.m_dir)
throw NonComparableIterators("In range::checkIteratorsComparable(): comparing iterators from different ranges.");
}
T m_val;
const T m_limit, m_step;
direction m_dir;
};
iterator cbegin() const {
return iterator(m_begin, m_end, m_step, m_begin<=m_end?iterator::direction::forward:iterator::direction::backward);
}
iterator cend() const {
return iterator(m_end, m_end, m_step, m_begin<=m_end?iterator::direction::forward:iterator::direction::backward);
}
iterator begin() const { return cbegin(); }
iterator end() const { return cend(); }
private:
const T m_begin;
const T m_end;
const T m_step;
};
} // namesapce cta
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2015 CERN
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "range.hpp"
#include <list>
#include <gtest/gtest.h>
namespace unitTests {
TEST(cta_rangeTest, basic) {
using namespace cta;
std::list<size_t> l;
// Test forward
for (const auto &i: range<size_t>(0, 4)) l.emplace_back(i);
ASSERT_EQ(std::list<size_t>({0,1,2,3}), l);
l.clear();
// Test forward with step.
for (const auto &i: range<size_t>(0, 10, 3)) l.emplace_back(i);
ASSERT_EQ(std::list<size_t>({0,3,6,9}), l);
l.clear();
// Test forward overflow
const auto max = std::numeric_limits<size_t>::max();
for (const auto &i: range<size_t>(max - 10, max, 3)) l.emplace_back(i);
ASSERT_EQ(std::list<size_t>({max-10, max-7, max-4, max-1}), l);
l.clear();
// Test backwards
for (const auto &i: range<size_t>(10, 5)) l.emplace_back(i);
ASSERT_EQ(std::list<size_t>({10, 9, 8, 7, 6}), l);
l.clear();
// Test backwards with step and underflow.
for (const auto &i: range<size_t>(10, 0, 3)) l.emplace_back(i);
ASSERT_EQ(std::list<size_t>({10, 7, 4, 1}), l);
}
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment