1
\$\begingroup\$

This is a follow-up question for A recursive_transform Template Function for BoostMultiArray and An Add/Minus Operator For Boost.MultiArray in C++. Besides the add / minus operator for Boost.MultiArray, I am trying to implement an element-wise increment operator and decrement operator as below. The recursive_transform template function is used here to handle the element-wise incresing and decresing work.

//  Element Increment Operator
//  Define nonmember prefix increment operator
template<class T> requires is_multi_array<T>
auto operator++(T& input)
{
    input = recursive_transform(input, [](auto& x) {return x + 1; });
    return input;
}

//  Define nonmember postfix increment operator
template<class T> requires is_multi_array<T>
auto operator++(T& input, int i)
{
    auto output = input;
    input = recursive_transform(input, [](auto& x) {return x + 1; });
    return output;
}

//  Element Decrement Operator
//  Define nonmember prefix decrement operator
template<class T> requires is_multi_array<T>
auto operator--(T& input)
{
    input = recursive_transform(input, [](auto& x) {return x - 1; });
    return input;
}

//  Define nonmember postfix decrement operator
template<class T> requires is_multi_array<T>
auto operator--(T& input, int i)
{
    auto output = input;
    input = recursive_transform(input, [](auto& x) {return x - 1; });
    return output;
}

The used recursive_transform template function and the other concepts:

template<typename T>
concept is_back_inserterable = requires(T x)
{
    std::back_inserter(x);
};

template<typename T>
concept is_multi_array = requires(T x)
{
    x.num_dimensions();
    x.shape();
    boost::multi_array(x);
};

template<typename T>
concept is_iterable = requires(T x)
{
    *std::begin(x);
    std::end(x);
};

template<typename T>
concept is_elements_iterable = requires(T x)
{
    std::begin(x)->begin();
    std::end(x)->end();
};

template<typename T>
concept is_summable = requires(T x) { x + x; };

template<class T, class F>
auto recursive_transform(const T& input, const F& f)
{
    return f(input);
}

template<class T, std::size_t S, class F>
auto recursive_transform(const std::array<T, S>& input, const F& f)
{
    using TransformedValueType = decltype(recursive_transform(*input.cbegin(), f));

    std::array<TransformedValueType, S> output;
    std::transform(input.cbegin(), input.cend(), output.begin(), 
        [f](auto& element)
        {
            return recursive_transform(element, f);
        }
    );
    return output;
}

template<template<class...> class Container, class Function, class... Ts>
requires (is_back_inserterable<Container<Ts...>>&& is_iterable<Container<Ts...>> && !is_elements_iterable<Container<Ts...>>)
// non-recursive version
auto recursive_transform(const Container<Ts...>& input, const Function& f)
{
    using TransformedValueType = decltype(f(*input.cbegin()));
    Container<TransformedValueType> output;
    std::transform(input.cbegin(), input.cend(), std::back_inserter(output), f);
    return output;
}

template<template<class...> class Container, class Function, class... Ts>
requires (is_back_inserterable<Container<Ts...>> && is_elements_iterable<Container<Ts...>>)
auto recursive_transform(const Container<Ts...>& input, const Function& f)
{
    using TransformedValueType = decltype(recursive_transform(*input.cbegin(), f));
    Container<TransformedValueType> output;

    std::transform(input.cbegin(), input.cend(), std::back_inserter(output),
        [&](auto& element)
        {
            return recursive_transform(element, f);
        }
    );

    return output;
}

template<class T, class F> requires (is_multi_array<T>)
auto recursive_transform(const T& input, const F& f)
{
    boost::multi_array output(input);
    for (decltype(+input.shape()[0]) i = 0; i < input.shape()[0]; i++)
    {
        output[i] = recursive_transform(input[i], f);
    }
    return output;
}

The test of the nonmember postfix increment operator for Boost.MultiArray is as below.

// Create a 3D array that is 3 x 4 x 2
typedef boost::multi_array<double, 3> array_type;
typedef array_type::index index;
array_type A(boost::extents[3][4][2]);

// Assign values to the elements
int values = 1;
for (index i = 0; i != 3; ++i)
    for (index j = 0; j != 4; ++j)
        for (index k = 0; k != 2; ++k)
            A[i][j][k] = values++;

std::cout << "A:" << std::endl;
for (index i = 0; i != 3; ++i)
    for (index j = 0; j != 4; ++j)
        for (index k = 0; k != 2; ++k)
            std::cout << A[i][j][k] << std::endl;

auto B = A++;

std::cout << "After executing auto B = A++" << std::endl;

std::cout << "A:" << std::endl;
for (index i = 0; i != 3; ++i)
    for (index j = 0; j != 4; ++j)
        for (index k = 0; k != 2; ++k)
            std::cout << A[i][j][k] << std::endl;

std::cout << "B:" << std::endl;
for (index i = 0; i != 3; ++i)
    for (index j = 0; j != 4; ++j)
        for (index k = 0; k != 2; ++k)
            std::cout << B[i][j][k] << std::endl;

The whole experimental code can be checked at here.

All suggestions are welcome.

The summary information:

\$\endgroup\$
1
\$\begingroup\$

This is wrong:

auto operator++(T& input)
{
    input = recursive_transform(input, [](auto& x) {return x + 1; });

So the outer call to operator++ causes recursion, but the recursive call uses + 1 instead of ++. There might be types where these operations are not equivalent, where only one of the two operations is supported, and/or where one of them has very different performance. Make sure you use operator++ on all levels of the recursion.

\$\endgroup\$
4
  • \$\begingroup\$ Thank you for the answer. About the part of using operator++ on all levels of the recursion, this means in both prefix increment operator and postfix increment operator? \$\endgroup\$ – JimmyHu Nov 6 '20 at 14:24
  • 1
    \$\begingroup\$ Yes, the prefix version should return ++x, the postfix version return x++. \$\endgroup\$ – G. Sliepen Nov 6 '20 at 15:19
  • \$\begingroup\$ Thank you for the reply. In the recursion process, there is something like boost::detail::multi_array::const_sub_array which can't apply ++ successfully. Is there any way to deal with this? \$\endgroup\$ – JimmyHu Nov 7 '20 at 1:55
  • 1
    \$\begingroup\$ Well if it's const you cannot modify it, so ++ and -- operators can't be used on them. There is nothing you can do about that. \$\endgroup\$ – G. Sliepen Nov 7 '20 at 8:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.