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

fixed problem related to compiling with MSVC with language version c++20 #71

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
.DS_Store
build/
install/

.vs
6 changes: 6 additions & 0 deletions Folder.DotSettings.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=54b5824a_002D34df_002D40b0_002D9181_002Df719efef3db5/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="A pipeline with 2 different iterator types as source" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;TestAncestor&gt;&#xD;
&lt;TestId&gt;Catch::DB94C55F-6B9A-4AAE-80D7-29FD8153E6B5::::A pipeline with 2 different iterator types as source&lt;/TestId&gt;&#xD;
&lt;/TestAncestor&gt;&#xD;
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>
6 changes: 3 additions & 3 deletions include/pipes/adjacent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ namespace pipes

if (second != end(range))
{
second++;
++second;
}

while (second != end(range))
{
send(*first, *second, pipeline);

first++;
second++;
++first;
++second;
}
}
} // namespace pipes
Expand Down
47 changes: 47 additions & 0 deletions include/pipes/buffered_transform.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#ifndef PIPES_BUFFERED_TRANSFORM_HPP
#define PIPES_BUFFERED_TRANSFORM_HPP

#include "pipes/operator.hpp"

#include "pipes/helpers/assignable.hpp"
#include "pipes/base.hpp"

namespace pipes
{
template<typename Buffered, typename Function>
class buffered_transform_pipe : public pipe_base
{
public:
template<typename... Values, typename TailPipeline>
void onReceive(Values&&... values, TailPipeline&& tailPipeline)
{
#if defined(_MSC_VER) && _MSVC_LANG >= 201703L
send(std::invoke(Fn_.get(), FWD(values)..., buffered_), tailPipeline);
#else
send(details::invoke(Fn_.get(), FWD(values)..., buffered_), tailPipeline);
#endif
}

template <typename BufferedTypeFwd, typename FunctionFwd>
explicit buffered_transform_pipe(BufferedTypeFwd&& buffered, FunctionFwd&& binary_fn) :
buffered_(std::forward<BufferedTypeFwd>(buffered)),
Fn_(std::forward<FunctionFwd>(binary_fn)) {}

private:
Buffered buffered_;
detail::assignable<Function> Fn_;
};

template<typename InitType, typename Function>
auto buffered_transform(InitType&& init, Function&& binary_fn)
{
return buffered_transform_pipe<std::decay_t<InitType>, std::decay_t<Function>>{
std::forward<InitType>(init),
std::forward<Function>(binary_fn)
};
}

} // namespace pipes


#endif /* PIPES_BUFFERED_TRANSFORM_HPP */
2 changes: 1 addition & 1 deletion include/pipes/dev_null.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ namespace pipes
}
};

} // namespace fluent
} // namespace pipes

#endif /* PIPES_DEAD_END_ITERATOR_HPP */
31 changes: 26 additions & 5 deletions include/pipes/helpers/invoke.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,44 @@ namespace pipes
{
namespace detail
{
#if defined(_MSC_VER) && _MSVC_LANG >= 201703L
template<typename Functor, typename... Args>
typename std::enable_if<
std::is_member_pointer<typename std::decay<Functor>::type>::value,
typename std::result_of<Functor&&(Args&&...)>::type
std::is_member_pointer<typename std::decay<Functor>::type>::value,
typename std::invoke_result<Functor && (Args&&...)>::type
>::type invoke(Functor&& f, Args&&... args)
{
return std::mem_fn(f)(std::forward<Args>(args)...);
}

template<typename Functor, typename... Args>
typename std::enable_if<
!std::is_member_pointer<typename std::decay<Functor>::type>::value,
typename std::result_of<Functor&&(Args&&...)>::type
!std::is_member_pointer<typename std::decay<Functor>::type>::value,
typename std::invoke_result<Functor && (Args&&...)>::type
>::type invoke(Functor&& f, Args&&... args)
{
return std::forward<Functor>(f)(std::forward<Args>(args)...);
}
#else
template<typename Functor, typename... Args>
typename std::enable_if<
std::is_member_pointer<typename std::decay<Functor>::type>::value,
typename std::result_of<Functor && (Args&&...)>::type
>::type invoke(Functor&& f, Args&&... args)
{
return std::mem_fn(f)(std::forward<Args>(args)...);
}

template<typename Functor, typename... Args>
typename std::enable_if<
!std::is_member_pointer<typename std::decay<Functor>::type>::value,
typename std::result_of<Functor && (Args&&...)>::type
>::type invoke(Functor&& f, Args&&... args)
{
return std::forward<Functor>(f)(std::forward<Args>(args)...);
}
#endif

}
}

Expand Down
7 changes: 5 additions & 2 deletions include/pipes/impl/pipes_assembly.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ namespace pipes
{
headPipe_.template onReceive<Ts...>(FWD(inputs)..., tailPipeline_);
}

generic_pipeline(HeadPipe headPipe, TailPipeline tailPipeline) : headPipe_(headPipe), tailPipeline_(tailPipeline) {}

template <typename HeadPipeFwd, typename TailPipelineFwd>
generic_pipeline(HeadPipeFwd&& headPipe, TailPipelineFwd&& tailPipeline)
: headPipe_(std::forward<HeadPipeFwd>(headPipe)), tailPipeline_(std::forward<TailPipelineFwd>(tailPipeline))
{}

private:
HeadPipe headPipe_;
Expand Down
34 changes: 22 additions & 12 deletions include/pipes/operator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,51 @@ namespace pipes
{

// range >>= pipeline (rvalue ranges)

template<typename Range, typename Pipeline, detail::IsARange<Range> = true, detail::IsAPipeline<Pipeline> = true>
std::enable_if_t<!std::is_lvalue_reference<Range>::value> operator>>=(Range&& range, Pipeline&& pipeline)
{
using std::begin;
using std::end;
std::copy(std::make_move_iterator(begin(range)), std::make_move_iterator(end(range)), pipeline);
using std::begin; using std::end;
auto beg_it = begin(range);
auto end_it = end(range);
while (beg_it != end_it)
{
*pipeline = std::move(*beg_it);
(void) ++beg_it;
}
}

// range >>= pipeline (lvalue ranges)

template<typename Range, typename Pipeline, detail::IsARange<Range> = true, detail::IsAPipeline<Pipeline> = true>
std::enable_if_t<std::is_lvalue_reference<Range>::value> operator>>=(Range&& range, Pipeline&& pipeline)
{
using std::begin;
using std::end;
std::copy(begin(range), end(range), pipeline);
using std::begin; using std::end;
auto beg_it = begin(range);
auto end_it = end(range);
while (beg_it != end_it)
{
*pipeline = *beg_it;
(void) ++beg_it;
}
}

// pipe >>= pipe

template<typename Pipe1, typename Pipe2, detail::IsAPipe<Pipe1> = true, detail::IsAPipe<Pipe2> = true>
auto operator>>=(Pipe1&& pipe1, Pipe2&& pipe2)
{
return detail::CompositePipe<std::decay_t<Pipe1>, std::decay_t<Pipe2>>(FWD(pipe1), FWD(pipe2));
}

// pipe >>= pipeline

template<typename Pipe, typename Pipeline, detail::IsAPipe<Pipe> = true, detail::IsAPipeline<Pipeline> = true>
auto operator>>=(Pipe&& pipe, Pipeline&& pipeline)
{
return make_generic_pipeline(pipe, pipeline);
return make_generic_pipeline(std::forward<Pipe>(pipe), std::forward<Pipeline>(pipeline));
}

} // namespace pipes

#endif /* OPERATOR_HPP */
6 changes: 5 additions & 1 deletion include/pipes/transform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ namespace pipes
template<typename... Values, typename TailPipeline>
void onReceive(Values&&... values, TailPipeline&& tailPipeline)
{
send(detail::invoke(function_.get(), FWD(values)...), tailPipeline);
#if defined(_MSC_VER) && _MSVC_LANG >= 201703L
send(std::invoke(function_.get(), FWD(values)...), tailPipeline);
#else
send(details::invoke(function_.get(), FWD(values)...), tailPipeline);
#endif
}

explicit transform_pipe(Function function) : function_(function){}
Expand Down
11 changes: 11 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_executable(pipes_test
main.cpp
adjacent.cpp
buffered_transform.cpp
cartesian_product.cpp
combinations.cpp
dev_null.cpp
Expand Down Expand Up @@ -36,6 +37,16 @@ target_include_directories(pipes_test PRIVATE

target_compile_features(pipes_test PRIVATE cxx_std_14)

string(REPLACE "/W3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "/O2" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "/W3" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
string(REPLACE "/O2" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
string(REPLACE "/W3" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
string(REPLACE "/O2" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
string(REPLACE "/O2" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
string(REPLACE "/W3" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
string(REPLACE "/W3" "" CMAKE_CXX_FLAGS_MINSIZE "${CMAKE_CXX_FLAGS_MINSIZE}")
string(REPLACE "/O2" "" CMAKE_CXX_FLAGS_MINSIZE "${CMAKE_CXX_FLAGS_MINSIZE}")
target_compile_options(pipes_test
PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/std:c++17>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
Expand Down
140 changes: 140 additions & 0 deletions tests/buffered_transform.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include "catch.hpp"
#include "pipes/helpers/FWD.hpp"
#include "pipes/buffered_transform.hpp"
#include "pipes/push_back.hpp"
#include "pipes/override.hpp"

#include <algorithm>
#include <memory>
#include <vector>

TEST_CASE("buffered_transform")
{
auto add = 1;
std::vector<int> input = { 1, 2, 3, 4, 5 };
std::vector<int> expected = { add + 2, add + 4, add + 6, add + 8, add + 10 };
std::vector<int> results;

SECTION("input from range")
{
input >>= pipes::buffered_transform(add, [](int i, int buf) { return buf + i * 2; }) >>= pipes::push_back(results);
REQUIRE(results == expected);
}

SECTION("input from STL algorithm")
{
std::copy(begin(input), end(input), pipes::buffered_transform(add, [](int i, int buf) { return buf + i * 2; }) >>= pipes::push_back(results));
REQUIRE(results == expected);
}
}
TEST_CASE("buffered_transform can use pointer to member functions")
{
struct S
{
int get_42(int i) const { return i + 41; }
};

auto const input = std::vector<S>(10);
auto const expected = std::vector<int>(10, 42);
std::vector<int> results;

input >>= pipes::buffered_transform(1, &S::get_42)
>>= pipes::push_back(results);

REQUIRE(expected == results);
}

TEST_CASE("buffered_transform's iterator category should be std::output_iterator_tag")
{
auto const times2 = pipes::buffered_transform(1, [](int i, int buf) { return buf + i * 2; });
std::vector<int> output;
static_assert(std::is_same<decltype(times2 >>= pipes::push_back(output))::iterator_category,
std::output_iterator_tag>::value,
"iterator category should be std::output_iterator_tag");
}

TEST_CASE("buffered_transform can override existing contents")
{
auto add = 1;
std::vector<int> input = { 1, 2, 3, 4, 5 };
std::vector<int> expected = { add + 2, add + 4, add + 6, add + 8, add + 10 };

auto const times2 = pipes::buffered_transform(add, [](int i, int buf) { return i * 2 + buf; });

std::vector<int> results = { 0, 0, 0, 0, 0 };
std::copy(begin(input), end(input), times2 >>= pipes::override(results));

REQUIRE(results == expected);
}

TEST_CASE("buffered_transform operator=")
{
std::vector<int> results1, results2;
auto func = [](int i, int buf) { return buf + i * 2; };
auto pipeline1 = pipes::buffered_transform(1, func) >>= pipes::push_back(results1);
auto pipeline2 = pipes::buffered_transform(1, func) >>= pipes::push_back(results2);

pipeline2 = pipeline1;
send(1, pipeline2);
REQUIRE(results1.size() == 1);
REQUIRE(results2.size() == 0);
}

TEST_CASE("buffered_transform move_only types")
{
// Can't use initializer list since it needs to copy
std::vector<std::unique_ptr<int>> input;
input.push_back(std::make_unique<int>(1));
input.push_back(std::make_unique<int>(2));

std::vector<std::unique_ptr<int>> result;

std::move(input) >>= pipes::buffered_transform(1, [](auto&& ptr, int) -> decltype(auto) { return std::move(ptr); })
>>= pipes::push_back(result);

// unique_ptr op == compares ptr not value
REQUIRE(result.size() == 2);
REQUIRE(*result[0] == 1);
REQUIRE(*result[1] == 2);

// input elements were moved from
REQUIRE(input.size() == 2);
REQUIRE(input[0] == nullptr);
REQUIRE(input[1] == nullptr);
}

TEST_CASE("buffered_transform move into buffer from r-value")
{
struct Buf
{
bool Moved{}, Copied{}, MovedFrom{};
Buf() = default;
Buf(const Buf&) noexcept : Copied(true)
{
}
Buf(Buf&& b) noexcept: Moved(true)
{
b.MovedFrom = true;
}
};

std::vector<int> input = {1, 2, 3, 4, 5};
std::vector<int> expected = { 2, 4, 6, 8, 10 };

std::vector<int> result;

Buf b;

std::move(input) >>= pipes::buffered_transform(std::move(b), [&](int ele, Buf& buf) mutable -> decltype(auto)
{
REQUIRE(buf.MovedFrom == false);
REQUIRE(buf.Copied == false);
REQUIRE(buf.Moved == true);
return ele * 2;
})
>>= pipes::push_back(result);

REQUIRE(b.MovedFrom == true);
REQUIRE(b.Copied == false);
REQUIRE(b.Moved == false);
}
Loading