I am implementing expression templates for Vector3<> template class in C++. Actually I have it working very well, but only when arguments of the same type are involved. Lets say Vector3<double> and Vector3<double> or double scalar and Vector3<double>, etc. In the case of for example multiplying Vector3<double> and Vector3<float> compiler prompts error, as it these are two different objects and it is unable to perform float to double conversion by itself.
To solve this issue I had two ideas:
1. Define all necessary classes and operators in terms of possible interactions float and double, float and int etc. Which is very very wrong approach
2. Create templates using auto and decltype
I managed to achieve somewhat a success. I can create a Vector3<anything1> and perform operations on it with other Vector3<anything2>. So it is a clean and nice solution, as it does not require to define all possible conversions. However when I try to perform an operation between any scalar and Vector3<anything> I get errors.
Different operators (in the code below it would be Vector3<DATA_TYPE_L>*Vector3<DATA_TYPE_R> and scalar DATA_TYPE_L*Vector3<DATA_TYPE_R>) interfere. Separately both operators work fine. However when both are present compiler always chooses scalar*Vector3<> operator, and of course fails to deduce arguments when Vector3<>*Vector3 operator should be used.
I managed to solve this situation only by providing additional class Scalar<DATA_TYPE> which embraces the scalar value. However then I have to call some function creating Scalar class explicitly out of let's say double. This is something I would like to avoid.
So I thought of two solutions:
1. Make the operator create Scalar class transparently out of float or double etc. so no explicit usage of function creating Scalar would be needed
2. Redesign the code so operators wouldn't interfere
However I did not manage to achieve any of those. Any help would be very appreciated.
The Code below works fine in VS2015. Remove comments to get mentioned error.
typedef int SIZE_INT;
template <class DATA_TYPE, class Expression>
class Vector3Expression
{
public:
auto operator[](SIZE_INT i) const { return static_cast<Expression const &>(*this)[i]; }
operator Expression&() { return static_cast< Expression&>(*this); }
operator Expression const&() const { return static_cast<const Expression&>(*this); }
};
template <class DATA_TYPE>
class Vector3 :public Vector3Expression<DATA_TYPE, Vector3<DATA_TYPE> >
{
public:
DATA_TYPE Data[3];
Vector3() {}
Vector3(const Vector3& that)
{
Data[0] = that.Data[0];
Data[1] = that.Data[1];
Data[2] = that.Data[2];
}
Vector3(Vector3&& that)
{
std::move(that.Data, 3, Data);
}
template <class OTHER_DATA_TYPE, class Expression>
Vector3(Vector3Expression<OTHER_DATA_TYPE, Expression> const& VE)
{
Expression const& ve = VE;
Data[0] = ve[0];
Data[1] = ve[1];
Data[2] = ve[2];
}
//access operators
DATA_TYPE& operator[](SIZE_INT i) { return Data[i]; }
DATA_TYPE operator[](SIZE_INT i) const { return Data[i]; }
DATA_TYPE& operator()(SIZE_INT i) { return Data[i]; }
DATA_TYPE operator()(SIZE_INT i) const { return Data[i]; }
//operators
Vector3<DATA_TYPE>& operator=(Vector3<DATA_TYPE> &&that)
{
if (&that != this)
{
std::swap(Data[0], that.Data[0]);
std::swap(Data[1], that.Data[1]);
std::swap(Data[2], that.Data[2]);
}
return *this;
}
Vector3<DATA_TYPE>& operator=(const Vector3<DATA_TYPE> &that)
{
if (&that != this)
{
Data[0] = that.Data[0];
Data[1] = that.Data[1];
Data[2] = that.Data[2];
}
return *this;
}
SIZE_INT Length() const {
return SIZE_INT(3);
}
};
template <class DATA_TYPE_LHS, class DATA_TYPE_RHS, class Lhs, class Operator, class Rhs>
class Vector3Vector3Operation : public Vector3Expression<decltype(Operator::Apply(DATA_TYPE_LHS(), DATA_TYPE_RHS())), Vector3Vector3Operation<DATA_TYPE_LHS, DATA_TYPE_RHS, Lhs, Operator, Rhs> >
{
private:
Lhs const& vL;
Rhs const& vR;
public:
Vector3Vector3Operation(Vector3Expression<DATA_TYPE_LHS, Lhs> const & lhs, Vector3Expression<DATA_TYPE_RHS, Rhs> const & rhs) : vL(lhs), vR(rhs) {}
auto operator[](SIZE_INT i) const { return Operator::Apply(vL[i], vR[i]); }
};
template <class DATA_TYPE_LHS, class DATA_TYPE_RHS, class Operator, class Vec>
class ScalarVector3Operation : public Vector3Expression<decltype(Operator::Apply(DATA_TYPE_LHS(), DATA_TYPE_RHS())), ScalarVector3Operation<DATA_TYPE_LHS, DATA_TYPE_RHS, Operator, Vec> >
{
private:
const DATA_TYPE_LHS S;
Vec const& V;
public:
ScalarVector3Operation(const DATA_TYPE_LHS s, Vector3Expression<DATA_TYPE_RHS, Vec> const& v) : V(v), S(s) {}
auto operator[](SIZE_INT i) const { return Operator::Apply(S, V[i]); }
};
namespace meta {
template<class DATA_TYPEL, class DATA_TYPER = DATA_TYPEL>
struct Multiply
{
inline static auto Apply(DATA_TYPEL a, DATA_TYPER b)->decltype(a*b) { return a*b; }
};
}
template <class DATA_TYPEL, class DATA_TYPER, class LExpression, class RExpression>
Vector3Vector3Operation<DATA_TYPEL, DATA_TYPER, LExpression, meta::Multiply<DATA_TYPEL, DATA_TYPER>, RExpression> const
operator*(Vector3Expression<DATA_TYPEL, LExpression> const& u, Vector3Expression<DATA_TYPER, RExpression> const& v)
{
return Vector3Vector3Operation<DATA_TYPEL, DATA_TYPER, LExpression, meta::Multiply<DATA_TYPEL, DATA_TYPER>, RExpression>(u, v);
}
/*
//operator causing trouble
template <class DATA_TYPE_LHS, class DATA_TYPE_RHS, class RExpression>
ScalarVector3Operation<DATA_TYPE_LHS, DATA_TYPE_RHS, meta::Multiply<DATA_TYPE_LHS, DATA_TYPE_RHS>, RExpression> const
operator*(const DATA_TYPE_LHS s, Vector3Expression<DATA_TYPE_RHS, RExpression> const& v)
{
return ScalarVector3Operation<DATA_TYPE_LHS, DATA_TYPE_RHS, meta::Multiply<DATA_TYPE_LHS, DATA_TYPE_RHS>, RExpression>(s.value, v);
}
*/
int main()
{
Vector3<double> d, x, u;
Vector3<float> f;
double s = 0.25;
d[0] = 1; d[1] = 2; d[2] = 3;
x = d;
f[0] = 3; f[1] = 2; f[2] = 1;
u = d*f;
std::cout << u[0] << " " << u[1] << " " << u[2] << std::endl;
u = d*x;
std::cout << u[0] << " " << u[1] << " " << u[2] << std::endl;
//u = s*d*x; //does not work
std::cout << u[0] << " " << u[1] << " " << u[2] << std::endl;
getchar();
return 0;
}