vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:11:13 +08:00
commit 12022378a3
3872 changed files with 2513409 additions and 0 deletions
+122
View File
@@ -0,0 +1,122 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2020 by Archit Rungta
// This header files hacks into the mapping code of CxxWrap to support automatic conversion between types from OpenCV and Julia
#include <vector>
#include "jlcxx/jlcxx.hpp"
#include "jlcxx/functions.hpp"
#include "jlcxx/stl.hpp"
#include "jlcxx/array.hpp"
#include "jlcxx/tuple.hpp"
#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/core/ocl.hpp>
#include <opencv2/core/bindings_utils.hpp>
#include <opencv2/opencv_modules.hpp>
#include <type_traits>
using namespace cv;
using namespace std;
using namespace jlcxx;
#ifdef HAVE_OPENCV_HIGHGUI
#include <opencv2/highgui.hpp>
#endif
#ifdef HAVE_OPENCV_IMGPROC
#include <opencv2/imgproc.hpp>
#endif
#ifdef HAVE_OPENCV_VIDEOIO
#include <opencv2/videoio.hpp>
#endif
#ifdef HAVE_OPENCV_FEATURES
#include <opencv2/features.hpp>
typedef SimpleBlobDetector::Params SimpleBlobDetector_Params;
typedef AKAZE::DescriptorType AKAZE_DescriptorType;
typedef AgastFeatureDetector::DetectorType AgastFeatureDetector_DetectorType;
typedef FastFeatureDetector::DetectorType FastFeatureDetector_DetectorType;
typedef DescriptorMatcher::MatcherType DescriptorMatcher_MatcherType;
typedef KAZE::DiffusivityType KAZE_DiffusivityType;
typedef ORB::ScoreType ORB_ScoreType;
#endif
#ifdef HAVE_OPENCV_XOBJDETECT
#include <opencv2/xobjdetect.hpp>
typedef HOGDescriptor::HistogramNormType HOGDescriptor_HistogramNormType;
typedef HOGDescriptor::DescriptorStorageFormat HOGDescriptor_DescriptorStorageFormat;
#endif
#ifdef HAVE_OPENCV_FLANN
typedef cvflann::flann_distance_t cvflann_flann_distance_t;
typedef cvflann::flann_algorithm_t cvflann_flann_algorithm_t;
typedef flann::IndexParams flann_IndexParams;
typedef flann::SearchParams flann_SearchParams;
#endif
#ifdef HAVE_OPENCV_DNN
#include <opencv2/dnn.hpp>
typedef cv::dnn::DictValue LayerId;
typedef cv::dnn::Backend dnn_Backend;
typedef cv::dnn::Target dnn_Target;
#endif
#ifdef HAVE_OPENCV_CALIB3D
#include <opencv2/calib3d.hpp>
#endif
template <typename C>
struct get_template_type;
template <typename C>
struct get_template_type_vec;
template <template <typename> class C, typename T>
struct get_template_type<C<T>> {
using type = T;
};
template <template <typename, int> class C, typename T, int N>
struct get_template_type_vec<C<T, N>> {
using type = T;
int dim = N;
};
template<typename T, bool v>
struct force_enum{};
template<typename T>
struct force_enum<T, false>{
using Type = T;
};
template<typename T>
struct force_enum<T, true>{
using Type = int64_t;
};
template<typename T>
struct force_enum_int{
using Type = typename force_enum<T, std::is_enum<T>::value>::Type;
};
typedef vector<Mat> vector_Mat;
typedef vector<UMat> vector_UMat;
typedef char* c_string;
#include "jlcv_types.hpp"
+283
View File
@@ -0,0 +1,283 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2020 by Archit Rungta
template<typename T>
struct CxxPoint
{
T x;
T y;
};
template<typename T>
struct CxxPoint3
{
T x;
T y;
T z;
};
template<typename T>
struct CxxSize
{
T width;
T height;
};
template<typename T>
struct CxxRect
{
T x;
T y;
T width;
T height;
};
struct CxxRotatedRect
{
Point2f center;
Size2f size;
float angle;
};
struct CxxRange
{
int start;
int end;
};
struct CxxTermCriteria
{
int type;
int maxCount;
double epsilon;
};
template<typename T>
struct CxxComplex
{
T re;
T im;
};
namespace jlcxx
{
template <> struct IsMirroredType<cv::Range> : std::true_type {};
template <> struct IsMirroredType<cv::RotatedRect> : std::true_type {};
template <> struct IsMirroredType<cv::TermCriteria> : std::true_type {};
template<typename T> struct IsMirroredType<cv::Point_<T>> : std::true_type {};
template<typename T> struct static_type_mapping<cv::Point_<T>> { using type = CxxPoint<T>; };
template<typename T>
struct julia_type_factory<cv::Point_<T>>
{
static inline jl_datatype_t* julia_type()
{
return (jl_datatype_t*)apply_type((jl_value_t*)jlcxx::julia_type("Point"), jl_svec1(julia_base_type<T>()));
}
};
template<typename T>
struct ConvertToJulia<cv::Point_<T>, NoMappingTrait>
{
CxxPoint<T> operator()(const cv::Point_<T>& cpp_val) const
{
return *reinterpret_cast<const CxxPoint<T>*>(&cpp_val);
}
};
template<typename T>
struct ConvertToCpp<cv::Point_<T>, NoMappingTrait>
{
inline cv::Point operator()(const CxxPoint<T>& julia_val) const
{
return *reinterpret_cast<const cv::Point_<T>*>(&julia_val);
}
};
template<typename T> struct IsMirroredType<cv::Size_<T>> : std::true_type {};
template<typename T> struct static_type_mapping<cv::Size_<T>> { using type = CxxSize<T>; };
template<typename T>
struct julia_type_factory<cv::Size_<T>>
{
static inline jl_datatype_t* julia_type()
{
return (jl_datatype_t*)apply_type((jl_value_t*)jlcxx::julia_type("Size"), jl_svec1(julia_base_type<T>()));
}
};
template<typename T>
struct ConvertToJulia<cv::Size_<T>, NoMappingTrait>
{
CxxSize<T> operator()(const cv::Size_<T>& cpp_val) const
{
return *reinterpret_cast<const CxxSize<T>*>(&cpp_val);
}
};
template<typename T>
struct ConvertToCpp<cv::Size_<T>, NoMappingTrait>
{
inline cv::Size operator()(const CxxSize<T>& julia_val) const
{
return *reinterpret_cast<const cv::Size_<T>*>(&julia_val);
}
};
template<typename T> struct IsMirroredType<cv::Point3_<T>> : std::true_type {};
template<typename T> struct static_type_mapping<cv::Point3_<T>> { using type = CxxPoint3<T>; };
template<typename T>
struct julia_type_factory<cv::Point3_<T>>
{
static inline jl_datatype_t* julia_type()
{
return (jl_datatype_t*)apply_type((jl_value_t*)jlcxx::julia_type("Point3"), jl_svec1(julia_base_type<T>()));
}
};
template<typename T>
struct ConvertToJulia<cv::Point3_<T>, NoMappingTrait>
{
CxxPoint3<T> operator()(const cv::Point3_<T>& cpp_val) const
{
return *reinterpret_cast<const CxxPoint3<T>*>(&cpp_val);
}
};
template<typename T>
struct ConvertToCpp<cv::Point3_<T>, NoMappingTrait>
{
inline cv::Point3_<T> operator()(const CxxPoint3<T>& julia_val) const
{
return *reinterpret_cast<const cv::Point3_<T>*>(&julia_val);
}
};
template<typename T> struct IsMirroredType<cv::Rect_<T>> : std::true_type {};
template<typename T> struct static_type_mapping<cv::Rect_<T>> { using type = CxxRect<T>; };
template<typename T>
struct julia_type_factory<cv::Rect_<T>>
{
static inline jl_datatype_t* julia_type()
{
return (jl_datatype_t*)apply_type((jl_value_t*)jlcxx::julia_type("Rect"), jl_svec1(julia_base_type<T>()));
}
};
template<typename T>
struct ConvertToJulia<cv::Rect_<T>, NoMappingTrait>
{
CxxRect<T> operator()(const cv::Rect_<T>& cpp_val) const
{
return *reinterpret_cast<const CxxRect<T>*>(&cpp_val);
}
};
template<typename T>
struct ConvertToCpp<cv::Rect_<T>, NoMappingTrait>
{
inline cv::Rect operator()(const CxxRect<T>& julia_val) const
{
return *reinterpret_cast<const cv::Rect_<T>*>(&julia_val);
}
};
template<typename T> struct IsMirroredType<cv::Complex<T>> : std::true_type {};
template<typename T> struct static_type_mapping<cv::Complex<T>> { using type = CxxComplex<T>; };
template<typename T>
struct julia_type_factory<cv::Complex<T>>
{
static inline jl_datatype_t* julia_type()
{
return (jl_datatype_t*)apply_type((jl_value_t*)jlcxx::julia_type("cvComplex"), jl_svec1(julia_base_type<T>()));
}
};
template<typename T>
struct ConvertToJulia<cv::Complex<T>, NoMappingTrait>
{
CxxComplex<T> operator()(const cv::Complex<T>& cpp_val) const
{
return *reinterpret_cast<const CxxComplex<T>*>(&cpp_val);
}
};
template<typename T>
struct ConvertToCpp<cv::Complex<T>, NoMappingTrait>
{
inline cv::Complex<T> operator()(const CxxComplex<T>& julia_val) const
{
return *reinterpret_cast<const cv::Complex<T>*>(&julia_val);
}
};
template<typename T>
struct BoxValue<Size_<T>,CxxSize<T>>
{
inline jl_value_t* operator()(Size_<T> cppval)
{
return jl_new_bits((jl_value_t*)julia_type<Size_<T>>(), reinterpret_cast<CxxSize<T>*>(&cppval));
}
inline jl_value_t* operator()(Size_<T> cppval, jl_value_t* dt)
{
return jl_new_bits(dt, reinterpret_cast<CxxSize<T>*>(&cppval));
}
};
template<typename T>
struct BoxValue<Point_<T>,CxxPoint<T>>
{
inline jl_value_t* operator()(Point_<T> cppval)
{
return jl_new_bits((jl_value_t*)julia_type<Point_<T>>(), reinterpret_cast<CxxPoint<T>*>(&cppval));
}
inline jl_value_t* operator()(Point_<T> cppval, jl_value_t* dt)
{
return jl_new_bits(dt, reinterpret_cast<CxxPoint<T>*>(&cppval));
}
};
template<typename T>
struct BoxValue<Point3_<T>,CxxPoint3<T>>
{
inline jl_value_t* operator()(Point3_<T> cppval)
{
return jl_new_bits((jl_value_t*)julia_type<Point3_<T>>(), reinterpret_cast<CxxPoint3<T>*>(&cppval));
}
inline jl_value_t* operator()(Point3_<T> cppval, jl_value_t* dt)
{
return jl_new_bits(dt, reinterpret_cast<CxxPoint3<T>*>(&cppval));
}
};
template<typename T>
struct BoxValue<Rect_<T>,CxxRect<T>>
{
inline jl_value_t* operator()(Rect_<T> cppval)
{
return jl_new_bits((jl_value_t*)julia_type<Rect_<T>>(), reinterpret_cast<CxxRect<T>*>(&cppval));
}
inline jl_value_t* operator()(Rect_<T> cppval, jl_value_t* dt)
{
return jl_new_bits(dt, reinterpret_cast<CxxRect<T>*>(&cppval));
}
};
};
+459
View File
@@ -0,0 +1,459 @@
// This file is a modified array.hpp from https://github.com/JuliaInterop/libcxxwrap-julia
// required for the hack that allows automated conversion of OpenCV types.
// Shouldn't be needed once CxxWrap gets inbuilt support
// Here is the original copyright and the license:
/*
==
Copyright (c) 2015: Bart Janssens.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
==
*/
#ifndef JLCXX_ARRAY_HPP
#define JLCXX_ARRAY_HPP
#include "jlcxx/type_conversion.hpp"
#include "jlcxx/tuple.hpp"
namespace jlcxx
{
template<typename PointedT, typename CppT>
struct ValueExtractor
{
inline CppT operator()(PointedT* p)
{
return convert_to_cpp<CppT>(*p);
}
};
template<typename PointedT>
struct ValueExtractor<PointedT, PointedT>
{
inline PointedT& operator()(PointedT* p)
{
return *p;
}
};
template<typename PointedT, typename CppT>
class array_iterator_base : public std::iterator<std::random_access_iterator_tag, CppT>
{
private:
PointedT* m_ptr;
public:
array_iterator_base() : m_ptr(nullptr)
{
}
explicit array_iterator_base(PointedT* p) : m_ptr(p)
{
}
template <class OtherPointedT, class OtherCppT>
array_iterator_base(array_iterator_base<OtherPointedT, OtherCppT> const& other) : m_ptr(other.m_ptr) {}
auto operator*() -> decltype(ValueExtractor<PointedT,CppT>()(m_ptr))
{
return ValueExtractor<PointedT,CppT>()(m_ptr);
}
array_iterator_base<PointedT, CppT>& operator++()
{
++m_ptr;
return *this;
}
array_iterator_base<PointedT, CppT>& operator--()
{
--m_ptr;
return *this;
}
array_iterator_base<PointedT, CppT>& operator+=(std::ptrdiff_t n)
{
m_ptr += n;
return *this;
}
array_iterator_base<PointedT, CppT>& operator-=(std::ptrdiff_t n)
{
m_ptr -= n;
return *this;
}
PointedT* ptr() const
{
return m_ptr;
}
};
/// Wrap a Julia 1D array in a C++ class. Array is allocated on the C++ side
template<typename ValueT>
class Array
{
public:
Array(const size_t n = 0)
{
jl_value_t* array_type = apply_array_type(julia_type<ValueT>(), 1);
m_array = jl_alloc_array_1d(array_type, n);
}
Array(jl_datatype_t* applied_type, const size_t n = 0)
{
jl_value_t* array_type = apply_array_type(applied_type, 1);
m_array = jl_alloc_array_1d(array_type, n);
}
/// Append an element to the end of the list
template<typename VT>
void push_back(VT&& val)
{
JL_GC_PUSH1(&m_array);
const size_t pos = jl_array_len(m_array);
jl_array_grow_end(m_array, 1);
jl_arrayset(m_array, box<ValueT>(val), pos);
JL_GC_POP();
}
/// Access to the wrapped array
jl_array_t* wrapped()
{
return m_array;
}
// access to the pointer for GC macros
jl_array_t** gc_pointer()
{
return &m_array;
}
private:
jl_array_t* m_array;
};
namespace detail
{
template<typename T, typename TraitT=mapping_trait<T>>
struct ArrayElementType
{
using type = static_julia_type<T>;
};
template<typename T>
struct ArrayElementType<T,WrappedPtrTrait>
{
using type = T;
};
}
/// Reference a Julia array in an STL-compatible wrapper
template<typename ValueT, int Dim = 1>
class ArrayRef
{
public:
using julia_t = typename detail::ArrayElementType<ValueT>::type;
ArrayRef(jl_array_t* arr) : m_array(arr)
{
assert(wrapped() != nullptr);
}
/// Convert from existing C-array (memory owned by C++)
template<typename... SizesT>
ArrayRef(julia_t* ptr, const SizesT... sizes);
/// Convert from existing C-array, explicitly setting Julia ownership
template<typename... SizesT>
ArrayRef(const bool julia_owned, julia_t* ptr, const SizesT... sizes);
typedef array_iterator_base<julia_t, ValueT> iterator;
typedef array_iterator_base<julia_t const, ValueT const> const_iterator;
inline jl_array_t* wrapped() const
{
return m_array;
}
iterator begin()
{
return iterator(static_cast<julia_t*>(jl_array_data(wrapped())));
}
const_iterator begin() const
{
return const_iterator(static_cast<julia_t*>(jl_array_data(wrapped())));
}
iterator end()
{
return iterator(static_cast<julia_t*>(jl_array_data(wrapped())) + jl_array_len(wrapped()));
}
const_iterator end() const
{
return const_iterator(static_cast<julia_t*>(jl_array_data(wrapped())) + jl_array_len(wrapped()));
}
void push_back(const ValueT& val)
{
static_assert(Dim == 1, "ArrayRef::push_back is only for 1D ArrayRef");
static_assert(std::is_same<julia_t,ValueT>::value, "ArrayRef::push_back is only for arrays of fundamental types");
jl_array_t* arr_ptr = wrapped();
JL_GC_PUSH1(&arr_ptr);
const size_t pos = size();
jl_array_grow_end(arr_ptr, 1);
jl_arrayset(arr_ptr, box<ValueT>(val), pos);
JL_GC_POP();
}
const julia_t* data() const
{
return (julia_t*)jl_array_data(wrapped());
}
julia_t* data()
{
return (julia_t*)jl_array_data(wrapped());
}
std::size_t size() const
{
return jl_array_len(wrapped());
}
ValueT& operator[](const std::size_t i)
{
if constexpr(std::is_same<julia_t, ValueT>::value)
{
return data()[i];
}
else if constexpr(std::is_same<julia_t, static_julia_type<ValueT>>::value && !std::is_same<julia_t, WrappedCppPtr>::value)
{
return *reinterpret_cast<ValueT*>(&data()[i]);
}
else
{
return *extract_pointer_nonull<ValueT>(data()[i]);
}
}
const ValueT& operator[](const std::size_t i) const
{
if constexpr(std::is_same<julia_t, ValueT>::value)
{
return data()[i];
}
else if constexpr(std::is_same<julia_t, static_julia_type<ValueT>>::value && !std::is_same<julia_t, WrappedCppPtr>::value)
{
return *reinterpret_cast<ValueT*>(&data()[i]);
}
else
{
return *extract_pointer_nonull<ValueT>(data()[i]);
}
}
jl_array_t* m_array;
};
// Conversions
template<typename T, int Dim, typename SubTraitT>
struct static_type_mapping<ArrayRef<T, Dim>, CxxWrappedTrait<SubTraitT>>
{
typedef jl_array_t* type;
};
namespace detail
{
template<typename T, typename TraitT=mapping_trait<T>>
struct PackedArrayType
{
static jl_datatype_t* type()
{
return julia_type<T>();
}
};
template<typename T>
struct PackedArrayType<T*, WrappedPtrTrait>
{
static jl_datatype_t* type()
{
return (jl_datatype_t*)apply_type((jl_value_t*)jlcxx::julia_type("Ptr"), jl_svec1(julia_base_type<T>()));
}
};
template<typename T, typename SubTraitT>
struct PackedArrayType<T,CxxWrappedTrait<SubTraitT>>
{
static jl_datatype_t* type()
{
create_if_not_exists<T&>();
return julia_type<T&>();
}
};
}
template<typename T, int Dim>
struct julia_type_factory<ArrayRef<T, Dim>>
{
static inline jl_datatype_t* julia_type()
{
create_if_not_exists<T>();
return (jl_datatype_t*)apply_array_type(detail::PackedArrayType<T>::type(), Dim);
}
};
template<typename ValueT, typename... SizesT>
jl_array_t* wrap_array(const bool julia_owned, ValueT* c_ptr, const SizesT... sizes)
{
jl_datatype_t* dt = julia_type<ArrayRef<ValueT, sizeof...(SizesT)>>();
jl_value_t *dims = nullptr;
JL_GC_PUSH1(&dims);
dims = convert_to_julia(std::make_tuple(static_cast<cxxint_t>(sizes)...));
jl_array_t* result = jl_ptr_to_array((jl_value_t*)dt, c_ptr, dims, julia_owned);
JL_GC_POP();
return result;
}
template<typename ValueT, int Dim>
template<typename... SizesT>
ArrayRef<ValueT, Dim>::ArrayRef(julia_t* c_ptr, const SizesT... sizes) : m_array(wrap_array(false, c_ptr, sizes...))
{
}
template<typename ValueT, int Dim>
template<typename... SizesT>
ArrayRef<ValueT, Dim>::ArrayRef(const bool julia_owned, julia_t* c_ptr, const SizesT... sizes) : m_array(wrap_array(julia_owned, c_ptr, sizes...))
{
}
template<typename ValueT, typename... SizesT>
auto make_julia_array(ValueT* c_ptr, const SizesT... sizes) -> ArrayRef<ValueT, sizeof...(SizesT)>
{
return ArrayRef<ValueT, sizeof...(SizesT)>(false, c_ptr, sizes...);
}
template<typename T, typename SubTraitT>
struct static_type_mapping<Array<T>, CxxWrappedTrait<SubTraitT>>
{
typedef jl_array_t* type;
};
template<typename T>
struct julia_type_factory<Array<T>>
{
static inline jl_datatype_t* julia_type()
{
create_if_not_exists<T>();
return (jl_datatype_t*)apply_array_type(jlcxx::julia_type<T>(), 1);
}
};
template<typename T, int Dim>
struct ConvertToJulia<ArrayRef<T,Dim>>
{
template<typename ArrayRefT>
jl_array_t* operator()(ArrayRefT&& arr) const
{
return arr.wrapped();
}
};
template<typename T>
struct ConvertToJulia<Array<T>>
{
jl_value_t* operator()(Array<T>&& arr) const
{
return (jl_value_t*)arr.wrapped();
}
};
template<typename T, int Dim, typename SubTraitT>
struct ConvertToCpp<ArrayRef<T,Dim>, CxxWrappedTrait<SubTraitT>>
{
ArrayRef<T,Dim> operator()(jl_array_t* arr) const
{
return ArrayRef<T,Dim>(arr);
}
};
// Iterator operator implementation
template<typename PointedT, typename CppT>
bool operator!=(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return r.ptr() != l.ptr();
}
template<typename PointedT, typename CppT>
bool operator==(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return r.ptr() == l.ptr();
}
template<typename PointedT, typename CppT>
bool operator<=(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return l.ptr() <= r.ptr();
}
template<typename PointedT, typename CppT>
bool operator>=(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return l.ptr() >= r.ptr();
}
template<typename PointedT, typename CppT>
bool operator>(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return l.ptr() > r.ptr();
}
template<typename PointedT, typename CppT>
bool operator<(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return l.ptr() < r.ptr();
}
template<typename PointedT, typename CppT>
array_iterator_base<PointedT, CppT> operator+(const array_iterator_base<PointedT, CppT>& l, const std::ptrdiff_t n)
{
return array_iterator_base<PointedT, CppT>(l.ptr() + n);
}
template<typename PointedT, typename CppT>
array_iterator_base<PointedT, CppT> operator+(const std::ptrdiff_t n, const array_iterator_base<PointedT, CppT>& r)
{
return array_iterator_base<PointedT, CppT>(r.ptr() + n);
}
template<typename PointedT, typename CppT>
array_iterator_base<PointedT, CppT> operator-(const array_iterator_base<PointedT, CppT>& l, const std::ptrdiff_t n)
{
return array_iterator_base<PointedT, CppT>(l.ptr() - n);
}
template<typename PointedT, typename CppT>
std::ptrdiff_t operator-(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return l.ptr() - r.ptr();
}
}
#endif