wifi: Add and align IEs printing in management frames

This commit is contained in:
Sébastien Deronne
2025-04-17 20:22:13 +02:00
parent da2e1fa359
commit 5b7a948fa9
38 changed files with 184 additions and 90 deletions

View File

@@ -14,7 +14,9 @@ namespace ns3
void
AddbaExtension::Print(std::ostream& os) const
{
os << "extBufferSize=" << +m_extParamSet.extBufferSize;
os << "ADDBA Extension=[No Fragmentation: " << +m_extParamSet.noFragment
<< ", HE Fragmentation Operation: " << +m_extParamSet.heFragmentOp
<< ", Extended Buffer Size: " << +m_extParamSet.extBufferSize << "]";
}
WifiInformationElementId

View File

@@ -303,4 +303,12 @@ EdcaParameterSet::DeserializeInformationField(Buffer::Iterator start, uint16_t l
return length;
}
void
EdcaParameterSet::Print(std::ostream& os) const
{
os << "EDCA Parameter Set=["
<< "QosInfo: " << +m_qosInfo << ", AC_BE: " << m_acBE << ", AC_BK: " << m_acBK
<< ", AC_VI: " << m_acVI << ", AC_VO: " << m_acVO << "]";
}
} // namespace ns3

View File

@@ -18,7 +18,7 @@ namespace ns3
* @brief The EDCA Parameter Set
* @ingroup wifi
*
* This class knows how to serialise and deserialise the EDCA Parameter Set.
* This class knows how to serialize and deserialize the EDCA Parameter Set.
*/
class EdcaParameterSet : public WifiInformationElement
{
@@ -262,6 +262,7 @@ class EdcaParameterSet : public WifiInformationElement
uint16_t GetInformationFieldSize() const override;
void SerializeInformationField(Buffer::Iterator start) const override;
uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
void Print(std::ostream& os) const override;
uint8_t m_qosInfo; ///< QOS info
uint8_t m_reserved; ///< reserved

View File

@@ -415,7 +415,7 @@ EhtCapabilities::ElementIdExt() const
void
EhtCapabilities::Print(std::ostream& os) const
{
os << "EHT Capabilities="; // TODO
os << "EHT Capabilities=[]"; // TODO
}
uint16_t

View File

@@ -18,28 +18,12 @@ namespace ns3
void
EhtOperation::Print(std::ostream& os) const
{
os << "EHT Operation=" << +m_params.opInfoPresent << "|" << +m_params.disabledSubchBmPresent
<< "|" << +m_params.defaultPeDur << "|" << +m_params.grpBuIndLimit << "|"
<< +m_params.grpBuExp << "|[";
for (const auto& maxRxNss : m_mcsNssSet.maxRxNss)
{
os << +maxRxNss << "|";
}
os << "]|[";
for (const auto& maxTxNss : m_mcsNssSet.maxTxNss)
{
os << +maxTxNss << "|";
}
os << "]";
os << "EHT Operation=[";
if (m_opInfo.has_value())
{
os << "|" << +m_opInfo->control.channelWidth << "|" << +m_opInfo->ccfs0 << "|"
<< +m_opInfo->ccfs1;
if (m_opInfo->disabledSubchBm.has_value())
{
os << "|" << m_opInfo->disabledSubchBm.value();
}
os << "Channel Width: " << +m_opInfo->control.channelWidth;
}
os << "]";
}
void

View File

@@ -676,6 +676,23 @@ MultiLinkElement::PerStaProfileSubelement::DeserProbeReqMlePerSta(ns3::Buffer::I
return count;
}
void
MultiLinkElement::PerStaProfileSubelement::Print(std::ostream& os) const
{
os << "Per-STA Profile Subelement=[";
os << "Variant: " << +static_cast<uint8_t>(m_variant);
os << ", STA Control: " << +m_staControl;
if (HasStaMacAddress())
{
os << ", STA MAC Address: " << m_staMacAddress;
}
if (m_bssParamsChgCnt)
{
os << ", BSS Params Change Count: " << +m_bssParamsChgCnt.value();
}
os << "]";
}
void
MultiLinkElement::AddPerStaProfileSubelement()
{
@@ -808,4 +825,15 @@ MultiLinkElement::DeserializeInformationField(Buffer::Iterator start, uint16_t l
return count;
}
void
MultiLinkElement::Print(std::ostream& os) const
{
os << "Multi-Link Element=[Per-STA Profile Subelements: {";
for (const auto& subelement : m_perStaProfileSubelements)
{
subelement.Print(os);
}
os << "}]";
}
} // namespace ns3

View File

@@ -98,6 +98,7 @@ class MultiLinkElement : public WifiInformationElement
uint16_t GetInformationFieldSize() const override;
void SerializeInformationField(Buffer::Iterator start) const override;
uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
void Print(std::ostream& os) const override;
/**
* Get the Multi-Link element variant
@@ -306,6 +307,7 @@ class MultiLinkElement : public WifiInformationElement
PerStaProfileSubelement& operator=(PerStaProfileSubelement&& perStaProfile) = default;
WifiInformationElementId ElementId() const override;
void Print(std::ostream& os) const override;
/**
* Set the Link ID subfield in the STA Control field

View File

@@ -291,4 +291,34 @@ TidToLinkMapping::DeserializeInformationField(Buffer::Iterator start, uint16_t l
return count;
}
void
TidToLinkMapping::Print(std::ostream& os) const
{
os << "TID-To-Link Mapping=["
<< "Direction: " << static_cast<uint16_t>(m_control.direction)
<< ", Default Mapping: " << m_control.defaultMapping
<< ", Link Mapping Size: " << +m_control.linkMappingSize;
if (m_control.mappingSwitchTimePresent)
{
os << ", Mapping Switch Time: " << *m_mappingSwitchTime;
}
if (m_control.expectedDurationPresent)
{
os << ", Expected Duration: " << *m_expectedDuration;
}
os << ", Link Mapping: {";
for (const auto& [tid, linkMapping] : m_linkMapping)
{
os << "TID " << +tid << ": ";
for (uint8_t linkId = 0; linkId < 15; linkId++)
{
if (((linkMapping >> linkId) & 0x0001) == 1)
{
os << +linkId << " ";
}
}
}
os << "}]";
}
} // namespace ns3

View File

@@ -132,6 +132,7 @@ class TidToLinkMapping : public WifiInformationElement
uint16_t GetInformationFieldSize() const override;
void SerializeInformationField(Buffer::Iterator start) const override;
uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
void Print(std::ostream& os) const override;
};
} // namespace ns3

View File

@@ -20,7 +20,7 @@ ExtendedCapabilities::ElementId() const
void
ExtendedCapabilities::Print(std::ostream& os) const
{
os << "Extended Capabilities=";
os << "Extended Capabilities=[]";
// TODO: print useful capabilities
}

View File

@@ -18,7 +18,7 @@ namespace ns3
* @brief The Extended Capabilities Information Element
* @ingroup wifi
*
* This class knows how to serialise and deserialise the Extended Capabilities Information Element
* This class knows how to serialize and deserialize the Extended Capabilities Information Element
*/
class ExtendedCapabilities : public WifiInformationElement
{

View File

@@ -16,7 +16,7 @@ namespace ns3
void
GcrGroupAddress::Print(std::ostream& os) const
{
os << "gcrGroupAddress=" << m_gcrGroupAddress;
os << "GCR Group Address=[" << m_gcrGroupAddress << "]";
}
WifiInformationElementId

View File

@@ -124,14 +124,14 @@ He6GhzBandCapabilities::DeserializeInformationField(Buffer::Iterator start, uint
void
He6GhzBandCapabilities::Print(std::ostream& os) const
{
os << "HE 6GHz Band Capabilities=[Capabilities Information|"
<< " Min MPDU start spacing: " << +m_capabilitiesInfo.m_minMpduStartSpacing
<< " Max A-MPDU Length Exp: " << +m_capabilitiesInfo.m_maxAmpduLengthExponent
<< " Max MPDU Length: " << +m_capabilitiesInfo.m_maxMpduLength
<< " SM Power Save: " << +m_capabilitiesInfo.m_smPowerSave
<< " RD Responder: " << +m_capabilitiesInfo.m_rdResponder
<< " RX Antenna Pattern: " << +m_capabilitiesInfo.m_rxAntennaPatternConsistency
<< " TX Antenna Pattern: " << +m_capabilitiesInfo.m_txAntennaPatternConsistency << "]";
os << "HE 6GHz Band Capabilities=["
<< "Min MPDU start spacing: " << +m_capabilitiesInfo.m_minMpduStartSpacing
<< ", Max A-MPDU Length Exp: " << +m_capabilitiesInfo.m_maxAmpduLengthExponent
<< ", Max MPDU Length: " << +m_capabilitiesInfo.m_maxMpduLength
<< ", SM Power Save: " << +m_capabilitiesInfo.m_smPowerSave
<< ", RD Responder: " << +m_capabilitiesInfo.m_rdResponder
<< ", RX Antenna Pattern: " << +m_capabilitiesInfo.m_rxAntennaPatternConsistency
<< ", TX Antenna Pattern: " << +m_capabilitiesInfo.m_txAntennaPatternConsistency << "]";
}
} // namespace ns3

View File

@@ -128,9 +128,8 @@ HeCapabilities::ElementIdExt() const
void
HeCapabilities::Print(std::ostream& os) const
{
os << "HE Capabilities=" << GetHeMacCapabilitiesInfo1() << "|" << +GetHeMacCapabilitiesInfo2()
<< "|" << GetHePhyCapabilitiesInfo1() << "|" << GetHePhyCapabilitiesInfo2() << "|"
<< +GetHePhyCapabilitiesInfo3() << "|" << GetSupportedMcsAndNss();
os << "HE Capabilities=[Max AMPDU Length Exponent: " << +m_maxAmpduLengthExponent
<< ", Channel Width Set: " << +m_channelWidthSet << "]";
}
uint16_t

View File

@@ -33,11 +33,12 @@ HeOperation::ElementIdExt() const
void
HeOperation::HeOperationParams::Print(std::ostream& os) const
{
os << "Default PE Duration: " << +m_defaultPeDuration << " TWT Required: " << +m_twtRequired
<< " TXOP Duration RTS Threshold: " << m_txopDurRtsThresh
<< " VHT Operation Information Present: " << +m_vhOpPresent
<< " Co-Hosted BSS: " << +m_coHostedBss << " ER SU Disable: " << +m_erSuDisable
<< " 6 GHz Operation Information Present: " << m_6GHzOpPresent;
os << "HE Operation=[Default PE Duration: " << +m_defaultPeDuration
<< ", TWT Required: " << +m_twtRequired
<< ", TXOP Duration RTS Threshold: " << m_txopDurRtsThresh
<< ", VHT Operation Information Present: " << +m_vhOpPresent
<< ", Co-Hosted BSS: " << +m_coHostedBss << ", ER SU Disable: " << +m_erSuDisable
<< ", 6 GHz Operation Information Present: " << m_6GHzOpPresent << "]";
}
uint16_t

View File

@@ -20,7 +20,7 @@ namespace ns3
* @brief The HE Operation Information Element
* @ingroup wifi
*
* This class knows how to serialise and deserialise
* This class knows how to serialize and deserialize
* the HE Operation Information Element
*/
class HeOperation : public WifiInformationElement

View File

@@ -155,4 +155,16 @@ MuEdcaParameterSet::DeserializeInformationField(Buffer::Iterator start, uint16_t
return 13;
}
void
MuEdcaParameterSet::Print(std::ostream& os) const
{
os << "MU EDCA Parameter Set=[QoS Info: " << +m_qosInfo << ", MU Parameter Records=";
for (const auto& record : m_records)
{
os << "{AIFSN: " << +record.aifsnField << ", CWmin/max: " << +record.cwMinMax
<< ", MU EDCA Timer: " << +record.muEdcaTimer << "}, ";
}
os << "]";
}
} // namespace ns3

View File

@@ -120,6 +120,7 @@ class MuEdcaParameterSet : public WifiInformationElement
uint16_t GetInformationFieldSize() const override;
void SerializeInformationField(Buffer::Iterator start) const override;
uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
void Print(std::ostream& os) const override;
/**
* MU AC Parameter Record type

View File

@@ -89,12 +89,18 @@ HtCapabilities::ElementId() const
void
HtCapabilities::Print(std::ostream& os) const
{
os << "HT Capabilities=" << bool(GetLdpc()) << "|" << bool(GetSupportedChannelWidth()) << "|"
<< bool(GetShortGuardInterval20()) << "|";
os << "HT Capabilities=[LDPC: " << +m_ldpc
<< ", Supported Channel Width: " << +m_supportedChannelWidth
<< ", SGI 20MHz: " << +m_shortGuardInterval20 << ", SGI 40MHz: " << +m_shortGuardInterval40
<< ", Supported MCS Set: {";
for (uint8_t i = 0; i < MAX_SUPPORTED_MCS; i++)
{
os << IsSupportedMcs(i) << " ";
if (IsSupportedMcs(i))
{
os << +i << " ";
}
}
os << "}]";
}
void

View File

@@ -25,7 +25,7 @@ namespace ns3
* @brief The HT Capabilities Information Element
* @ingroup wifi
*
* This class knows how to serialise and deserialise the HT Capabilities Information Element
* This class knows how to serialize and deserialize the HT Capabilities Information Element
*/
class HtCapabilities : public WifiInformationElement
{

View File

@@ -54,18 +54,20 @@ HtOperation::ElementId() const
void
HtOperation::Print(std::ostream& os) const
{
os << "HT Operation=" << bool(GetPrimaryChannel()) << "|" << +GetSecondaryChannelOffset() << "|"
<< bool(GetStaChannelWidth()) << "|" << bool(GetRifsMode()) << "|" << +GetHtProtection()
<< "|" << bool(GetNonGfHtStasPresent()) << "|" << bool(GetObssNonHtStasPresent()) << "|"
<< bool(GetDualBeacon()) << "|" << bool(GetDualCtsProtection()) << "|"
<< bool(GetStbcBeacon()) << "|" << bool(GetLSigTxopProtectionFullSupport()) << "|"
<< bool(GetPcoActive()) << "|" << bool(GetPhase()) << "|" << GetRxHighestSupportedDataRate()
<< "|" << bool(GetTxMcsSetDefined()) << "|" << bool(GetTxRxMcsSetUnequal()) << "|"
<< +GetTxMaxNSpatialStreams() << "|" << bool(GetTxUnequalModulation()) << "|";
os << "HT Operation=[Primary Channel: " << +m_primaryChannel
<< ", Secondary Channel Offset: " << +m_secondaryChannelOffset
<< ", STA Channel Width: " << +m_staChannelWidth << ", HT Protection: " << +m_htProtection
<< ", OBSS NON HT STAs Present: " << +m_obssNonHtStasPresent
<< ", L-SIG TXOP Protection Full Support: " << +m_lSigTxopProtectionFullSupport
<< ", RX Highest Supported Data Rate: " << m_rxHighestSupportedDataRate << ", MCS Set: {";
for (uint8_t i = 0; i < MAX_SUPPORTED_MCS; i++)
{
os << IsSupportedMcs(i) << " ";
if (IsSupportedMcs(i))
{
os << +i << " ";
}
}
os << "}]";
}
uint16_t

View File

@@ -33,7 +33,7 @@ enum HtProtectionType
* @brief The HT Operation Information Element
* @ingroup wifi
*
* This class knows how to serialise and deserialise
* This class knows how to serialize and deserialize
* the HT Operation Information Element
*/
class HtOperation : public WifiInformationElement

View File

@@ -48,4 +48,10 @@ DsssParameterSet::DeserializeInformationField(Buffer::Iterator start, uint16_t l
return length;
}
void
DsssParameterSet::Print(std::ostream& os) const
{
os << "DSSS Parameter Set=[Current Channel: " << +m_currentChannel << "]";
}
} // namespace ns3

View File

@@ -18,7 +18,7 @@ namespace ns3
* @brief The DSSS Parameter Set
* @ingroup wifi
*
* This class knows how to serialise and deserialise the DSSS Parameter Set.
* This class knows how to serialize and deserialize the DSSS Parameter Set.
*/
class DsssParameterSet : public WifiInformationElement
{
@@ -39,6 +39,7 @@ class DsssParameterSet : public WifiInformationElement
uint16_t GetInformationFieldSize() const override;
void SerializeInformationField(Buffer::Iterator start) const override;
uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
void Print(std::ostream& os) const override;
uint8_t m_currentChannel; ///< current channel number
};

View File

@@ -78,13 +78,12 @@ ErpInformation::DeserializeInformationField(Buffer::Iterator start, uint16_t len
return length;
}
std::ostream&
operator<<(std::ostream& os, const ErpInformation& erpInformation)
void
ErpInformation::Print(std::ostream& os) const
{
os << bool(erpInformation.GetBarkerPreambleMode()) << "|"
<< bool(erpInformation.GetUseProtection()) << "|" << bool(erpInformation.GetNonErpPresent());
return os;
os << "ERP Information=[Barker Preamble Mode: " << GetBarkerPreambleMode()
<< ", Use Protection: " << GetUseProtection() << ", Non ERP Present: " << GetNonErpPresent()
<< "]";
}
} // namespace ns3

View File

@@ -18,7 +18,7 @@ namespace ns3
* @brief The ErpInformation Information Element
* @ingroup wifi
*
* This class knows how to serialise and deserialise the ErpInformation Information Element.
* This class knows how to serialize and deserialize the ErpInformation Information Element.
*/
class ErpInformation : public WifiInformationElement
{
@@ -30,6 +30,7 @@ class ErpInformation : public WifiInformationElement
uint16_t GetInformationFieldSize() const override;
void SerializeInformationField(Buffer::Iterator start) const override;
uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
void Print(std::ostream& os) const override;
/**
* Set the Barker_Preamble_Mode field in the ErpInformation information element.
@@ -74,16 +75,6 @@ class ErpInformation : public WifiInformationElement
uint8_t m_erpInformation; ///< ERP information
};
/**
* output stream output operator
*
* @param os output stream
* @param erpInformation the ERP Information
*
* @returns output stream
*/
std::ostream& operator<<(std::ostream& os, const ErpInformation& erpInformation);
} // namespace ns3
#endif /* ERP_INFORMATION_H */

View File

@@ -726,4 +726,18 @@ ReducedNeighborReport::DeserializeInformationField(Buffer::Iterator start, uint1
return count;
}
void
ReducedNeighborReport::Print(std::ostream& os) const
{
os << "Reduced Neighbor Report=[";
for (const auto& neighborApInfo : m_nbrApInfoFields)
{
os << "{Operating Class: " << +neighborApInfo.operatingClass
<< ", Channel Number: " << +neighborApInfo.channelNumber
<< ", TBTT Information Count: " << +neighborApInfo.tbttInfoHdr.tbttInfoCount
<< ", TBTT Information Length: " << +neighborApInfo.tbttInfoHdr.tbttInfoLength << "}, ";
}
os << "]";
}
} // namespace ns3

View File

@@ -24,7 +24,7 @@ class WifiPhyOperatingChannel;
* @brief The Reduced Neighbor Report element
* @ingroup wifi
*
* This class knows how to serialise and deserialise the Reduced Neighbor Report element.
* This class knows how to serialize and deserialize the Reduced Neighbor Report element.
*/
class ReducedNeighborReport : public WifiInformationElement
{
@@ -92,6 +92,7 @@ class ReducedNeighborReport : public WifiInformationElement
uint16_t GetInformationFieldSize() const override;
void SerializeInformationField(Buffer::Iterator start) const override;
uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
void Print(std::ostream& os) const override;
/**
* Get the number of Neighbor AP Information fields

View File

@@ -43,7 +43,7 @@ Ssid::Ssid(std::string s)
void
Ssid::Print(std::ostream& os) const
{
os << "ssid=" << PeekString();
os << "SSID=[" << PeekString() << "]";
}
bool

View File

@@ -30,7 +30,7 @@ SupportedRates::SupportedRates()
void
SupportedRates::Print(std::ostream& os) const
{
os << "rates=[";
os << "Supported Rates=[";
for (std::size_t i = 0; i < m_rates.size(); i++)
{
if ((m_rates[i] & 0x80) > 0)

View File

@@ -21,7 +21,7 @@ namespace ns3
* @brief The Supported Rates Information Element
* @ingroup wifi
*
* This class knows how to serialise and deserialise the Supported
* This class knows how to serialize and deserialize the Supported
* Rates Element that holds the first 8 (non-HT) supported rates.
*
* The \c ExtendedSupportedRatesIE class deals with rates beyond the first 8.
@@ -57,7 +57,7 @@ class SupportedRates : public WifiInformationElement
* @brief The Extended Supported Rates Information Element
* @ingroup wifi
*
* This class knows how to serialise and deserialise the Extended
* This class knows how to serialize and deserialize the Extended
* Supported Rates Element that holds (non-HT) rates beyond the 8 that
* the original Supported Rates element can carry.
*/

View File

@@ -205,9 +205,8 @@ Tim::GetPartialVirtualBitmap() const
void
Tim::Print(std::ostream& os) const
{
os << "DTIM Count: " << +m_dtimCount << ", "
<< "DTIM Period: " << +m_dtimPeriod << ", "
<< "Has Multicast Pending: " << m_hasMulticastPending << ", AID values:";
os << "TIM=[DTIM Count: " << +m_dtimCount << ", DTIM Period: " << +m_dtimPeriod
<< ", Has Multicast Pending: " << m_hasMulticastPending << ", AID values: ";
for (uint16_t aid = 0; aid < 2008; ++aid)
{
if (HasAid(aid))
@@ -215,6 +214,7 @@ Tim::Print(std::ostream& os) const
os << aid << " ";
}
}
os << "]";
}
} // namespace ns3

View File

@@ -55,7 +55,10 @@ VhtCapabilities::ElementId() const
void
VhtCapabilities::Print(std::ostream& os) const
{
os << "VHT Capabilities=" << GetVhtCapabilitiesInfo() << "|" << GetSupportedMcsAndNssSet();
os << "VHT Capabilities=[Supported Channel Width Set: " << +m_supportedChannelWidthSet
<< ", SGI 80 MHz: " << +m_shortGuardIntervalFor80Mhz
<< ", SGI 160 MHz: " << +m_shortGuardIntervalFor160Mhz
<< ", Max MPDU Length: " << m_maxMpduLength << "]";
}
uint16_t

View File

@@ -28,8 +28,10 @@ VhtOperation::ElementId() const
void
VhtOperation::Print(std::ostream& os) const
{
os << "VHT Operation=" << +GetChannelWidth() << "|" << +GetChannelCenterFrequencySegment0()
<< "|" << +GetChannelCenterFrequencySegment1() << "|" << GetBasicVhtMcsAndNssSet();
os << "VHT Operation=[Channel Width: " << +m_channelWidth
<< ", Channel Center Frequency Segment 0: " << +m_channelCenterFrequencySegment0
<< ", Channel Center Frequency Segment 1: " << +m_channelCenterFrequencySegment1
<< ", Basic VHT-MCS and NSS Set: " << m_basicVhtMcsAndNssSet << "]";
}
uint16_t

View File

@@ -18,7 +18,7 @@ namespace ns3
* @brief The VHT Operation Information Element
* @ingroup wifi
*
* This class knows how to serialise and deserialise
* This class knows how to serialize and deserialize
* the VHT Operation Information Element
*/
class VhtOperation : public WifiInformationElement

View File

@@ -123,7 +123,7 @@ WifiInformationElement::Deserialize(Buffer::Iterator i)
Buffer::Iterator start = i;
i = DeserializeIfPresent(i);
// This IE was not optional, so confirm that we did actually
// deserialise something.
// deserialize something.
NS_ASSERT(i.GetDistanceFrom(start) != 0);
return i;
}

View File

@@ -262,7 +262,7 @@ typedef uint8_t WifiInformationElementId;
* maintain the relevant state.
*
* This class also provides an implementation of the equality
* operator, which operates by comparing the serialised versions of
* operator, which operates by comparing the serialized versions of
* the two WifiInformationElement objects concerned.
*
* Elements are defined to have a common general format consisting of
@@ -347,7 +347,7 @@ class WifiInformationElement : public SimpleRefCount<WifiInformationElement>
virtual void Print(std::ostream& os) const;
/**
* Compare two IEs for equality by ID & Length, and then through
* memcmp of serialised version
* memcmp of serialized version
*
* @param a another information element to compare with
*

View File

@@ -572,7 +572,7 @@ DoPrint(const std::optional<T>& elem, std::ostream& os)
{
if (elem.has_value())
{
os << *elem << " , ";
os << *elem << ", ";
}
}