4
\$\begingroup\$

This is a follow-up question for An element_wise_add Function For Boost.MultiArray in C++. The following code is the improved version based on G. Sliepen's answer. On the other hand, the built-in function .num_dimensions() I used here for the part of array dimension mismatch detection.

template<typename T>
concept is_multi_array = requires(T x)
{
 x.num_dimensions();
 x.shape();
 boost::multi_array(x);
};
// Add operator
template<class T1, class T2> requires (is_multi_array<T1>&& is_multi_array<T2>)
auto operator+(const T1& input1, const T2& input2)
{
 if (input1.num_dimensions() != input2.num_dimensions()) // Dimensions are different, unable to perform element-wise add operation
 {
 throw std::logic_error("Array dimensions are different");
 }
 if (*input1.shape() != *input2.shape()) // Shapes are different, unable to perform element-wise add operation
 {
 throw std::logic_error("Array shapes are different");
 }
 boost::multi_array output(input1); // [ToDo] drawback to be improved: avoiding copying whole input1 array into output, but with appropriate memory allocation
 for (decltype(+input1.shape()[0]) i = 0; i < input1.shape()[0]; i++)
 {
 output[i] = input1[i] + input2[i];
 }
 return output;
}
// Minus operator
template<class T1, class T2> requires (is_multi_array<T1>&& is_multi_array<T2>)
auto operator-(const T1& input1, const T2& input2)
{
 if (input1.num_dimensions() != input2.num_dimensions()) // Dimensions are different, unable to perform element-wise add operation
 {
 throw std::logic_error("Array dimensions are different");
 }
 if (*input1.shape() != *input2.shape()) // Shapes are different, unable to perform element-wise add operation
 {
 throw std::logic_error("Array shapes are different");
 }
 boost::multi_array output(input1); // [ToDo] drawback to be improved: avoiding copying whole input1 array into output, but with appropriate memory allocation
 for (decltype(+input1.shape()[0]) i = 0; i < input1.shape()[0]; i++)
 {
 output[i] = input1[i] - input2[i];
 }
 return output;
}

The test of the add/minus operator:

int main()
{
 // 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 = 0;
 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++;
 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 sum_result = A;
 for (size_t i = 0; i < 3; i++)
 {
 sum_result = sum_result + A;
 }
 sum_result = sum_result - A;
 
 for (index i = 0; i != 3; ++i)
 for (index j = 0; j != 4; ++j)
 for (index k = 0; k != 2; ++k)
 std::cout << sum_result[i][j][k] << std::endl;
 return 0;
}

All suggestions are welcome.

  • Which question it is a follow-up to?

    An element_wise_add Function For Boost.MultiArray in C++

  • What changes has been made in the code since last question?

    The previous question is the implementation of element_wise_add function for Boost.MultiArray. Based on G. Sliepen's suggestion, this kind of element-wise operations could be implemented with C++ operator overloading. As the result, here comes the operator+ and operator- overload functions for Boost.MultiArray.

  • Why a new review is being asked for?

    The operator+ and operator- overload functions here include the exception handling for the situation of different dimensions and shapes. Please check if this usage is appropriate. Moreover, I am still seeking another smarter method to improve the construction of output object. If there is any better idea for deducing the output structure from input without copying process, please let me know.

asked Oct 31, 2020 at 14:59
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$
if (input1.num_dimensions() != input2.num_dimensions())
{
 throw std::logic_error("Array dimensions are different");
}

If I'm not mistaken, the dimensions are static, and can be accessed through the array class's dimensionality constant, so I think this check could be part of the function requiresments, rather than a run-time error.


if (*input1.shape() != *input2.shape())
{
 throw std::logic_error("Array shapes are different");
}

Is it possible to create an array with num_dimensions() == 0? I think we could use a (probably static_) assertion to prove to ourselves and later maintainers that dereferencing the pointer returned by shape() is safe.

I believe *input1.shape() could be written as input1.size(), which is simpler.


for (decltype(+input1.shape()[0]) i = 0; i < input1.shape()[0]; i++)

Isn't input1.shape()[0] simply *input1.shape()? i.e. input1.size() again.


boost::multi_array output(input1);

So... the type of the output array is always the same as the type of input1? This seems very arbitrary, and could lead to unexpected type differences when simply changing a + b to b + a.

Perhaps we could use decltype(input1[0] + input2[0]) to get a more appropriate "common" element type for the output?


It might be neater to implement operator+= instead, and then implement operator+ using operator+=.

(And similarly for operator-= and operator-).

answered Mar 30, 2022 at 9:12
\$\endgroup\$

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.