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?
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.
1 Answer 1
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 requires
ments, 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_
) assert
ion 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-
).
Explore related questions
See similar questions with these tags.