samedi 5 mai 2018

Avoiding const/non-const duplication of method accepting a lambda

class Frame<P> represents an image with pixels of type P. The algorithm that iterates through its pixels is non-trivial due to several flexibilities in the underlying data buffer format.

I define its point-wise arithmetic operators like this:

template <typename P, bool RM = true> // P is pixel type; RM = is_row_major
class Frame {

    template<typename T>
    Frame<P, RM>& operator += (const Frame<T, RM>& rhs) {
        iterateTogether(rhs, [](auto& me, auto him) {
            me += him;
        });
        return *this;
    }
    // ...
    // ...
    // ... many operations similar to above ...


    template<typename P2, bool RM2, typename F>
    void iterateTogether(const Frame<P2, RM2>& other, F f) {

        // This function has a non-trivial body and may be extended in future.

 // ************************************************************************
 // * The rest of the code is background info. Feel free to ignore below.  *
 // ************************************************************************

        static_assert(RM == RM2, "TODO: Implement for cases with RM!=RM2.");

        if (size != other.size) {
            throw std::invalid_argument("Frame sizes inconsistent.");
        }
        // Choose a performant way to iterate under the present conditions.
        if (stride == other.stride) {
            if (stride == (RM ? size.w : size.h)) {
                auto n = size.area();
                for (index_t k = 0; k < n; k++) {
                    f(view[k], other.view[k]);
                }
            }
            else {
                auto s = static_cast<index_t>(stride)*(RM ? size.h : size.w);
                for (index_t k0 = 0; k0 < s; k0 += stride) {
                    auto m = k0 + (RM ? size.w : size.h);
                    for (index_t k = k0; k < m; k++) {
                        f(view[k], other.view[k]);
                    }
                }
            }
        }
        else {
            auto s = static_cast<index_t>(stride)*(RM ? size.h : size.w);
            index_t l0 = 0;
            for (index_t k0 = 0; k0 < s; k0 += stride) {
                auto m = k0 + (RM ? size.w : size.h);
                auto n = l0 + (RM ? size.w : size.h);
                auto l = l0;
                for (index_t k = k0; k < m; k++) {
                    f(view[k], other.view[l++]);
                }
                l0 += other.stride;
            }
        }
    }
}

However, occasionally I would like to call iterateTogether() on a const Frame. Since the method does not modify its host object (except if the lambda passed into it modifies its own arguments) I can duplicate the entire method and attach const to its signature. But is there a better way?

Aucun commentaire:

Enregistrer un commentaire