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:
Which question it is a follow-up to?
A recursive_transform Template Function for BoostMultiArray and
What changes has been made in the code since last question?
The previous question A recursive_transform Template Function for BoostMultiArray focused on
recursive_transform
template function and An Add/Minus Operator For Boost.MultiArray in C++ just mentioned that the+
and-
operator for Boost.MultiArray. I am trying to propose the++
and--
operator overloading implementation here.Why a new review is being asked for?
I am not sure if this is a good idea to call another function (such as the usage of
recursive_transform
template function here) in++
and--
operator. If there is any possible improvement, please let me know.
1 Answer 1
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.
-
\$\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– JimmyHu2020年11月06日 14:24:03 +00:00Commented Nov 6, 2020 at 14:24 -
1\$\begingroup\$ Yes, the prefix version should
return ++x
, the postfix versionreturn x++
. \$\endgroup\$G. Sliepen– G. Sliepen2020年11月06日 15:19:22 +00:00Commented 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\$JimmyHu– JimmyHu2020年11月07日 01:55:57 +00:00Commented 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\$G. Sliepen– G. Sliepen2020年11月07日 08:21:32 +00:00Commented Nov 7, 2020 at 8:21