Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

All tests now pass under latest MSVC #47

Merged
merged 6 commits into from
Oct 1, 2018
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions combinations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,11 @@ class iter::impl::Combinator {
if (!(dumb_next(*iter, dist) != get_end(*container_p_))) {
if ((iter + 1) != indices_.get().rend()) {
size_t inc = 1;
for (auto down = iter; down != indices_.get().rbegin() - 1;
--down) {
for (auto down = iter; ; --down) {
(*down) = dumb_next(*(iter + 1), 1 + inc);
++inc;
if (down == indices_.get().rbegin())
break;
}
} else {
steps_ = COMPLETE;
Expand Down
5 changes: 3 additions & 2 deletions combinations_with_replacement.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,10 @@ class iter::impl::CombinatorWithReplacement {
++(*iter);
if (!(*iter != get_end(*container_p_))) {
if ((iter + 1) != indices_.get().rend()) {
for (auto down = iter; down != indices_.get().rbegin() - 1;
--down) {
for (auto down = iter; ; --down) {
(*down) = dumb_next(*(iter + 1));
if (down == indices_.get().rbegin())
break;
}
} else {
steps_ = COMPLETE;
Expand Down
22 changes: 22 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Note that some examples currently use boost.optional which we do no not search for in this file.
# You might have to use the "keep going" option to continue building on errors if boost.optional is not in the include path.
# For example, building with MSVC (from an examples/buildMsvc directory):
# set CXX=cl.exe
# cmake .. -G Ninja
# cmake --build . -- -k99

cmake_minimum_required(VERSION 3.8)
project(cppitertools_examples CXX)
set (CMAKE_CXX_STANDARD 17)

include_directories(
..
)

file(GLOB _examples_files "*_examples.cpp")

foreach(_file_cpp ${_examples_files})
get_filename_component(_name_cpp "${_file_cpp}" NAME)
get_filename_component(_name_without_extension "${_name_cpp}" NAME_WE)
add_executable(${_name_without_extension} ${_file_cpp})
endforeach()
1 change: 1 addition & 0 deletions examples/filterfalse_examples.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <filterfalse.hpp>

#include <vector>
#include <string>
#include <iostream>

bool greater_than_four(int i) {
Expand Down
6 changes: 3 additions & 3 deletions internal/iterator_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ namespace iter {

template <typename Container>
using IteratorWrapper = typename IteratorWrapperImplType<Container,
std::is_same<impl::iterator_type<Container>,
impl::iterator_end_type<Container>>{}>::type;
std::is_same_v<impl::iterator_type<Container>,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this actually required for msvc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is. I should report the bug in msvc. It gives an "error C2512: 'std::is_same<Ts,Ts>': no appropriate default constructor available". There is a problem with the constexpr contructor when used in a variadic template. But anyway, since C++17, I would say that _v template variables are the preferred way, thus this modification follows C++17.

impl::iterator_end_type<Container>>>::type;
}
}

template <typename SubIter, typename SubEnd>
class iter::impl::IteratorWrapperImpl {
private:
static_assert(!std::is_same<SubIter, SubEnd>{});
static_assert(!std::is_same_v<SubIter, SubEnd>);
SubIter& sub_iter() {
auto* sub = std::get_if<SubIter>(&sub_iter_or_end_);
assert(sub);
Expand Down
4 changes: 2 additions & 2 deletions internal/iteratoriterator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ namespace iter {
return ret;
}

auto operator*() -> decltype(**sub_iter) {
auto operator*() const -> decltype(**sub_iter) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This places a greater requirement on the sub_iter to have a const operator*()

Copy link
Contributor Author

@frboyer frboyer Sep 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If sub_iter is supposed to be an InputIterator, then it must have a const version of operator*. In C++17 standard:
[iterator.requirements.general]¶13 "In the following sections, a and b denote values of type X or const X"; then Table 95 "Input iterator requirements [...]" contains expression "* a".
Thus operator* must work on a const X.

If you think IteratorIterator should support also iterators that do not obey to the InputIterator requirements, both a const and non const version of the operators could be defined.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The library has always supported things as long as they have some operator*, so I think to keep that the same the way would be to add a const overload instead of replacing the nonconst. Do that and I'll merge

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While trying to add a test case for non-const operator* (i.e. we should not add code unless there is a test that requires that code), I realized that IteratorIterator requires that TopIter is a RandomAccessIterator (requires the random_access_iterator_tag; there is a static_assert for that). Thus unless a class lies about being such an iterator, it must support const operator*. (Note that IteratorIterator also requires a const operator[], which I forgot in that version; I will correct that.)

Simple duplication of current operators *, -> and [], with const and non-const versions, would support an iterator having both the const and non-const versions (and also a standard iterator having only the const version). To do the tests requires to define a new special iterator, as vector::iterator does not have non-const versions.

To support non standard conforming iterators, without the const version, would require to remove the decltype return type of the operators, or use SFINAE, on the const version in IteratorIterator. Is this what you would like?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah you're right I forgot about that requirement. In that case I think we might be fine as is with just const versions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so I added tests to verify that IteratorIterator operators are all working as specified by RandomAccessIterator in the standard, for both const and non-const iterators (having only const versions of *, -> and []). I passed each line of the requirements tables, I hope I did not miss anything (but I only test method results, I do not test the types).

I corrected the operator[] to be also const (as mentioned in my previous comment), to pass the RandomAccessIterator tests.

Those tests showed me that a weird operator- was not covered; in fact it was an incorrect operator where an expression like (2 - it) (subtracting an iterator from an integer) would give (it - 2). I thus simply removed that method.

return **this->sub_iter;
}

auto operator-> () -> decltype(*sub_iter) {
auto operator-> () const -> decltype(*sub_iter) {
return *this->sub_iter;
}

Expand Down
4 changes: 2 additions & 2 deletions internal/iterbase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ namespace iter {
using AsConst = decltype(std::as_const(std::declval<T&>()));

// iterator_type<C> is the type of C's iterator
template <typename Container>
using iterator_type = decltype(get_begin(std::declval<Container&>()));
template <typename T> //TODO: See bug https://developercommunity.visualstudio.com/content/problem/252157/sfinae-error-depends-on-name-of-template-parameter.html for why we use T instead of Container. Should be changed back to Container when that bug is fixed in MSVC.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given that this is fixed in 16.0 I'm not sure how long this is worth keeping around

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the same. But that modification was required to verify now that everything is working on msvc. I'm not sure when 16.0 will be released; current preview is 15.9 pre 2.

using iterator_type = decltype(get_begin(std::declval<T&>()));

// iterator_type<C> is the type of C's iterator
template <typename Container>
Expand Down
9 changes: 3 additions & 6 deletions product.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,9 @@ class iter::impl::Productor {
template <typename T, template <typename> class IT,
template <typename> class TD>
bool operator!=(const IteratorTempl<T, IT, TD>& other) const {
if (sizeof...(Is) == 0) return false;

bool results[] = {
true, (std::get<Is>(iters_) != std::get<Is>(other.iters_))...};
return std::all_of(
get_begin(results), get_end(results), [](bool b) { return b; });
if constexpr (sizeof...(Is) == 0) return false;
else
return (... && (std::get<Is>(iters_) != std::get<Is>(other.iters_)));
}

template <typename T, template <typename> class IT,
Expand Down
4 changes: 2 additions & 2 deletions reversed.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ namespace iter {
template <typename Container>
using ReverseIteratorWrapper =
typename ReverseIteratorWrapperImplType<Container,
std::is_same<impl::reverse_iterator_type<Container>,
std::is_same_v<impl::reverse_iterator_type<Container>,
impl::
reverse_iterator_end_type<Container>>{}>::
reverse_iterator_end_type<Container>>>::
type;

template <typename Container>
Expand Down
2 changes: 1 addition & 1 deletion starmap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class iter::impl::TupleStarMapper {
class IteratorData {
public:
template <std::size_t Idx>
static decltype(auto) get_and_call_with_tuple(Func& f, TupTypeT& t) {
static auto get_and_call_with_tuple(Func& f, TupTypeT& t) -> decltype(std::apply(f, std::get<Idx>(t))) { //TODO: Remove duplicated expression in decltype, using decltype(auto) as return type, when all compilers correctly deduce type (i.e. MSVC cl 19.15 does not do it).
return std::apply(f, std::get<Idx>(t));
}

Expand Down
32 changes: 32 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Note that some examples currently use boost.optional which we do no not search for in this file.
# You might have to use the "keep going" option to continue building on errors if boost.optional is not in the include path.
# For example, building with MSVC (from a test/buildMsvc directory):
# set CXX=cl.exe
# cmake .. -G Ninja
# cmake --build . -- -k99

cmake_minimum_required(VERSION 3.8)
project(cppitertools_tests CXX)
set (CMAKE_CXX_STANDARD 17)

include_directories(
..
)

include(CheckIncludeFileCXX)
set(CMAKE_REQUIRED_INCLUDES ${PROJECT_SOURCE_DIR})
CHECK_INCLUDE_FILE_CXX(catch.hpp _has_catch)
if(NOT "${_has_catch}")
message("WARNING: catch.hpp not found, run ./download_catch.sh from test/ directory first")
endif()

file(GLOB test_sources RELATIVE ${PROJECT_SOURCE_DIR} "test_*.cpp")
list(REMOVE_ITEM test_sources test_main.cpp)
add_library(test_main OBJECT test_main.cpp)

foreach(_source_cpp ${test_sources})
get_filename_component(_name_without_extension "${_source_cpp}" NAME_WE)
add_executable(${_name_without_extension} ${_source_cpp} $<TARGET_OBJECTS:test_main>)
endforeach()

add_executable(test_all ${test_sources} $<TARGET_OBJECTS:test_main>)
2 changes: 1 addition & 1 deletion test/test_accumulate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ TEST_CASE("accumulate: intermidate type need not be default constructible",
"[accumulate]") {
std::vector<Integer> v = {{2}, {3}, {10}};
auto a = accumulate(v, std::plus<Integer>{});
std::begin(a);
(void)std::begin(a);
}

TEST_CASE("accumulate: binds reference when it should", "[accumulate]") {
Expand Down
4 changes: 2 additions & 2 deletions test/test_mixed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class MyUnMovable {
constexpr int get_val() const {
return val;
}
void set_val(int val) {
this->val = val;
void set_val(int new_val) {
this->val = new_val;
}

bool operator==(const MyUnMovable& other) const {
Expand Down
13 changes: 7 additions & 6 deletions test/test_sorted.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ TEST_CASE("sorted: const iteration", "[sorted][const]") {
REQUIRE(v == vc);
}

//FIXME: This test currently fails (STL assertion fails on MSVC with debug library, simple test failure on gcc). The problem is 'sorted' will sort twice, once for non-const and once for const container; the resulting iterators are thus not on the same container (violating domain of == as specified in C++17 [forward.iterators]�2). Remove [!hide] tag when fixed.
TEST_CASE("sorted: const iterators can be compared to non-const iterators",
"[sorted][const]") {
auto s = sorted(Vec{});
"[sorted][const][!hide]") {
auto s = sorted(Vec{1});
const auto& cs = s;
(void)(std::begin(s) == std::end(cs));
REQUIRE(std::begin(s) == std::begin(cs));
}

TEST_CASE("sorted: can modify elements through sorted", "[sorted]") {
Expand All @@ -67,12 +68,12 @@ char inc_vowels(char c) {
}

TEST_CASE("sorted: Works with different begin and end types", "[sorted]") {
using Vec = std::vector<char>;
using VecC = std::vector<char>;
CharRange cr{'g'};
auto s =
sorted(cr, [](char x, char y) { return inc_vowels(x) < inc_vowels(y); });
Vec v(s.begin(), s.end());
Vec vc{'b', 'c', 'd', 'f', 'a', 'e'};
VecC v(s.begin(), s.end());
VecC vc{'b', 'c', 'd', 'f', 'a', 'e'};
REQUIRE(v == vc);
}

Expand Down
12 changes: 6 additions & 6 deletions test/test_starmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ namespace {
return a + b + c;
}

int operator()(int a, int b) {
return a + b;
int operator()(double a, int b) {
return int(a + b);
}

int operator()(int a) {
Expand All @@ -39,7 +39,7 @@ namespace {

TEST_CASE("starmap: works with function pointer and lambda", "[starmap]") {
using Vec = const std::vector<int>;
const std::vector<std::pair<double, int>> v1 = {{1l, 2}, {3l, 11}, {6l, 7}};
const std::vector<std::pair<long, int>> v1 = {{1l, 2}, {3l, 11}, {6l, 7}};
Vec vc = {2l, 33l, 42l};

std::vector<int> v;
Expand Down Expand Up @@ -74,7 +74,7 @@ TEST_CASE("starmap: works with pointer to member function", "[starmap]") {

TEST_CASE("starmap: vector of pairs const iteration", "[starmap][const]") {
using Vec = const std::vector<int>;
const std::vector<std::pair<double, int>> v1 = {{1l, 2}, {3l, 11}, {6l, 7}};
const std::vector<std::pair<double, int>> v1 = {{1.0, 2}, {3.0, 11}, {6.0, 7}};

const auto sm = starmap(Callable{}, v1);
std::vector<int> v(std::begin(sm), std::end(sm));
Expand Down Expand Up @@ -121,7 +121,7 @@ TEST_CASE(

TEST_CASE("starmap: list of tuples", "[starmap]") {
using Vec = const std::vector<std::string>;
using T = std::tuple<std::string, int, double>;
using T = std::tuple<std::string, int, char>;
std::list<T> li = {T{"hey", 42, 'a'}, T{"there", 3, 'b'}, T{"yall", 5, 'c'}};

auto sm = starmap(g, li);
Expand Down Expand Up @@ -172,7 +172,7 @@ TEST_CASE("starmap: moves rvalues, binds to lvalues", "[starmap]") {
TEST_CASE("starmap: iterator meets requirements", "[starmap]") {
std::string s{};
const std::vector<std::pair<double, int>> v1;
auto sm = starmap([](long a, int b) { return a * b; }, v1);
auto sm = starmap([](double a, int b) { return a * b; }, v1);
REQUIRE(itertest::IsIterator<decltype(std::begin(sm))>::value);
}

Expand Down
6 changes: 3 additions & 3 deletions test/test_unique_everseen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ TEST_CASE(
TEST_CASE("unique everseen: Works with different begin and end types",
"[unique_everseen]") {
CharRange cr{'d'};
using Vec = std::vector<char>;
using VecC = std::vector<char>;
auto ue = unique_everseen(cr);
Vec v(ue.begin(), ue.end());
Vec vc{'a', 'b', 'c'};
VecC v(ue.begin(), ue.end());
VecC vc{'a', 'b', 'c'};
REQUIRE(v == vc);
}

Expand Down
6 changes: 3 additions & 3 deletions test/test_unique_justseen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ TEST_CASE("unique justseen: some repeating values", "[unique_justseen]") {
TEST_CASE("unique justseen: Works with different begin and end types",
"[unique_justseen]") {
CharRange cr{'d'};
using Vec = std::vector<char>;
using VecC = std::vector<char>;
auto uj = unique_justseen(cr);
Vec v(uj.begin(), uj.end());
Vec vc{'a', 'b', 'c'};
VecC v(uj.begin(), uj.end());
VecC vc{'a', 'b', 'c'};
REQUIRE(v == vc);
}

Expand Down
4 changes: 2 additions & 2 deletions test/test_zip_longest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ TEST_CASE("zip_longest: const iterators can be compared to non-const iterators",
"[zip_longest][const]") {
auto zl = zip_longest(std::vector<int>{});
const auto& czl = zl;
std::begin(zl);
std::begin(czl);
(void)std::begin(zl);
(void)std::begin(czl);
(void)(std::begin(zl) == std::end(czl));
}

Expand Down
9 changes: 3 additions & 6 deletions zip.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,9 @@ class iter::impl::Zipped {
template <typename T, template <typename> class IT,
template <typename> class TD>
bool operator!=(const Iterator<T, IT, TD>& other) const {
if (sizeof...(Is) == 0) return false;

bool results[] = {
true, (std::get<Is>(iters_) != std::get<Is>(other.iters_))...};
return std::all_of(
get_begin(results), get_end(results), [](bool b) { return b; });
if constexpr (sizeof...(Is) == 0) return false;
else
return (... && (std::get<Is>(iters_) != std::get<Is>(other.iters_)));
}

template <typename T, template <typename> class IT,
Expand Down
7 changes: 1 addition & 6 deletions zip_longest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,7 @@ class iter::impl::ZippedLongest {
template <typename T, template <typename> class TT,
template <std::size_t, typename> class TU>
bool operator!=(const Iterator<T, TT, TU>& other) const {
if (sizeof...(Is) == 0) return false;

bool results[] = {
false, (std::get<Is>(iters_) != std::get<Is>(other.iters_))...};
return std::any_of(
get_begin(results), get_end(results), [](bool b) { return b; });
return (... || (std::get<Is>(iters_) != std::get<Is>(other.iters_)));
}

template <typename T, template <typename> class TT,
Expand Down