- edited description
What is the easiest way to implement compatibility layer where types don't agree in padding?
I hope I'm using the correct communication channel for this question. I didn’t find question issue type, so I put the least urgent one.
So basically I would like CustomMatrix
to work on GIL images, but since pixel type and static vector don't agree in sizes, it will not work. For example:
gil::rgb8_pixel_t
vs blaze::StaticVector<uint8_t, 3>
If I turn off the padding and alignment, there will still be mismatch in the sizes, as the pixel uses padding to 4 bytes. Turning the flags off does work for types like rgba8_pixel_t
which takes up the same amount of space as the sum of channels.
Is there an easy route to implement a proxy that will behave like StaticVector<T, N>
, but inherit from StaticVector<T, M>
where M > N? I was thinking if I could inherit from a wider vector and then make it think it has less elements than it actually has space for, even with padding and alignment turned off. Will replacing the size()
function with my own be enough to do this?
Comments (6)
-
reporter -
Hi Olzhas!
The question is very hard to answer since we don’t know what you are planning to do with the vectors and matrices. We assume that you are not interested in vectorization, but that you just want to represent the 4-byte data type gil::rgb8_pixel_t as Blaze vector. Then you are correct,
StaticVector
doesn’t fit your bill since its size and capacity are always equal (same as astd::array
) and it uses padding only to facilitate vectorization. For a 1-byte data type such asuint8_t
this will result in at least 16 elements. Inheriting fromStaticVector
would not work either.StaticVector
is implemented by means of CRTP and down-casts would not know about your type. This means internallyStaticVector::size()
would be called.The most promising approach seems to be to use a custom data type (see the last post in issue #349 for the most recent explanation of this topic):
DynamicMatrix<gil::rgb8_pixel_t> pixels( ... );
Depending on which operations you have in mind, you would just have to provide the necessary overloads for
gil::rgb8_pixel_t
. For instance:gil::rgb8_pixel_t operator+( gil::rgb8_pixel_t a, gil::rgb8_pixel_t b ) { /*...*/ } DynamicMatrix<gil::rgb8_pixel_t> A, B; A + B; // Enabled via operator+() for gil::rgb8_pixel_t
I hope this helps,
Best regards,
Klaus!
-
reporter Thanks for the pointers. I will try out an adaptor I have in mind and come back with the results (will resolve the issue as well).
-
reporter I was able to make it compile for the Schur product case when it is an element of a matrix.
#include <blaze/Blaze.h> #include <boost/gil/pixel.hpp> #include <functional> #include <type_traits> template <typename ChannelValue, typename Layout, bool IsRowVector = blaze::rowVector> struct pixel_vector : boost::gil::pixel<ChannelValue, Layout>, blaze::DenseVector<pixel_vector<ChannelValue, Layout, IsRowVector>, IsRowVector> { using parent_t = boost::gil::pixel<ChannelValue, Layout>; using boost::gil::pixel<ChannelValue, Layout>::pixel; using This = pixel_vector<ChannelValue, Layout, IsRowVector>; using Basetype = blaze::DenseVector<This, IsRowVector>; using ResultType = This; using TransposeType = pixel_vector<ChannelValue, Layout, !IsRowVector>; static constexpr bool simdEnabled = false; using ElementType = ChannelValue; using TagType = blaze::Group0; using ReturnType = const ChannelValue&; using CompositeType = const This&; using Reference = ChannelValue&; using ConstReference = const ChannelValue&; using Pointer = ChannelValue*; using ConstPointer = const ChannelValue*; // TODO: rule of 5 template <typename OtherChannelValue> pixel_vector(pixel_vector<OtherChannelValue, Layout> other) : parent_t(other) { } template <typename OtherChannelValue> pixel_vector& operator=(const pixel_vector<OtherChannelValue, Layout> other) { parent_t::operator=(other); return *this; } template <typename VT, bool TransposeFlag> pixel_vector& operator=(const blaze::DenseVector<VT, TransposeFlag>& v) { if ((~v).size() != size()) { throw std::invalid_argument( "incoming vector has incompatible size with this pixel vector"); } for (std::size_t i = 0; i < size(); ++i) { (*this)[i] = (~v)[i]; } return *this; } private: template <typename OtherChannelValue, typename Op> void perform_op(pixel_vector<OtherChannelValue, Layout> p, Op op) { constexpr auto num_channels = boost::gil::num_channels<parent_t>{}; auto& current = *this; for (std::ptrdiff_t i = 0; i < num_channels; ++i) { current[i] = op(current[i], p[i]); } } public: std::size_t size() const { return boost::gil::num_channels<parent_t>{}; } constexpr bool canAlias() const { return true; } template <typename Other> bool isAliased(const Other* alias) const noexcept { return static_cast<const void*>(this) == static_cast<const void*>(alias); } template <typename Other> bool canAlias(const Other* alias) const noexcept { return static_cast<const void*>(this) == static_cast<const void*>(alias); } // TODO: Add SFINAE for cases when parent_t::is_mutable is false template <typename OtherChannelValue, typename OtherLayout> pixel_vector& operator+=(pixel_vector<OtherChannelValue, OtherLayout> other) { perform_op(other, std::plus<>{}); return *this; } template <typename OtherChannelValue, typename OtherLayout> pixel_vector& operator-=(pixel_vector<OtherChannelValue, OtherLayout> other) { perform_op(other, std::minus<>{}); return *this; } template <typename OtherChannelValue, typename OtherLayout> pixel_vector& operator*=(pixel_vector<OtherChannelValue, OtherLayout> other) { perform_op(other, std::multiplies<>{}); return *this; } template <typename OtherChannelValue, typename OtherLayout> pixel_vector& operator/=(pixel_vector<OtherChannelValue, OtherLayout> other) { perform_op(other, std::divides<>{}); return *this; } }; template <typename ChannelValue1, typename ChannelValue2, typename Layout, bool IsRowVector> inline pixel_vector<std::common_type_t<ChannelValue1, ChannelValue2>, Layout> operator+(pixel_vector<ChannelValue1, Layout, IsRowVector> lhs, pixel_vector<ChannelValue2, Layout, IsRowVector> rhs) { lhs += rhs; return lhs; } template <typename ChannelValue1, typename ChannelValue2, typename Layout, bool IsRowVector> inline pixel_vector<std::common_type_t<ChannelValue1, ChannelValue2>, Layout> operator-(pixel_vector<ChannelValue1, Layout, IsRowVector> lhs, pixel_vector<ChannelValue2, Layout, IsRowVector> rhs) { lhs -= rhs; return lhs; } template <typename ChannelValue1, typename ChannelValue2, typename Layout, bool IsRowVector> inline pixel_vector<std::common_type_t<ChannelValue1, ChannelValue2>, Layout> operator*(pixel_vector<ChannelValue1, Layout, IsRowVector> lhs, pixel_vector<ChannelValue2, Layout, IsRowVector> rhs) { lhs *= rhs; return lhs; } template <typename ChannelValue1, typename ChannelValue2, typename Layout, bool IsRowVector> inline pixel_vector<std::common_type_t<ChannelValue1, ChannelValue2>, Layout> operator/(pixel_vector<ChannelValue1, Layout, IsRowVector> lhs, pixel_vector<ChannelValue2, Layout, IsRowVector> rhs) { lhs /= rhs; return lhs; } template <typename Pixel, bool IsRowVector = blaze::rowVector> struct pixel_vector_type; template <typename ChannelValue, typename Layout, bool IsRowVector> struct pixel_vector_type<boost::gil::pixel<ChannelValue, Layout>, IsRowVector> { using type = pixel_vector<ChannelValue, Layout, IsRowVector>; }; namespace blaze { template <typename ChannelValue, typename Layout, bool IsRowVector> struct UnderlyingElement<pixel_vector<ChannelValue, Layout, IsRowVector>> { using Type = ChannelValue; }; template <typename ChannelValue, typename Layout, bool IsRowVector> struct IsDenseVector<pixel_vector<ChannelValue, Layout, IsRowVector>> : std::true_type { }; } // namespace blaze
I guess there is no documentation for my case as hand-rolling a vector is not considered yet. Would it be hard to make a full blown
StaticVector
out of this? Like with expression templates and stuff. -
Hi Olzhas!
It would take some more effort to make this work in all relevant operations. That’s why we don’t plan for users of Blaze to do this on their own and why there is no documentation for this. However, if it works for you then of course this is a reasonable solution.
It should not be necessary to provide the two template specializations for
UnderlyingElement
andIsDenseVector
. Did you try it without them? Also, did you try to use agil::pixel
as a custom data type for aCustomMatrix
? If yes, why and how did it fail?I apologize for the late reply,
Best regards,
Klaus!
-
- changed status to resolved
- Log in to comment
Corrected formatting