I would like to make an __attribute__((packed))
version of a template class, to use it in packed structs to send or receive as bytes. But I don't want the cost of packed to impact the arithmetic and other aspects on the regular use of the class (compiling for ARM using GCC with c++14).
So I have a template class like this example:
template<typename T>
struct Vec3 {
T x, y, z;
constexpr Vec3() = default;
constexpr Vec3(T x, T y, T z) : x(x), y(y), z(z) {}
constexpr Vec3& operator=(const Vec3& v);
constexpr Vec3 operator+(const Vec3& v) const;
template<typename U>
constexpr auto operator+(const Vec3<U>& v) const -> Vec3<decltype(T{} + U{})>;
// many other member functions
}
template<typename T, typename U>
constexpr auto dot(const Vec3<T>& v1, const Vec3<U>& v2) -> decltype(T{} * U{});
// other non-member functions
Now I would like to make a packed version of the Vec3 class, something like:
#include <type_traits>
template<typename T>
class PackedType;
template<typename T>
using Packed = std::conditional_t<std::is_arithmetic<T>::value, T, PackedType<T>>;
template<typename T>
struct __attribute__((packed)) PackedType<Vec3<T>> {
Packed<T> x, y, z; // make sure the internal type is packable
constexpr PackedType(const Vec3<T>& v) : x(v.x), y(v.y), z(v.z) {}
constexpr operator Vec3<T>() const { return Vec3<T>(T(x), T(y), T(z)); }
}
But this is where it breaks down. The call to function expecting Vec3
use template argument deduction/substitution, but PackedType<Vec3<T>>
isn't a Vec3
, nor is it derived from Vec3
. So the compiler cannot deduce anything.
We would like the packed Vec3
class to be converted to a regular Vec3
when used in functions. As I said previously, we don't want/need operation to be performed on the packed values, so making a copy is fine.
But now we need to provide overload for all functions to use the packed version of the class, plus all the (regular, packed), (packed, regular), (packed, packed) for function with two arguments and so on... And I don't mean just the Vec3.h/cpp file, any function that as a templated Vec3<T>
as argument might need to be overloaded.
As for the other options:
Vec3
class cannot be derived from Vec3
since regular Vec3
isn't packed (my version of GCC does not raise an error on that one even though it is probably undefined behavior).template<typename T, bool Packed> struct Vec3 {}
and a specialization for packed and non-packed doesn't solve the issue of having to duplicate everything.v.unpack()
every time we need to make a conversion (and cry at the compiler output if you forget one)To be clear, what I would like is a way to convert automatically a packed class into a regular class, the same way a c array decay into a pointer for example.
The answer suggested in a comment does the trick.
#include <type_traits>
template<typename T>
struct Vec3 {
using type = T;
T x, y, z;
//...
}
template<typename T>
struct isVec3Conv : std::false_type {};
template<typename T>
struct isVec3Conv<Vec3<T>> : std::true_type {};
template<typename T>
struct isVec3Conv<PackedType<Vec3<T>>> : std::true_type {};
template<typename V1, typename V2,
std::enable_if_t<isVec3Conv<V1>::value && isVec3Conv<V2>::value, bool> = 0>
constexpr auto dot(const V1& v1, const V2& v2) ->
decltype(typename V1::type{} * typename V2::type{});
In that case we can replace typename V1::type{}
by v1.x
, but my classes have more template type parameters and so my implementation become a bit longer because of that.