(en trois parties)
//> Helper tag.
template <auto EnumValue, typename T>
struct ChoiceValue final
{
using value_type = T;
constexpr static const auto enum_value = EnumValue;
template <typename X>
static bool TryConstruct(decltype(EnumValue) enumValue, X storage) {
if (value != EnumValue)
return false;
new (storage) value_type();
return true;
}
template <typename X>
static bool TryDestruct(decltype(EnumValue) enumValue, X storage) {
if (enumValue != EnumValue)
return false;
reinterpret_cast<value_type*>(storage)->~value_type();
return true;
}
template <typename X>
static bool TryWrite(BitStream& stream, decltype(EnumValue) enumValue, X storage)
{
if (enumValue != EnumValue)
return false;
stream.Write(*reinterpret_cast<value_type*>(storage));
return true;
}
template <typename X>
static bool TryRead(BitStream& stream, decltype(EnumValue) enumValue, X storage)
{
if (enumValue != EnumValue)
return false;
stream >> *reinterpret_cast<value_type*>(storage);
return true;
}
};
// Glorified union, BSN style.
template <typename ChoiceType, size_t ChoiceBits, typename... Ts>
struct Choice final
{
static_assert(std::is_enum_v<ChoiceType> && !std::is_convertible_v<ChoiceType, int>, "ChoiceType must be a strongly typed enum.");
static_assert((std::is_same_v<ChoiceType, std::decay_t<decltype(Ts::enum_value)>> && ...), "At least one element type doesn't match the enumeration type.");
protected:
using storage_type = std::aligned_union_t<sizeof(Null), typename Ts::value_type...>;
private:
// Helper to retrieve the index of a type in a tuple
template <size_t I, ChoiceType Choice, typename Tuple>
struct choice_index_in_pack
{
static_assert(I < std::tuple_size_v<Tuple>, "Choice not found in tuple");
// ChoiceValue<enum, type> of Tuple at I
using ith_type = std::tuple_element_t<I, Tuple>;
// Collapsing resolution
using tag_type = std::conditional_t<ith_type::element_choice == Choice,
ith_type,
typename choice_index_in_pack<I + 1, Choice, Tuple>::tag_type
>;
// ChoiceValue<enum, type>::type with enum = Choice
using value_type = typename tag_type::value_type;
};
template <ChoiceType Choice>
using value_type_t = typename choice_index_in_pack<0, Choice, std::tuple<Ts...>>::value_type;
template <ChoiceType Choice>
using tag_type = typename choice_index_in_pack<0, Choice, std::tuple<Ts...>>::tag_type;
public:
Choice() : _choice(ChoiceType {})
{
}
~Choice()
{
TryDestruct();
}
template <ChoiceType Choice>
bool SetType()
{
if (!TryConstruct(Choice))
return false;
_choice = Choice;
return true;
}
ChoiceType GetType() const { return _choice; }
template <ChoiceType Type>
auto GetValue() -> value_type_t<Type>*
{
if (Type != _choice)
return nullptr;
return reinterpret_cast<value_type_t<Type>*>(&_storage);
}
friend BitStream& operator << (BitStream& stream, Choice<ChoiceType, ChoiceBits, Ts...> const& choice)
{
choice.TryWrite(stream);
return stream;
}
friend BitStream& operator >> (BitStream& stream, Choice<ChoiceType, ChoiceBits, Ts...>& choice)
{
choice.TryRead(stream);
return stream;
}
private:
bool TryConstruct(ChoiceType selectedValue)
{
bool result = (Ts::TryConstruct(selectedValue, &_storage) || ...);
if (result)
_initialized = true;
return result;
}
bool TryDestruct()
{
if (!_initialized)
return true;
_initialized = !(Ts::TryDestruct(_choice, &_storage) || ...);
return !_initialized;
}
bool TryRead(BitStream& stream)
{
ChoiceType wireType = stream.Read<ChoiceType>(ChoiceBits);
_initialized = (Ts::TryRead(stream, wireType, &_storage) || ...);
if (_initialized)
_choice = wireType;
return _initialized;
}
bool TryWrite(BitStream& stream) const
{
if (!_initialized)
return false;
_stream.Write<ChoiceType>(_choice, ChoiceBits);
return (Ts::TryWrite(stream, _choice, &_storage) || ...);
}
ChoiceType _choice{ };
storage_type _storage;
bool _initialized = false;
};
namespace FieldSpec
{
enum class SizeType : bool
{
Fixed = 0,
Variable = 1
};
namespace Size
{
namespace _Internal = Battlenet::_Internal;
using Fixed = TypeSize;
using Variable = Battlenet::_Internal::Null;
using Choice = _Internal::Choice<SizeType, 1, _Internal::ChoiceValue<SizeType::Fixed, Fixed>, _Internal::ChoiceValue<SizeType::Variable, Variable>>;
};
}
struct TC_SHARED_API FieldSpecUpdate // FieldSpec in actual BSNs
{
TypeEnum::Type Id;
bool Writable;
bool Ephemeral;
bool ServerOnly;
bool ClientOnly;
FieldSpec::Size::Choice Size;
};
FieldSpecUpdate update;
update.Size.SetType<FieldSpec::SizeType::Fixed>();
*update.Size.GetValue<FieldSpec::SizeType::Fixed>() = 0xBEEF;