[fidl][cpp] Bits are represented with class
Far from a fashion statement, representing bits with class rather than
an enum makes it possible to have a conversion operator to a numerical
type (and hence to bool).
This makes it possible to write `if (my_bits_value) { ...`.
A downside is that doing an exhaustive match against a bits value in a
switch statement is no longer helped by compiler warnings... However,
using a bits value in a switch statement and matching against possible
bits members is likely an incorrect pattern in the first place. (This
revealed a bug fostr's handling of bits values, which will be fixed in a
subsequent CL.)
FIDL-620 #done
FIDL-683 #comment no testing infra, tracked in FIDL-623
FIDL-623 #comment test FIDL-683
Test: added some for new behavior, coverage existed for the rest
Change-Id: I922f3e9943d75d44a57dcfc68ee326378fab0ede
diff --git a/garnet/go/src/fidl/compiler/backend/cpp/templates/bits.tmpl.go b/garnet/go/src/fidl/compiler/backend/cpp/templates/bits.tmpl.go
index 20f6dae..55e4403 100644
--- a/garnet/go/src/fidl/compiler/backend/cpp/templates/bits.tmpl.go
+++ b/garnet/go/src/fidl/compiler/backend/cpp/templates/bits.tmpl.go
@@ -6,13 +6,34 @@
const Bits = `
{{- define "BitsForwardDeclaration" }}
-enum class {{ .Name }} : {{ .Type }} {
+class {{ .Name }} final {
+public:
+ constexpr {{ .Name }}() : value_(0u) {}
+ explicit constexpr {{ .Name }}({{ .Type }} value) : value_(value) {}
+
{{- range .Members }}
- {{ .Name }} = {{ .Value }},
+ const static {{ $.Name }} {{ .Name }};
{{- end }}
+ const static {{ .Name }} mask;
+
+ explicit constexpr inline operator {{ .Type }}() const { return value_; }
+ constexpr inline operator bool() const { return value_; }
+ constexpr inline {{ .Name }} operator~() const;
+ constexpr inline {{ .Name }} operator|(const {{ .Name }}& other) const;
+ constexpr inline {{ .Name }} operator&(const {{ .Name }}& other) const;
+ constexpr inline {{ .Name }} operator^(const {{ .Name }}& other) const;
+ constexpr inline void operator|=(const {{ .Name }}& other);
+ constexpr inline void operator&=(const {{ .Name }}& other);
+ constexpr inline void operator^=(const {{ .Name }}& other);
+
+private:
+ {{ .Type }} value_;
};
-const static {{ .Name }} {{ .MaskName }} = static_cast<{{ .Name }}>({{ .Mask }}u);
+{{- range $member := .Members }}
+constexpr const {{ $.Namespace }}::{{ $.Name }} {{ $.Name }}::{{ $member.Name }} = {{ $.Namespace }}::{{ $.Name }}({{ $member.Value }});
+{{- end }}
+constexpr const {{ .Namespace }}::{{ .Name }} {{ .Name }}::mask = {{ $.Namespace }}::{{ $.Name }}({{ .Mask }}u);
inline zx_status_t Clone({{ .Namespace }}::{{ .Name }} value,
{{ .Namespace }}::{{ .Name }}* result) {
@@ -20,45 +41,38 @@
return ZX_OK;
}
-constexpr inline {{ .Namespace }}::{{ .Name }} operator|({{ .Namespace }}::{{ .Name }} _lhs,
- {{ .Namespace }}::{{ .Name }} _rhs) {
- return static_cast<{{ .Namespace }}::{{ .Name }}>(
- static_cast<{{ .Type }}>(_lhs) | static_cast<{{ .Type }}>(_rhs));
+constexpr inline {{ .Namespace }}::{{ .Name }} {{ .Name }}::operator~() const {
+ return {{ $.Namespace }}::{{ $.Name }}(~this->value_ & static_cast<{{ .Type }}>(mask));
}
-constexpr inline {{ .Namespace }}::{{ .Name }}& operator|=({{ .Namespace }}::{{ .Name }}& _lhs,
- {{ .Namespace }}::{{ .Name }} _rhs) {
- _lhs = _lhs | _rhs;
- return _lhs;
+constexpr inline {{ .Namespace }}::{{ .Name }} {{ .Name }}::operator|(
+ const {{ .Namespace }}::{{ .Name }}& other) const {
+ return {{ $.Namespace }}::{{ $.Name }}(this->value_ | other.value_);
}
-constexpr inline {{ .Namespace }}::{{ .Name }} operator&({{ .Namespace }}::{{ .Name }} _lhs,
- {{ .Namespace }}::{{ .Name }} _rhs) {
- return static_cast<{{ .Namespace }}::{{ .Name }}>(
- static_cast<{{ .Type }}>(_lhs) & static_cast<{{ .Type }}>(_rhs));
+constexpr inline {{ .Namespace }}::{{ .Name }} {{ .Name }}::operator&(
+ const {{ .Namespace }}::{{ .Name }}& other) const {
+ return {{ $.Namespace }}::{{ $.Name }}(this->value_ & other.value_);
}
-constexpr inline {{ .Namespace }}::{{ .Name }}& operator&=({{ .Namespace }}::{{ .Name }}& _lhs,
- {{ .Namespace }}::{{ .Name }} _rhs) {
- _lhs = _lhs & _rhs;
- return _lhs;
+constexpr inline {{ .Namespace }}::{{ .Name }} {{ .Name }}::operator^(
+ const {{ .Namespace }}::{{ .Name }}& other) const {
+ return {{ $.Namespace }}::{{ $.Name }}(this->value_ ^ other.value_);
}
-constexpr inline {{ .Namespace }}::{{ .Name }} operator^({{ .Namespace }}::{{ .Name }} _lhs,
- {{ .Namespace }}::{{ .Name }} _rhs) {
- return static_cast<{{ .Namespace }}::{{ .Name }}>(
- static_cast<{{ .Type }}>(_lhs) ^ static_cast<{{ .Type }}>(_rhs));
+constexpr inline void {{ .Name }}::operator|=(
+ const {{ .Namespace }}::{{ .Name }}& other) {
+ this->value_ |= other.value_;
}
-constexpr inline {{ .Namespace }}::{{ .Name }}& operator^=({{ .Namespace }}::{{ .Name }}& _lhs,
- {{ .Namespace }}::{{ .Name }} _rhs) {
- _lhs = _lhs ^ _rhs;
- return _lhs;
+constexpr inline void {{ .Name }}::operator&=(
+ const {{ .Namespace }}::{{ .Name }}& other) {
+ this->value_ &= other.value_;
}
-constexpr inline {{ .Namespace }}::{{ .Name }} operator~({{ .Namespace }}::{{ .Name }} _value) {
- return static_cast<{{ .Namespace }}::{{ .Name }}>(
- ~static_cast<{{ .Type }}>(_value) & static_cast<{{ .Type }}>({{ .MaskName }}));
+constexpr inline void {{ .Name }}::operator^=(
+ const {{ .Namespace }}::{{ .Name }}& other) {
+ this->value_ ^= other.value_;
}
{{ end }}
@@ -73,7 +87,7 @@
static void Decode(Decoder* decoder, {{ .Namespace }}::{{ .Name }}* value, size_t offset) {
{{ .Type }} underlying = {};
::fidl::Decode(decoder, &underlying, offset);
- *value = static_cast<{{ .Namespace }}::{{ .Name }}>(underlying);
+ *value = {{ $.Namespace }}::{{ $.Name }}(underlying);
}
};
diff --git a/garnet/go/src/fidl/compiler/backend/goldens/bits.test.test.fidl.json.h.golden b/garnet/go/src/fidl/compiler/backend/goldens/bits.test.test.fidl.json.h.golden
index 0fd058c..2c9fbd9 100644
--- a/garnet/go/src/fidl/compiler/backend/goldens/bits.test.test.fidl.json.h.golden
+++ b/garnet/go/src/fidl/compiler/backend/goldens/bits.test.test.fidl.json.h.golden
@@ -8,12 +8,30 @@
namespace test {
namespace name {
-enum class MyBits : uint32_t {
- MY_FIRST_BIT = 1u,
- MY_OTHER_BIT = 2u,
-};
+class MyBits final {
+public:
+ constexpr MyBits() : value_(0u) {}
+ explicit constexpr MyBits(uint32_t value) : value_(value) {}
+ const static MyBits MY_FIRST_BIT;
+ const static MyBits MY_OTHER_BIT;
+ const static MyBits mask;
-const static MyBits MyBitsMask = static_cast<MyBits>(3u);
+ explicit constexpr inline operator uint32_t() const { return value_; }
+ constexpr inline operator bool() const { return value_; }
+ constexpr inline MyBits operator~() const;
+ constexpr inline MyBits operator|(const MyBits& other) const;
+ constexpr inline MyBits operator&(const MyBits& other) const;
+ constexpr inline MyBits operator^(const MyBits& other) const;
+ constexpr inline void operator|=(const MyBits& other);
+ constexpr inline void operator&=(const MyBits& other);
+ constexpr inline void operator^=(const MyBits& other);
+
+private:
+ uint32_t value_;
+};
+constexpr const ::test::name::MyBits MyBits::MY_FIRST_BIT = ::test::name::MyBits(1u);
+constexpr const ::test::name::MyBits MyBits::MY_OTHER_BIT = ::test::name::MyBits(2u);
+constexpr const ::test::name::MyBits MyBits::mask = ::test::name::MyBits(3u);
inline zx_status_t Clone(::test::name::MyBits value,
::test::name::MyBits* result) {
@@ -21,45 +39,38 @@
return ZX_OK;
}
-constexpr inline ::test::name::MyBits operator|(::test::name::MyBits _lhs,
- ::test::name::MyBits _rhs) {
- return static_cast<::test::name::MyBits>(
- static_cast<uint32_t>(_lhs) | static_cast<uint32_t>(_rhs));
+constexpr inline ::test::name::MyBits MyBits::operator~() const {
+ return ::test::name::MyBits(~this->value_ & static_cast<uint32_t>(mask));
}
-constexpr inline ::test::name::MyBits& operator|=(::test::name::MyBits& _lhs,
- ::test::name::MyBits _rhs) {
- _lhs = _lhs | _rhs;
- return _lhs;
+constexpr inline ::test::name::MyBits MyBits::operator|(
+ const ::test::name::MyBits& other) const {
+ return ::test::name::MyBits(this->value_ | other.value_);
}
-constexpr inline ::test::name::MyBits operator&(::test::name::MyBits _lhs,
- ::test::name::MyBits _rhs) {
- return static_cast<::test::name::MyBits>(
- static_cast<uint32_t>(_lhs) & static_cast<uint32_t>(_rhs));
+constexpr inline ::test::name::MyBits MyBits::operator&(
+ const ::test::name::MyBits& other) const {
+ return ::test::name::MyBits(this->value_ & other.value_);
}
-constexpr inline ::test::name::MyBits& operator&=(::test::name::MyBits& _lhs,
- ::test::name::MyBits _rhs) {
- _lhs = _lhs & _rhs;
- return _lhs;
+constexpr inline ::test::name::MyBits MyBits::operator^(
+ const ::test::name::MyBits& other) const {
+ return ::test::name::MyBits(this->value_ ^ other.value_);
}
-constexpr inline ::test::name::MyBits operator^(::test::name::MyBits _lhs,
- ::test::name::MyBits _rhs) {
- return static_cast<::test::name::MyBits>(
- static_cast<uint32_t>(_lhs) ^ static_cast<uint32_t>(_rhs));
+constexpr inline void MyBits::operator|=(
+ const ::test::name::MyBits& other) {
+ this->value_ |= other.value_;
}
-constexpr inline ::test::name::MyBits& operator^=(::test::name::MyBits& _lhs,
- ::test::name::MyBits _rhs) {
- _lhs = _lhs ^ _rhs;
- return _lhs;
+constexpr inline void MyBits::operator&=(
+ const ::test::name::MyBits& other) {
+ this->value_ &= other.value_;
}
-constexpr inline ::test::name::MyBits operator~(::test::name::MyBits _value) {
- return static_cast<::test::name::MyBits>(
- ~static_cast<uint32_t>(_value) & static_cast<uint32_t>(MyBitsMask));
+constexpr inline void MyBits::operator^=(
+ const ::test::name::MyBits& other) {
+ this->value_ ^= other.value_;
}
} // namespace name
@@ -76,7 +87,7 @@
static void Decode(Decoder* decoder, ::test::name::MyBits* value, size_t offset) {
uint32_t underlying = {};
::fidl::Decode(decoder, &underlying, offset);
- *value = static_cast<::test::name::MyBits>(underlying);
+ *value = ::test::name::MyBits(underlying);
}
};
diff --git a/garnet/public/build/fostr/fostr.fidlmerge b/garnet/public/build/fostr/fostr.fidlmerge
index 623b526..6086c35 100644
--- a/garnet/public/build/fostr/fostr.fidlmerge
+++ b/garnet/public/build/fostr/fostr.fidlmerge
@@ -176,12 +176,17 @@
{{- $type_name := .Name.Parts.Name }}
std::ostream& operator<<(std::ostream& os, const {{ .Name.Parts.Name }}& value) {
using fidl::operator<<;
- switch (value) {
+ bool is_first = true;
{{- range .Members }}
- case {{ $type_name }}::{{ .Name }}:
- return os << "{{ toFriendlyCase .Name }}";
- {{- end }}
+ if (value & {{ $type_name }}::{{ .Name }}) {
+ os << "{{ toFriendlyCase .Name }}";
+ if (is_first) {
+ is_first = false;
+ } else {
+ os << " | ";
+ }
}
+ {{- end }}
return os;
}
diff --git a/sdk/lib/fidl/cpp/bits_unittest.cc b/sdk/lib/fidl/cpp/bits_unittest.cc
index 69cabd9..5294d3d 100644
--- a/sdk/lib/fidl/cpp/bits_unittest.cc
+++ b/sdk/lib/fidl/cpp/bits_unittest.cc
@@ -60,7 +60,19 @@
TEST(Bits, IsConstexpr) {
static constexpr auto this_should_compile = SampleBits::B | SampleBits::D | SampleBits::E;
- EXPECT_EQ(this_should_compile, SampleBitsMask);
+ EXPECT_EQ(this_should_compile, SampleBits::mask);
+}
+
+TEST(Bits, CanConvertToNumberButMustBeExplicit) {
+ uint8_t r8 = static_cast<uint32_t>(SampleBits::B);
+ EXPECT_EQ(r8, 2u);
+ uint16_t r16 = static_cast<uint32_t>(SampleBits::B);
+ EXPECT_EQ(r16, 2u);
+}
+
+TEST(Bits, CanConvertToBool) {
+ bool result = SampleBits::B;
+ EXPECT_TRUE(result);
}
} // namespace
diff --git a/src/connectivity/wlan/drivers/wlanif/convert.cpp b/src/connectivity/wlan/drivers/wlanif/convert.cpp
index 589db09..54fcc00 100644
--- a/src/connectivity/wlan/drivers/wlanif/convert.cpp
+++ b/src/connectivity/wlan/drivers/wlanif/convert.cpp
@@ -991,50 +991,50 @@
}
wlan_mlme::MgmtFrameCaptureFlags ConvertMgmtCaptureFlags(uint32_t ddk_flags) {
- uint32_t ret_flags = 0;
+ wlan_mlme::MgmtFrameCaptureFlags ret_flags;
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_ASSOC_REQ) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::ASSOC_REQ);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::ASSOC_REQ;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_ASSOC_RESP) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::ASSOC_RESP);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::ASSOC_RESP;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_REASSOC_REQ) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::REASSOC_REQ);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::REASSOC_REQ;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_REASSOC_RESP) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::REASSOC_RESP);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::REASSOC_RESP;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_PROBE_REQ) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::PROBE_REQ);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::PROBE_REQ;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_PROBE_RESP) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::PROBE_RESP);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::PROBE_RESP;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_TIMING_AD) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::TIMING_AD);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::TIMING_AD;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_BEACON) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::BEACON);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::BEACON;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_ATIM) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::ATIM);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::ATIM;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_DISASSOC) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::DISASSOC);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::DISASSOC;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_AUTH) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::AUTH);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::AUTH;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_DEAUTH) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::DEAUTH);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::DEAUTH;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_ACTION) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::ACTION);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::ACTION;
}
if ((ddk_flags & WLAN_MGMT_CAPTURE_FLAG_ACTION_NO_ACK) != 0) {
- ret_flags |= static_cast<uint32_t>(wlan_mlme::MgmtFrameCaptureFlags::ACTION_NO_ACK);
+ ret_flags |= wlan_mlme::MgmtFrameCaptureFlags::ACTION_NO_ACK;
}
- return static_cast<wlan_mlme::MgmtFrameCaptureFlags>(ret_flags);
+ return ret_flags;
}
} // namespace wlanif