#include <iterator>
namespace detail
{
template< typename T >
class basic_range
{
private:
static T align_to_step(T const first, T const last, T const step)
{
using UT = typename std::make_unsigned<T>::type;
UT const difference = std::abs(last - first);
UT const stepping = std::abs(step);
T const underflow = T(difference % stepping) * (step / T(stepping));
if (underflow == 0) return last;
return last + (step - underflow);
}
public:
explicit basic_range(T const last, int const step = 1)
: basic_range(T{ 0 }, last, step)
{}
explicit basic_range(T const first, T const last, int const step = 1)
: first{ first, step }, last{ align_to_step(first, last, step), step }
{}
basic_range(basic_range const& other) = delete;
basic_range(basic_range && other) = default;
basic_range operator=(basic_range const& other) = delete;
basic_range operator=(basic_range && other) = delete;
public:
struct iterator : std::iterator< std::forward_iterator_tag, T >
{
private:
explicit iterator(T const from, int const step = T{ 1 })
: from{ from }, step{ step }
{}
friend class basic_range< T >;
public:
iterator(iterator const& other) = default;
iterator(iterator && other) = delete;
iterator operator=(iterator const& other) = delete;
iterator operator=(iterator && other) = delete;
T const operator*() const { return from; }
bool operator==(iterator const& other) const { return from == other.from; }
bool operator!=(iterator const& other) const { return from != other.from; }
void operator++() { from += step; }
private:
T from;
T const step;
};
typedef iterator iterator;
typedef iterator const const_iterator;
const_iterator begin() const { return first; }
const_iterator end() const { return last; }
private:
const_iterator first;
const_iterator last;
};
template< typename T, bool is_enum = std::is_enum< T >::value >
struct get_integral_type
{
typedef std::underlying_type_t< T > type;
};
template< typename T >
struct get_integral_type< T, false >
{
typedef T type;
};
template< typename T, bool is_enum = std::is_enum< T >::value >
using get_integral_type_t = typename get_integral_type< T >::type;
}
template< typename T >
auto range(T const begin, T const end, int const step = 1)
{
typedef detail::get_integral_type_t< T > type;
static_assert(std::is_integral< type >::value,
"Only integer-based types allowed!");
return detail::basic_range< type >{
static_cast<type>(begin),
static_cast<type>(end),
step
};
}
template< typename T, typename U >
auto range(T const begin, U const end, int const step = 1)
{
typedef std::common_type_t
<
detail::get_integral_type_t< T >,
detail::get_integral_type_t< U >
> type;
static_assert(std::is_integral< type >::value,
"Only integer-based types allowed!");
return detail::basic_range< type >{
static_cast<type>(begin),
static_cast<type>(end),
step
};
}
template< typename T >
auto reverse_range(T const from, T const to, int const step = -1)
{
return range(from, to, step);
}
template< typename T, typename U >
auto reverse_range(T const from, U const to, int const step = -1)
{
return range(from, to, step);
}
/**************************************************
* Unit testing framework
*************************************************/
#include <string>
#include <functional>
#include <iostream>
#include <exception>
namespace test
{
struct test_spec
{
std::string const name;
std::function< void() > const function;
};
struct failure : std::runtime_error
{
failure(std::string const& message)
: std::runtime_error{ message }
{}
};
bool expect(bool expression, std::string && message)
{
if (!expression)
{
throw failure(message);
}
return true;
}
bool expect(std::function< bool() > const function, std::string && message)
{
if (!function())
{
throw failure(message);
}
return true;
}
template< std::size_t N >
bool run_tests(test_spec const (&specification)[N], std::ostream & os = std::cout)
{
unsigned int failures = 0;
for (auto const& test : specification)
{
try
{
os << test.name;
test.function();
os << " - PASS\n";
}
catch (failure const& f)
{
os << " - FAIL: " << f.what() << "\n";
++failures;
}
}
if (failures > 0)
{
os << "\n" << failures << " out of " << N << (failures == 1 ? " test" : " tests") << " failed\n";
}
return failures == 0 ? true : false;
}
}
/**************************************************
* Implement test cases here
*************************************************/
#include <algorithm>
template< typename Container1, typename Container2 >
auto equal(Container1 const container1, Container2 const container2)
{
return std::equal(std::cbegin(container1), std::cend(container1),
std::cbegin(container2), std::cend(container2));
}
test::test_spec tests[] =
{
"Range_TestForwardRange", []
{
auto expected = { 0, 1, 2, 3, 4 };
std::vector< int > result;
for (auto const i : range(0, 5))
{
result.emplace_back(i);
}
test::expect(equal(result, expected) == true,
"Generated range does not match expected result!");
},
"Range_TestForwardRangeStep", []
{
auto expected = { 0, 2, 4, 6, 8 };
std::vector< int > result;
for (auto const i : range(0, 10, 2))
{
result.emplace_back(i);
}
test::expect(equal(result, expected) == true,
"Generated range does not match expected result!");
},
"Range_TestReverseRange", []
{
auto expected = { 5, 4, 3, 2, 1 };
std::vector< int > result;
for (auto const i : reverse_range(5, 0))
{
result.emplace_back(i);
}
test::expect(equal(result, expected) == true,
"Generated range does not match expected result!");
},
"Range_TestReverseRangeStep", []
{
auto expected = { 10, 8, 6, 4, 2 };
std::vector< int > result;
for (auto const i : reverse_range(10, 0, -2))
{
result.emplace_back(i);
}
test::expect(equal(result, expected) == true,
"Generated range does not match expected result!");
},
"Range_TestGetIntegralType", []
{
bool is_same = std::is_same< int, detail::get_integral_type_t< int > >::value;
test::expect(is_same == true, "Types not identical for int case");
enum class SomeType : unsigned int { };
is_same = std::is_same< unsigned int, detail::get_integral_type_t< SomeType > >::value;
test::expect(is_same == true, "Could not convert enum to underlying type");
}
};
#include <vector>
#include <chrono>
int main(int argc, char* argv[])
{
if(test::run_tests(tests))
{
constexpr auto num_elements = 1000000;
{
std::vector< int > range_vector(num_elements);
auto start = std::chrono::high_resolution_clock::now();
for(auto const i: range(0, num_elements))
{
range_vector[i] = i;
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration< double > diff = end - start;
std::cout << "Range for loop: " << diff.count() << "s\n";
}
{
std::vector< int > for_vector(num_elements);
auto start = std::chrono::high_resolution_clock::now();
for(int i = 0; i < num_elements; i++)
{
for_vector[i] = i;
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration< double > diff = end - start;
std::cout << "Standard for loop: " << diff.count() << "s\n";
}
}
return 0;
}