Commit 1dfc219f authored by Edwin Carlinet's avatar Edwin Carlinet

Clang 10 && GCC 10 Support

See merge request !99
parents fb239cb3 2d907e47
Pipeline #19765 passed with stages
in 36 minutes and 51 seconds
......@@ -11,11 +11,16 @@ cache:
variables:
FEDORA_RAWHIDE: "${CI_REGISTRY}/olena/pylene-dockers/fedora-rawhide"
FEDORA_31: "${CI_REGISTRY}/olena/pylene-dockers/fedora-31"
FEDORA_32: "${CI_REGISTRY}/olena/pylene-dockers/fedora-32"
PACKAGE_NAME: "pylene" # Conan package name
PACKAGE_TAG: "stable" # Conan tag
PACKAGE_VERSION: "head" # Version to build
CMAKE_BUILD_PARALLEL_LEVEL: 6
CONAN_USER: "lrde"
CONAN_PROFILE: "gcc-9"
ASAN: "OFF"
MSAN: "OFF"
UBSAN: "OFF"
before_script:
- conan config set storage.path="${CI_PROJECT_DIR}/.cache/conan/data"
......@@ -45,6 +50,7 @@ windows-debug:
- if: $CI_MERGE_REQUEST_ID
- if: $CI_COMMIT_BRANCH == "master"
- when: manual
allow_failure: true
variables:
VCVAR2019: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat'
ARCH: x64
......@@ -53,7 +59,7 @@ windows-debug:
stage: build
script:
- mkdir build && cd build
- conan install .. -pr gcc-9 --build missing -e CXX=g++ -e CC=gcc
- conan install .. --build missing -e CXXFLAGS="" -e CCFLAGS="" -pr $CONAN_PROFILE
- cmake -G Ninja .. -DCMAKE_BUILD_TYPE=$PYLENE_CONFIGURATION -DSANITIZE_ADDRESS=$ASAN -DSANITIZE_MEMORY=$MSAN -DSANITIZE_UNDEFINED=$UBSAN
- cmake --build . --target Pylene
- cmake --build . --target build-tests
......@@ -77,9 +83,8 @@ distcheck-linux-gcc9-release:
PYLENE_CONFIGURATION: "Release"
CXX: "g++"
CC: "gcc"
ASAN: "OFF"
MSAN: "OFF"
UBSAN: "OFF"
CONAN_PROFILE: "gcc-9"
distcheck-linux-gcc9-debug-asan-ubsan:
<<: *distcheck-linux-base
......@@ -91,6 +96,54 @@ distcheck-linux-gcc9-debug-asan-ubsan:
ASAN: "ON"
MSAN: "OFF"
UBSAN: "ON"
CONAN_PROFILE: "gcc-9"
distcheck-linux-clang10-release:
<<: *distcheck-linux-base
image: ${FEDORA_32}
variables:
PYLENE_CONFIGURATION: "Release"
CXX: "clang++"
CC: "clang"
CONAN_PROFILE: "clang-10"
rules:
- if: $CI_MERGE_REQUEST_ID
- if: $CI_COMMIT_BRANCH == "master"
distcheck-linux-clang10-debug:
<<: *distcheck-linux-base
image: ${FEDORA_32}
variables:
PYLENE_CONFIGURATION: "Debug"
CXX: "clang++"
CC: "clang"
CCFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CXXFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CONAN_PROFILE: "clang-10"
distcheck-linux-gcc10-release:
<<: *distcheck-linux-base
image: ${FEDORA_32}
variables:
PYLENE_CONFIGURATION: "Release"
CXX: "g++"
CC: "gcc"
CONAN_PROFILE: "gcc-10"
rules:
- if: $CI_MERGE_REQUEST_ID
- if: $CI_COMMIT_BRANCH == "master"
distcheck-linux-gcc10-debug:
<<: *distcheck-linux-base
image: ${FEDORA_32}
variables:
PYLENE_CONFIGURATION: "Debug"
CXX: "g++"
CC: "gcc"
CCFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CXXFLAGS: -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CONAN_PROFILE: "gcc-10"
distcheck-linux-coverage:
......@@ -115,7 +168,6 @@ distcheck-linux-coverage:
rules:
- if: $CI_MERGE_REQUEST_ID
- if: $CI_COMMIT_BRANCH == "master"
- when: manual
#########
# Bench #
......@@ -125,7 +177,7 @@ distcheck-linux-coverage:
stage: bench
script:
- mkdir build && cd build
- conan install .. -pr gcc-9 --build missing -e CXX=g++ -e CC=gcc
- conan install .. -pr gcc-9 --build missing
- cmake -G Ninja .. -DCMAKE_BUILD_TYPE=Release
- cmake --build . --target fetch-external-data
- cmake --build . --target build-bench
......@@ -158,7 +210,7 @@ distbench-linux-gcc9-release:
stage: build
script:
- mkdir build && cd build
- conan install -u .. -pr gcc-9 --build missing -e CXX=g++ -e CC=gcc
- conan install -u .. -pr gcc-9 --build missing
- cmake -G Ninja .. -DCMAKE_BUILD_TYPE=Release
- cmake --build . --target build-doc
- mkdir ../public && mv doc/sphinx/* ../public/
......
......@@ -20,12 +20,11 @@ Link to the [C++ Documentation](http://olena.pages.lrde.epita.fr/pylene/)
# Requirements
Pylene is developed in modern C++. You need a modern C++ compatible compiler:
* GCC 9 ~~GCC 10~~ (in progress)
* ~~Clang 10~~ (in progress)
* GCC 9 GCC 10
* Clang 10
* Microsoft Visual Studio 2019
This project relies on:
......
include(CTest)
find_package(Threads REQUIRED)
find_package(GTest MODULE REQUIRED)
find_package(GTest REQUIRED)
add_executable(UTBenchImpl_Neighborhood neighborhood.cpp)
target_link_libraries(UTBenchImpl_Neighborhood PRIVATE BenchImpl Fixtures::ImagePath Fixtures::ImageCompare Pylene::Pylene GTest::GTest GTest::Main ${FreeImage_LIBRARIES})
target_link_libraries(UTBenchImpl_Neighborhood PRIVATE BenchImpl Fixtures::ImagePath Fixtures::ImageCompare Pylene::Pylene GTest::GTest ${FreeImage_LIBRARIES})
add_test(NAME UTBenchImpl_Neighborhood COMMAND UTBenchImpl_Neighborhood --gtest_output=xml:UTBenchImpl_Neighborhood.xml WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bench/tests)
......
......@@ -11,19 +11,17 @@ class Pylene(ConanFile):
options = {
"shared": [True, False],
"fPIC": [True, False],
"gtest": [True, False],
"benchmark": [True, False],
"freeimage": [True, False],
"boost": [True, False],
"boost_program_options": [True, False]}
"boost": [True, False]}
default_options = {
"shared": False,
"fPIC": False,
"gtest": False,
"benchmark": False,
"freeimage": False,
"boost": False,
"boost_program_options": False}
"gtest:shared": False
}
generators = [ "cmake", "cmake_paths", "cmake_find_package" ]
exports_sources = ["pylene/*", "cmake/*", "CMakeLists.txt", "LICENSE"]
......@@ -43,17 +41,20 @@ class Pylene(ConanFile):
if self.settings.compiler in ["gcc", "clang"]:
self.cpp_info.cppflags = ["-std=c++20"]
# developer dependancies (to be removed)
def build_requirements(self):
self.build_requires("gtest/[>=1.10]", force_host_context=True)
# Requirements part of the INTERFACE
def requirements(self):
self.requires("range-v3/0.9.1@ericniebler/stable")
self.requires("range-v3/0.10.0@ericniebler/stable")
self.requires("fmt/6.0.0")
if self.options.freeimage:
self.requires("freeimage/3.18.0@dutiona/stable")
if self.options.gtest:
self.requires("gtest/1.8.1")
if self.options.benchmark:
self.requires("benchmark/1.5.0")
......
......@@ -4,7 +4,7 @@ find_package(Boost 1.58 REQUIRED)
find_package(FreeImage REQUIRED)
find_package(TBB)
find_package(range-v3 0.9.1 REQUIRED CONFIG)
find_package(range-v3 0.10.0 REQUIRED CONFIG)
find_package(fmt 6.0 REQUIRED)
set(PYLENE_USE_TBB YES CACHE BOOL "Set to NO to disable use of TBB and parallelization")
......
include(CMakeFindDependencyMacro)
find_dependency(Boost 1.58)
find_dependency(range-v3 0.9.1 CONFIG)
find_dependency(range-v3 0.10.0 CONFIG)
find_dependency(fmt 6.0)
include("${CMAKE_CURRENT_LIST_DIR}/PyleneTargets.cmake")
......@@ -98,8 +98,8 @@ namespace mln
return size(2);
}
using Impl::cursor;
using Impl::backward_cursor;
using typename Impl::cursor;
using typename Impl::backward_cursor;
using Impl::begin_cursor;
using Impl::end_cursor;
using Impl::rbegin_cursor;
......@@ -713,11 +713,11 @@ namespace mln
template <class Impl>
constexpr std::size_t _box<Impl>::size() const noexcept
{
std::size_t sz = 1;
int sz = 1;
for (int k = 0; k < this->dim(); ++k)
sz *= (this->__end(k) - this->__begin(k));
if (sz < 0)
sz = 0;
return 0;
return sz;
}
......
......@@ -13,7 +13,7 @@ namespace mln::canvas
template <class SE, class I, class J, class Self = void>
// This concept check makes an ICE with MSVC
#ifndef _MSC_VER
requires concepts::StructuringElement<SE, image_point_t<I>>&& concepts::Image<I>&& concepts::Image<J>
requires concepts::StructuringElement<SE, image_point_t<I>> && concepts::Image<I> && concepts::Image<J>
#endif
class LocalAlgorithm
{
......@@ -101,6 +101,9 @@ namespace mln::canvas
template <class SE, class I, class J, class Self>
#ifndef _MSC_VER
requires concepts::StructuringElement<SE, image_point_t<I>> && concepts::Image<I> && concepts::Image<J>
#endif
void LocalAlgorithm<SE, I, J, Self>::Execute()
{
mln_entering("LocalAlgorithm::Execute (Non-incremental)");
......
......@@ -20,9 +20,9 @@ namespace mln::concepts
mln::ranges::mdrange<Dom> &&
Point<mln::ranges::mdrange_value_t<Dom>> &&
requires(const Dom cdom, mln::ranges::mdrange_value_t<Dom> p) {
{ cdom.has(p) } -> bool;
{ cdom.empty() } -> bool;
{ cdom.dim() } -> int;
{ cdom.has(p) } -> ::concepts::same_as<bool>;
{ cdom.empty() } -> ::concepts::same_as<bool>;
{ cdom.dim() } -> ::concepts::same_as<int>;
};
......@@ -39,7 +39,6 @@ namespace mln::concepts
concept ShapedDomain =
SizedDomain<Dom> &&
requires(const Dom cdom) {
{ cdom.shape() } -> ::ranges::range_value_t<Dom>;
{ cdom.extents() } -> ::ranges::cpp20::forward_range;
};
......
......@@ -73,7 +73,7 @@ namespace mln::concepts
{ cima.template ch_value<mln::archetypes::Value>() }
-> ::concepts::convertible_to<image_ch_value_t<I, mln::archetypes::Value>>;
{ cima.concretize() } -> ::concepts::convertible_to<image_concrete_t<I>>;
{ cima.domain() } -> image_domain_t<I>;
{ cima.domain() } -> ::concepts::convertible_to<image_domain_t<I>>;
{ ima.pixels() } -> mln::ranges::mdrange;
{ ima.values() } -> mln::ranges::mdrange;
requires ::concepts::convertible_to<mln::ranges::mdrange_value_t<decltype(ima.pixels())>, image_pixel_t<I>>;
......@@ -120,7 +120,11 @@ namespace mln::concepts
} &&
image_indexable_v<I> &&
requires (I ima, image_index_t<I> k) {
{ ima[k] } -> image_reference_t<I>; // For concrete image it returns a const_reference
#if __GNUC__ == 9
{ ima[k] } -> ::concepts::same_as<image_reference_t<I>>&&; // For concrete image it returns a const_reference
#else
{ ima[k] } -> ::concepts::same_as<image_reference_t<I>>; // For concrete image it returns a const_reference
#endif
};
......@@ -133,9 +137,13 @@ namespace mln::concepts
WritableImage<I> &&
IndexableImage<I> &&
requires(I ima, image_index_t<I> k, image_value_t<I> v) {
{ ima[k] = v } -> image_reference_t<I>;
#if __GNUC__ == 9
{ ima[k] = v } -> ::concepts::same_as<image_reference_t<I>>&&;
#else
{ ima[k] = v } -> ::concepts::same_as<image_reference_t<I>>;
#endif
};
} // namespace detail
......@@ -145,10 +153,17 @@ namespace mln::concepts
Image<I> &&
image_accessible_v<I> &&
requires (I ima, image_point_t<I> p) {
{ ima(p) } -> image_reference_t<I>; // For concrete image it returns a const_reference
{ ima.at(p) } -> image_reference_t<I>; // idem
{ ima.new_pixel(p) } -> image_pixel_t<I>; // For concrete image pixel may propagate constness
{ ima.new_pixel_at(p) } -> image_pixel_t<I>; // idem
#if __GNUC__ == 9
{ ima(p) } -> ::concepts::same_as<image_reference_t<I>>&&; // For concrete image it returns a const_reference
{ ima.at(p) } -> ::concepts::same_as<image_reference_t<I>>&&; // idem
{ ima.new_pixel(p) } -> ::concepts::same_as<image_pixel_t<I>>&&; // For concrete image pixel may propagate constness
{ ima.new_pixel_at(p) } -> ::concepts::same_as<image_pixel_t<I>>&&; // idem
#else
{ ima(p) } -> ::concepts::same_as<image_reference_t<I>>; // For concrete image it returns a const_reference
{ ima.at(p) } -> ::concepts::same_as<image_reference_t<I>>; // idem
{ ima.new_pixel(p) } -> ::concepts::same_as<image_pixel_t<I>>; // For concrete image pixel may propagate constness
{ ima.new_pixel_at(p) } -> ::concepts::same_as<image_pixel_t<I>>; // idem
#endif
};
......@@ -161,10 +176,10 @@ namespace mln::concepts
detail::WritableImage<I> &&
AccessibleImage<I> &&
requires(I ima, image_point_t<I> p, image_value_t<I> v) {
{ ima(p) = v } -> image_reference_t<I>;
{ ima.at(p) = v } -> image_reference_t<I>;
{ ima(p) = v };
{ ima.at(p) = v };
};
} // namespace detail
......@@ -174,9 +189,9 @@ namespace mln::concepts
IndexableImage<I> &&
AccessibleImage<I> &&
requires (const I cima, image_index_t<I> k, image_point_t<I> p) {
{ cima.point_at_index(k) } -> image_point_t<I>;
{ cima.index_of_point(p) } -> image_index_t<I>;
{ cima.delta_index(p) } -> image_index_t<I>;
{ cima.point_at_index(k) } -> ::concepts::same_as<image_point_t<I>>;
{ cima.index_of_point(p) } -> ::concepts::same_as<image_index_t<I>>;
{ cima.delta_index(p) } -> ::concepts::same_as<image_index_t<I>>;
};
......@@ -222,8 +237,8 @@ namespace mln::concepts
BidirectionalImage<I> &&
::concepts::derived_from<image_category_t<I>, raw_image_tag> &&
requires (I ima, const I cima, int dim) {
{ ima.data() } -> ::concepts::convertible_to<const image_value_t<I>*>; // data() may be proxied by a view
{ cima.stride(dim) } -> std::ptrdiff_t;
{ ima.data() } -> ::concepts::convertible_to<const image_value_t<I>*>; // data() may be proxied by a view
{ cima.stride(dim) } -> ::concepts::same_as<std::ptrdiff_t>;
};
......@@ -238,7 +253,7 @@ namespace mln::concepts
WritableBidirectionalImage<I> &&
RawImage<I> &&
requires(I ima, image_value_t<I> v) {
{ ima.data() } -> ::concepts::convertible_to<image_value_t<I>*>;
{ ima.data() } -> ::concepts::convertible_to<image_value_t<I>*>;
{ *(ima.data()) = v };
};
......@@ -267,7 +282,7 @@ namespace mln::concepts
} &&
not ::concepts::same_as<mln::extension::none_extension_tag, image_extension_category_t<I>> &&
requires (I ima, image_point_t<I> p) {
{ ima.extension() } -> image_extension_t<I>;
{ ima.extension() } -> ::concepts::same_as<image_extension_t<I>>;
};
......
......@@ -49,6 +49,10 @@ namespace mln
template <typename T, template <typename> class Concept>
using is_a = typename internal::is_a_helper<T, Concept>::type;
template <typename T, template <typename> class Concept>
constexpr inline bool is_a_v = is_a<T, Concept>::value;
/*********************/
/* Implementation */
/*********************/
......
......@@ -33,7 +33,7 @@ namespace mln::concepts
!std::is_reference_v<pixel_value_t<Pix>> &&
requires(const Pix cpix, Pix pix, pixel_point_t<Pix> p) {
{ cpix.point() } -> ::concepts::convertible_to<pixel_point_t<Pix>>;
#if (__GNUG__) // see https://stackoverflow.com/questions/55198202/unable-to-deduce-placeholder-type-in-concept
#if (__GNUG__ == 9) // see https://stackoverflow.com/questions/55198202/unable-to-deduce-placeholder-type-in-concept
{ cpix.val() } -> ::concepts::convertible_to<pixel_reference_t<Pix>>&&;
#else
{ cpix.val() } -> ::concepts::convertible_to<pixel_reference_t<Pix>>;
......
......@@ -27,7 +27,7 @@ namespace mln::concepts
template <typename SE>
concept DynamicStructuringElement =
requires (SE se) {
{ se.radial_extent() } -> int;
{ se.radial_extent() } -> ::concepts::same_as<int>;
};
......@@ -73,7 +73,7 @@ namespace mln::concepts
StructuringElement<SE, P> &&
::concepts::convertible_to<typename SE::decomposable, std::true_type> &&
requires(const SE se) {
{ se.is_decomposable() } -> bool;
{ se.is_decomposable() } -> ::concepts::same_as<bool>;
{ se.decompose() } -> ::ranges::cpp20::forward_range;
requires details::RangeOfStructuringElement<decltype(se.decompose()), P>;
};
......@@ -84,7 +84,7 @@ namespace mln::concepts
StructuringElement<SE, P> &&
::concepts::convertible_to<typename SE::separable, std::true_type> &&
requires(const SE se) {
{ se.is_separable() } -> bool;
{ se.is_separable() } -> ::concepts::same_as<bool>;
{ se.separate() } -> ::ranges::cpp20::forward_range;
requires details::RangeOfStructuringElement<decltype(se.separate()), P>;
};
......
......@@ -25,7 +25,7 @@ namespace mln::extension
template <typename SE>
constexpr bool fit(const SE&) const
{
static_assert(concepts::StructuringElement<SE>, "SE is not a valid Structuring Element!");
static_assert(mln::is_a_v<SE, details::StructuringElement>, "SE is not a valid Structuring Element!");
return true;
}
......
......@@ -37,7 +37,7 @@ namespace mln::extension
template <typename SE>
constexpr bool fit(const SE&) const
{
static_assert(concepts::StructuringElement<SE>, "SE is not a valid Structuring Element!");
static_assert(mln::is_a_v<SE, details::StructuringElement>, "SE is not a valid Structuring Element!");
// TODO: non-trivial
return true;
......
......@@ -23,7 +23,7 @@ namespace mln::extension
template <typename SE>
constexpr bool fit(const SE&) const
{
static_assert(concepts::StructuringElement<SE>, "SE is not a valid Structuring Element!");
static_assert(mln::is_a_v<SE, details::StructuringElement>, "SE is not a valid Structuring Element!");
return true;
}
......
......@@ -25,7 +25,7 @@ namespace mln::extension
template <typename SE>
constexpr bool fit(const SE&) const
{
static_assert(concepts::StructuringElement<SE>, "SE is not a valid Structuring Element!");
static_assert(mln::is_a_v<SE, details::StructuringElement>, "SE is not a valid Structuring Element!");
return true;
}
......
......@@ -30,7 +30,7 @@ namespace mln::extension
template <typename SE>
constexpr bool fit(const SE&) const
{
static_assert(concepts::StructuringElement<SE>, "SE is not a valid Structuring Element!");
static_assert(mln::is_a_v<SE, details::StructuringElement>, "SE is not a valid Structuring Element!");
return true;
}
......
......@@ -22,7 +22,7 @@ namespace mln::extension
template <typename SE>
constexpr bool fit(const SE& se) const
{
static_assert(concepts::StructuringElement<SE>, "SE is not a valid Structuring Element!");
static_assert(mln::is_a_v<SE, details::StructuringElement>, "SE is not a valid Structuring Element!");
if constexpr (std::is_base_of_v<mln::dynamic_neighborhood_tag, typename SE::category>)
{
......
......@@ -139,7 +139,7 @@ namespace mln
[[deprecated]] const T* data() const noexcept { return this->buffer(); }
index_type index_of_point(fast_point_type p) const noexcept;
fast_point_type point_at_index(index_type i) const noexcept;
point_type point_at_index(index_type i) const noexcept;
index_type delta_index(fast_point_type p) const noexcept;
/// \}
......@@ -402,7 +402,7 @@ namespace mln
template <class T, int N>
inline auto __ndbuffer_image<T, N>::point_at_index(index_type i) const noexcept -> fast_point_type
inline auto __ndbuffer_image<T, N>::point_at_index(index_type i) const noexcept -> point_type
{
fast_point_type coords;
Impl::get_point(this->__info(), i, coords.data());
......
......@@ -21,7 +21,7 @@ namespace mln::internal
class __ndbuffer_image_data;
template <>
class __ndbuffer_image_data<void> : public ndbuffer_image_data
class __ndbuffer_image_data<void> final : public ndbuffer_image_data
{
std::allocator<std::byte> m_allocator;
......@@ -33,7 +33,7 @@ namespace mln::internal
};
template <class T>
class __ndbuffer_image_data : public ndbuffer_image_data
class __ndbuffer_image_data final : public ndbuffer_image_data
{
std::allocator<T> m_allocator;
......
......@@ -11,10 +11,9 @@ namespace mln
template <class Pix>
struct pixel_adaptor
{
using point_type = typename Pix::point_type;
using site_type[[deprecated]] = point_type;
using value_type = typename Pix::value_type;
using reference = typename Pix::reference;
using point_type = typename Pix::point_type;
using value_type = typename Pix::value_type;
using reference = typename Pix::reference;
decltype(auto) val() const { return m_pix.val(); }
auto point() const { return m_pix.point(); }
......@@ -79,8 +78,7 @@ namespace mln
template <class I>
struct image_adaptor_base_indexable<I, std::enable_if_t<I::indexable::value>>
{
using size_type[[deprecated]] = image_index_t<I>;
using index_type = size_type;
using index_type = image_index_t<I>;
};
template <class I, class = void>
......
......@@ -76,7 +76,7 @@ namespace mln
template <typename SE>
constexpr bool fit(const SE& se) const
{
static_assert(concepts::StructuringElement<SE>, "SE is not a valid Structuring Element!");
static_assert(mln::is_a_v<SE, details::StructuringElement>, "SE is not a valid Structuring Element!");
return std::visit([&se](auto&& ima) { return ima.extension().fit(se); }, *m_adapted_image_ptr);
}
......
......@@ -59,19 +59,19 @@ namespace mln
template <class P>
requires(!mln::is_a<P, mln::details::Pixel>::value) auto operator()(const P& point) const
{
return ::ranges::view::transform(static_cast<const N*>(this)->offsets(), details::add_point<P>{point});
return ::ranges::views::transform(static_cast<const N*>(this)->offsets(), details::add_point<P>{point});
}
template <class P>
requires(!mln::is_a<P, mln::details::Pixel>::value) auto before(const P& point) const
{
return ::ranges::view::transform(static_cast<const N*>(this)->before_offsets(), details::add_point<P>{point});
return ::ranges::views::transform(static_cast<const N*>(this)->before_offsets(), details::add_point<P>{point});
}
template <class P>
requires(!mln::is_a<P, mln::details::Pixel>::value) auto after(const P& point) const
{
return ::ranges::view::transform(static_cast<const N*>(this)->after_offsets(), details::add_point<P>{point});
return ::ranges::views::transform(static_cast<const N*>(this)->after_offsets(), details::add_point<P>{point});
}
};
......@@ -99,19 +99,19 @@ namespace mln
template <class P> requires(!mln::is_a<P, mln::details::Pixel>::value)
auto operator()(const P& point) const
{
return ::ranges::view::transform(static_cast<const N*>(this)->offsets(), details::add_wpoint<P>{point});
return ::ranges::views::transform(static_cast<const N*>(this)->offsets(), details::add_wpoint<P>{point});