The vulan enums in the hpp class use a template wrapper to define bitwise operations to guarantee safety.
It looks roughly like this:
template<typename BitType> class Flags
{
public:
using MaskType = typename std::underlying_type<BitType>::type;
// constructors
constexpr Flags()
: m_mask(0)
{
}
constexpr Flags(BitType bit)
: m_mask(static_cast<MaskType>(bit))
{
}
constexpr Flags(Flags<BitType> const& rhs)
: m_mask(rhs.m_mask)
{
}
constexpr Flags(MaskType flags)
: m_mask(flags)
{
}
auto operator<=>(Flags<BitType> const&) const = default;
// logical operator
constexpr bool operator!() const { return !m_mask; }
// bitwise operators
constexpr Flags<BitType> operator&(Flags<BitType> const& rhs) const
{
return Flags<BitType>(m_mask & rhs.m_mask);
}
constexpr Flags<BitType> operator|(Flags<BitType> const& rhs) const
{
return Flags<BitType>(m_mask | rhs.m_mask);
}
constexpr Flags<BitType> operator^(Flags<BitType> const& rhs) const
{
return Flags<BitType>(m_mask ^ rhs.m_mask);
}
// assignment operators
constexpr Flags<BitType>& operator=(Flags<BitType> const& rhs)
{
m_mask = rhs.m_mask;
return *this;
}
constexpr Flags<BitType>& operator|=(Flags<BitType> const& rhs)
{
m_mask |= rhs.m_mask;
return *this;
}
constexpr Flags<BitType>& operator&=(Flags<BitType> const& rhs)
{
m_mask &= rhs.m_mask;
return *this;
}
constexpr Flags<BitType>& operator^=(Flags<BitType> const& rhs)
{
m_mask ^= rhs.m_mask;
return *this;
}
// Arithmetic operators
constexpr Flags<BitType>& operator*=(Flags<BitType> const& rhs)
{
m_mask *= rhs.m_mask;
return *this;
}
constexpr Flags<BitType>& operator+=(Flags<BitType> const& rhs)
{
m_mask += rhs.m_mask;
return *this;
}
constexpr Flags<BitType>& operator*(Flags<BitType> const& rhs)
{
return {m_mask * rhs.m_mask};
}
constexpr Flags<BitType>& operator+(Flags<BitType> const& rhs)
{
return {m_mask + rhs.m_mask};
}
// cast operators
explicit constexpr operator bool() const { return !!m_mask; }
explicit constexpr operator MaskType() const { return m_mask; }
private:
MaskType m_mask;
};
The problem is that if you define a cereal function directly, this seems to lead cereal into a hellspcape that after minutes ends in a segfault.
i.e. this is bad:
// cereal function for serialization.
template <class Archive, typename BitType>
void serialize( Archive& archive, Flags<BitType>& tag )
{
archive(
tag
);
}
One way around is to define the masktype as public, then you can do:
// cereal function for serialization.
template <class Archive, typename BitType>
void serialize( Archive& archive, Flags<BitType>& tag )
{
archive(
tag.m_mask
);
}
This works fine but means you have to modify the vulkan.hpp header, which is a terrible idea.
Alternatively you can try to do this:
// cereal function for serialization.
template <class Archive, typename BitType>
void serialize( Archive& archive, Flags<BitType>& tag )
{
archive(
(BitType)tag
);
}
That doesn't end in a segfault but cereal does not load the correct values when I do this, in fact the serialized data is correct but when it gets loaded all teh vulkan enum fields are set to 0 instead of the stored value.
I am out fo ideas, how can get cereal to properly load this data?