Motivation - in abstract mathematics we can generalise 3-dimensional cross product to n dimensions, assuming that for two vectors v,w of dimension n, their cross product will have dimension n(n-1)/2. Skipping the ideas of differential forms, I would like to converse this idea into the language of templates in C++ library. I had this idea:
auto Cross(const Vec<dim, float_t> &rhs) {
if (dim == 0)return;
if (dim == 1)return (float_t)0;
if (dim == 2)return Values[0] * rhs.Values[1] - Values[1] * rhs.Values[0];
if (dim == 3)return Vec<dim, float_t>(
Values[1] * rhs.Values[2] - Values[2] * rhs.Values[1],
Values[2] * rhs.Values[0] - Values[0] * rhs.Values[2],
Values[0] * rhs.Values[1] - Values[1] * rhs.Values[0]);
//...
}
but sadly, auto specifier doesn't allow for different return types. I could of course mess with preprocessor, but I believe that there is more clever way to do it, something like:
Vec<dim*(dim-1)/2,float_t> Cross(const Vec<dim,float_t> &rhs);
(with void as a special case for zero-dimensional point and just float_t for one-dimensional point). How to solve this in a smart way?
I believe you could do this:
template <std::size_t dim>
auto cross(const Vec<dim, float_t>& rhs)
{
/*logic goes here, also you can generalize*/
}
This will create new function for each dim (implicitly). Also, try to do it at compile time rather than runtime by using specializations.
One way to implent this is by means of specializations. Without specialization it would be very hard to implement a truly generic template algorithm, (which I would be interested to see).
If you use specializations you can't use auto for the most part, the return types will have to be explicit. This should do what you want:
#include<cassert>
#include<array>
using std::array;
template<std::size_t Dim>
array<double, Dim*(Dim-1)/2>
Cross(array<double, Dim> const&, array<double, Dim> const&);
template<> array<double, 0> Cross(
array<double, 1> const&, array<double, 1> const& v2
){return {};}
template<> array<double, 1> Cross(
array<double, 2> const& v1, array<double, 2> const& v2
){return {{v1[0] * v2[1] - v1[1] * v2[0]}};}
template<> array<double, 3> Cross(
array<double, 3> const& v1, array<double, 3> const& v2
){
return {{
v1[1] * v2[2] - v1[2] * v2[1],
v1[2] * v2[0] - v1[0] * v2[2],
v1[0] * v2[1] - v1[1] * v2[0]
}};
}
int main(){
array<double, 1> v1{{1.}};
array<double, 2> v2{{1.,2.}};
array<double, 3> v3{{1.,2., 3.}};
assert(( Cross(v1, v1) == array<double, 0>{} ));
assert(( Cross(v2, v2)[0] == 0. ));
assert(( Cross(v3, v3)[0] == 0. ));
}
Now at this point, if you know you are going to work with (total) specializations (like the above), there is little difference between this and separate overloads of Cross (except enforcing a certain return type defined in 6th line of code.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With