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:

asked Nov 6, 2020 at 1:50
\$\endgroup\$

1 Answer 1

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.

answered Nov 6, 2020 at 10:03
\$\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\$ Commented Nov 6, 2020 at 14:24
  • 1
    \$\begingroup\$ Yes, the prefix version should return ++x, the postfix version return x++. \$\endgroup\$ Commented Nov 6, 2020 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\$ Commented Nov 7, 2020 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\$ Commented Nov 7, 2020 at 8:21

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.