1

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?

Makogan
  • 8,208
  • 7
  • 44
  • 112

0 Answers0