diff --git a/src/core/model/attribute-container.h b/src/core/model/attribute-container.h index 88bc31ebc..974abfd6c 100644 --- a/src/core/model/attribute-container.h +++ b/src/core/model/attribute-container.h @@ -45,9 +45,10 @@ class AttributeChecker; * can return the items in a container specified by \p C. * * @tparam A AttributeValue type to be contained. + * @tparam Sep Character separator between elements for parsing. * @tparam C Possibly templated container class returned by Get. */ -template class C = std::list> +template class C = std::list> class AttributeContainerValue : public AttributeValue { public: @@ -74,9 +75,8 @@ class AttributeContainerValue : public AttributeValue /** * Default constructor. - * \param[in] sep Character separator between elements for parsing. */ - AttributeContainerValue(char sep = ','); + AttributeContainerValue(); /** * Construct from another container. @@ -186,9 +186,8 @@ class AttributeContainerValue : public AttributeValue * \return This object with items copied. */ template - Ptr> CopyFrom(const ITER begin, const ITER end); + Ptr> CopyFrom(const ITER begin, const ITER end); - char m_sep; //!< Item separator container_type m_container; //!< Internal container }; @@ -210,35 +209,40 @@ class AttributeContainerChecker : public AttributeChecker /** * Make AttributeContainerChecker from AttributeContainerValue. * @tparam A \deduced AttributeValue type in container. + * @tparam Sep \deduced Character separator between elements for parsing. * @tparam C \deduced Container type returned by Get. * \param[in] value AttributeContainerValue from which to deduce types. * \return AttributeContainerChecker for value. */ -template class C> -Ptr MakeAttributeContainerChecker(const AttributeContainerValue& value); +template class C> +Ptr MakeAttributeContainerChecker( + const AttributeContainerValue& value); /** * Make AttributeContainerChecker using explicit types, initialize item checker. * @tparam A AttributeValue type in container. + * @tparam Sep Character separator between elements for parsing. * @tparam C Container type returned by Get. * \param[in] itemchecker AttributeChecker used for each item in the container. * \return AttributeContainerChecker. */ -template class C = std::list> +template class C = std::list> Ptr MakeAttributeContainerChecker(Ptr itemchecker); /** * Make uninitialized AttributeContainerChecker using explicit types. * @tparam A AttributeValue type in container. + * @tparam Sep Character separator between elements for parsing. * @tparam C Container type returned by Get. * \return AttributeContainerChecker. */ -template class C = std::list> +template class C = std::list> Ptr MakeAttributeContainerChecker(); /** * Make AttributeContainerAccessor using explicit types. * @tparam A AttributeValue type in container. + * @tparam Sep Character separator between elements for parsing. * @tparam C Container type returned by Get. * \tparam T1 \deduced The type of the class data member, * or the type of the class get functor or set method. @@ -246,12 +250,13 @@ Ptr MakeAttributeContainerChecker(); * or the get or set method. * \return AttributeContainerAccessor. */ -template class C = std::list, typename T1> +template class C = std::list, typename T1> Ptr MakeAttributeContainerAccessor(T1 a1); /** * Make AttributeContainerAccessor using explicit types. * @tparam A AttributeValue type in container. + * @tparam Sep Character separator between elements for parsing. * @tparam C Container type returned by Get. * \tparam T1 \deduced The type of the class data member, * or the type of the class get functor or set method. @@ -262,7 +267,11 @@ Ptr MakeAttributeContainerAccessor(T1 a1); * or the get or set method. * \return AttributeContainerAccessor. */ -template class C = std::list, typename T1, typename T2> +template class C = std::list, + typename T1, + typename T2> Ptr MakeAttributeContainerAccessor(T1 a1, T2 a2); } // namespace ns3 @@ -283,7 +292,7 @@ namespace internal * in MakeAttributeContainerChecker. The non-templated base ns3::AttributeContainerChecker * is returned from that function. This is the same pattern as ObjectPtrContainer. */ -template class C> +template class C> class AttributeContainerChecker : public ns3::AttributeContainerChecker { public: @@ -300,58 +309,59 @@ class AttributeContainerChecker : public ns3::AttributeContainerChecker Ptr m_itemchecker; //!< The AttributeChecker }; -template class C> -AttributeContainerChecker::AttributeContainerChecker() +template class C> +AttributeContainerChecker::AttributeContainerChecker() : m_itemchecker(nullptr) { } -template class C> -AttributeContainerChecker::AttributeContainerChecker(Ptr itemchecker) +template class C> +AttributeContainerChecker::AttributeContainerChecker( + Ptr itemchecker) : m_itemchecker(itemchecker) { } -template class C> +template class C> void -AttributeContainerChecker::SetItemChecker(Ptr itemchecker) +AttributeContainerChecker::SetItemChecker(Ptr itemchecker) { m_itemchecker = itemchecker; } -template class C> +template class C> Ptr -AttributeContainerChecker::GetItemChecker() const +AttributeContainerChecker::GetItemChecker() const { return m_itemchecker; } } // namespace internal -template class C> +template class C> Ptr -MakeAttributeContainerChecker(const AttributeContainerValue& value) +MakeAttributeContainerChecker(const AttributeContainerValue& value) { - return MakeAttributeContainerChecker(); + return MakeAttributeContainerChecker(); } -template class C> +template class C> Ptr MakeAttributeContainerChecker(Ptr itemchecker) { - auto checker = MakeAttributeContainerChecker(); + auto checker = MakeAttributeContainerChecker(); auto acchecker = DynamicCast(checker); acchecker->SetItemChecker(itemchecker); return checker; } -template class C> +template class C> Ptr MakeAttributeContainerChecker() { std::string containerType; std::string underlyingType; - typedef AttributeContainerValue T; + typedef AttributeContainerValue T; { std::ostringstream oss; oss << "ns3::AttributeContainerValue<" << typeid(typename T::attribute_type).name() << ", " @@ -365,51 +375,50 @@ MakeAttributeContainerChecker() underlyingType = oss.str(); } - return MakeSimpleAttributeChecker>(containerType, - underlyingType); + return MakeSimpleAttributeChecker>( + containerType, + underlyingType); } -template class C> -AttributeContainerValue::AttributeContainerValue(char sep) - : m_sep(sep) +template class C> +AttributeContainerValue::AttributeContainerValue() { } -template class C> +template class C> template -AttributeContainerValue::AttributeContainerValue(const CONTAINER& c) - : AttributeContainerValue(c.begin(), c.end()) +AttributeContainerValue::AttributeContainerValue(const CONTAINER& c) + : AttributeContainerValue(c.begin(), c.end()) { } -template class C> +template class C> template -AttributeContainerValue::AttributeContainerValue(const ITER begin, const ITER end) +AttributeContainerValue::AttributeContainerValue(const ITER begin, const ITER end) : AttributeContainerValue() { CopyFrom(begin, end); } -template class C> -AttributeContainerValue::~AttributeContainerValue() +template class C> +AttributeContainerValue::~AttributeContainerValue() { m_container.clear(); } -template class C> +template class C> Ptr -AttributeContainerValue::Copy() const +AttributeContainerValue::Copy() const { - auto c = Create>(); - c->m_sep = m_sep; + auto c = Create>(); c->m_container = m_container; return c; } -template class C> +template class C> bool -AttributeContainerValue::DeserializeFromString(std::string value, - Ptr checker) +AttributeContainerValue::DeserializeFromString(std::string value, + Ptr checker) { auto acchecker = DynamicCast(checker); if (!acchecker) @@ -418,7 +427,7 @@ AttributeContainerValue::DeserializeFromString(std::string value, } std::istringstream iss(value); // copies value - while (std::getline(iss, value, m_sep)) + while (std::getline(iss, value, Sep)) { auto avalue = acchecker->GetItemChecker()->CreateValidValue(StringValue(value)); if (!avalue) @@ -438,9 +447,9 @@ AttributeContainerValue::DeserializeFromString(std::string value, return true; } -template class C> +template class C> std::string -AttributeContainerValue::SerializeToString(Ptr checker) const +AttributeContainerValue::SerializeToString(Ptr checker) const { std::ostringstream oss; bool first = true; @@ -448,7 +457,7 @@ AttributeContainerValue::SerializeToString(Ptr che { if (!first) { - oss << m_sep; + oss << Sep; } oss << attr->SerializeToString(checker); first = false; @@ -456,9 +465,9 @@ AttributeContainerValue::SerializeToString(Ptr che return oss.str(); } -template class C> -typename AttributeContainerValue::result_type -AttributeContainerValue::Get() const +template class C> +typename AttributeContainerValue::result_type +AttributeContainerValue::Get() const { result_type c; for (const value_type& a : *this) @@ -468,10 +477,10 @@ AttributeContainerValue::Get() const return c; } -template class C> +template class C> template bool -AttributeContainerValue::GetAccessor(T& value) const +AttributeContainerValue::GetAccessor(T& value) const { result_type src = Get(); value.clear(); @@ -479,75 +488,75 @@ AttributeContainerValue::GetAccessor(T& value) const return true; } -template class C> +template class C> template void -AttributeContainerValue::Set(const T& c) +AttributeContainerValue::Set(const T& c) { m_container.clear(); CopyFrom(c.begin(), c.end()); } -template class C> -typename AttributeContainerValue::size_type -AttributeContainerValue::GetN() const +template class C> +typename AttributeContainerValue::size_type +AttributeContainerValue::GetN() const { return size(); } -template class C> -typename AttributeContainerValue::Iterator -AttributeContainerValue::Begin() +template class C> +typename AttributeContainerValue::Iterator +AttributeContainerValue::Begin() { return begin(); } -template class C> -typename AttributeContainerValue::Iterator -AttributeContainerValue::End() +template class C> +typename AttributeContainerValue::Iterator +AttributeContainerValue::End() { return end(); } -template class C> -typename AttributeContainerValue::size_type -AttributeContainerValue::size() const +template class C> +typename AttributeContainerValue::size_type +AttributeContainerValue::size() const { return m_container.size(); } -template class C> -typename AttributeContainerValue::iterator -AttributeContainerValue::begin() +template class C> +typename AttributeContainerValue::iterator +AttributeContainerValue::begin() { return m_container.begin(); } -template class C> -typename AttributeContainerValue::iterator -AttributeContainerValue::end() +template class C> +typename AttributeContainerValue::iterator +AttributeContainerValue::end() { return m_container.end(); } -template class C> -typename AttributeContainerValue::const_iterator -AttributeContainerValue::begin() const +template class C> +typename AttributeContainerValue::const_iterator +AttributeContainerValue::begin() const { return m_container.cbegin(); } -template class C> -typename AttributeContainerValue::const_iterator -AttributeContainerValue::end() const +template class C> +typename AttributeContainerValue::const_iterator +AttributeContainerValue::end() const { return m_container.cend(); } -template class C> +template class C> template -Ptr> -AttributeContainerValue::CopyFrom(const ITER begin, const ITER end) +Ptr> +AttributeContainerValue::CopyFrom(const ITER begin, const ITER end) { for (ITER iter = begin; iter != end; ++iter) { @@ -556,18 +565,18 @@ AttributeContainerValue::CopyFrom(const ITER begin, const ITER end) return this; } -template class C, typename T1> +template class C, typename T1> Ptr MakeAttributeContainerAccessor(T1 a1) { - return MakeAccessorHelper>(a1); + return MakeAccessorHelper>(a1); } -template class C, typename T1, typename T2> +template class C, typename T1, typename T2> Ptr MakeAttributeContainerAccessor(T1 a1, T2 a2) { - return MakeAccessorHelper>(a1, a2); + return MakeAccessorHelper>(a1, a2); } } // namespace ns3 diff --git a/src/core/test/attribute-container-test-suite.cc b/src/core/test/attribute-container-test-suite.cc index 5bd5175fe..cc95db691 100644 --- a/src/core/test/attribute-container-test-suite.cc +++ b/src/core/test/attribute-container-test-suite.cc @@ -93,7 +93,8 @@ class AttributeContainerObject : public Object std::list m_doublelist; //!< List of doubles. std::vector m_intvec; //!< Vector of ints. // TODO(jared): need PairValue attributevalue to handle std::pair elements - std::map m_map; //!< Map of . + std::map m_map; //!< Map of . + std::map> m_intVecIntMapping; //!< Mapping integers to vectors }; AttributeContainerObject::AttributeContainerObject() @@ -107,6 +108,8 @@ AttributeContainerObject::~AttributeContainerObject() TypeId AttributeContainerObject::GetTypeId() { + using IntVecMapValue = PairValue>; + static TypeId tid = TypeId("ns3::AttributeContainerObject") .SetParent() @@ -118,15 +121,16 @@ AttributeContainerObject::GetTypeId() MakeAttributeContainerAccessor( &AttributeContainerObject::m_doublelist), MakeAttributeContainerChecker(MakeDoubleChecker())) - .AddAttribute("IntegerVector", - "Vector of integers", - // the container value container differs from the underlying object - AttributeContainerValue(), - // the type of the underlying container cannot be deduced - MakeAttributeContainerAccessor( - &AttributeContainerObject::SetIntVec, - &AttributeContainerObject::GetIntVec), - MakeAttributeContainerChecker(MakeIntegerChecker())) + .AddAttribute( + "IntegerVector", + "Vector of integers", + // the container value container differs from the underlying object + AttributeContainerValue(), + // the type of the underlying container cannot be deduced + MakeAttributeContainerAccessor( + &AttributeContainerObject::SetIntVec, + &AttributeContainerObject::GetIntVec), + MakeAttributeContainerChecker(MakeIntegerChecker())) .AddAttribute( "MapStringInt", "Map of strings to ints", @@ -136,7 +140,23 @@ AttributeContainerObject::GetTypeId() &AttributeContainerObject::m_map), MakeAttributeContainerChecker>( MakePairChecker(MakeStringChecker(), - MakeIntegerChecker()))); + MakeIntegerChecker()))) + .AddAttribute( + "IntVecPairVec", + "An example of complex attribute that is defined by a vector of pairs consisting " + "of an integer value and a vector of integers. In case a string is used to set " + "this attribute, the string shall contain the pairs separated by a semicolon (;); " + "in every pair, the integer value and the vector of integers are separated by a " + "blank space, and the elements of the vectors are separated by a comma (,) " + "without spaces. E.g. \"0 1,2,3; 1 0; 2 0,1\" consists of three pairs containing " + "vectors of 3, 1 and 2 elements, respectively.", + StringValue(""), + MakeAttributeContainerAccessor( + &AttributeContainerObject::m_intVecIntMapping), + MakeAttributeContainerChecker( + MakePairChecker>( + MakeIntegerChecker(), + MakeAttributeContainerChecker(MakeIntegerChecker())))); return tid; } @@ -269,7 +289,7 @@ AttributeContainerTestCase::DoRun() { auto ref = {"one", "two", "three"}; - AttributeContainerValue ac(ref); + AttributeContainerValue ac(ref); NS_TEST_ASSERT_MSG_EQ(3, ac.GetN(), "Container size mismatch"); auto aciter = ac.Begin(); @@ -366,7 +386,7 @@ AttributeContainerSerializationTestCase::DoRun() { std::string strings = "this is a sentence with words"; - AttributeContainerValue attr(' '); + AttributeContainerValue attr; auto checker = MakeAttributeContainerChecker(attr); auto acchecker = DynamicCast(checker); acchecker->SetItemChecker(MakeStringChecker()); @@ -467,11 +487,11 @@ AttributeContainerSetGetTestCase::DoRun() std::vector ints = {-1, 0, 1, 2, 3}; // NOTE: here the underlying attribute container type differs from the actual container - obj->SetAttribute("IntegerVector", AttributeContainerValue(ints)); + obj->SetAttribute("IntegerVector", AttributeContainerValue(ints)); { // NOTE: changing the container here too! - AttributeContainerValue value; + AttributeContainerValue value; obj->GetAttribute("IntegerVector", value); NS_TEST_ASSERT_MSG_EQ(ints.size(), value.GetN(), "AttributeContainerValue wrong size"); @@ -485,6 +505,37 @@ AttributeContainerSetGetTestCase::DoRun() } } + std::string intVecPairString("0 1,2,3; 1 0; 2 0,1"); + // NOTE: here the underlying attribute container type differs from the actual container + obj->SetAttribute("IntVecPairVec", StringValue(intVecPairString)); + + { + using IntVecMapValue = PairValue>; + + // NOTE: changing the container here too! + AttributeContainerValue value; + obj->GetAttribute("IntVecPairVec", value); + NS_TEST_ASSERT_MSG_EQ(3, value.GetN(), "AttributeContainerValue wrong size"); // 3 pairs + + AttributeContainerValue::result_type reslist = value.Get(); + NS_TEST_ASSERT_MSG_EQ(3, reslist.size(), "IntVecMapValue wrong size"); + auto reslistIt = reslist.begin(); + NS_TEST_ASSERT_MSG_EQ(reslistIt->first, 0, "Incorrect integer value in first pair"); + NS_TEST_ASSERT_MSG_EQ(reslistIt->second.size(), + 3, + "Incorrect number of integer values in first pair"); + ++reslistIt; + NS_TEST_ASSERT_MSG_EQ(reslistIt->first, 1, "Incorrect integer value in second pair"); + NS_TEST_ASSERT_MSG_EQ(reslistIt->second.size(), + 1, + "Incorrect number of integer values in second pair"); + ++reslistIt; + NS_TEST_ASSERT_MSG_EQ(reslistIt->first, 2, "Incorrect integer value in third pair"); + NS_TEST_ASSERT_MSG_EQ(reslistIt->second.size(), + 2, + "Incorrect number of integer values in third pair"); + } + std::map map = {{"one", 1}, {"two", 2}, {"three", 3}}; obj->SetAttribute("MapStringInt", AttributeContainerValue>(map)); diff --git a/src/wifi/model/txop.cc b/src/wifi/model/txop.cc index efbef04a5..011b97deb 100644 --- a/src/wifi/model/txop.cc +++ b/src/wifi/model/txop.cc @@ -68,8 +68,7 @@ Txop::GetTypeId() "The minimum values of the contention window for all the links", TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time AttributeContainerValue(), - MakeAttributeContainerAccessor(&Txop::SetMinCws, - &Txop::GetMinCws), + MakeAttributeContainerAccessor(&Txop::SetMinCws, &Txop::GetMinCws), MakeAttributeContainerChecker(MakeUintegerChecker())) .AddAttribute("MaxCw", "The maximum value of the contention window (just for the first link, " @@ -84,8 +83,7 @@ Txop::GetTypeId() "The maximum values of the contention window for all the links", TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time AttributeContainerValue(), - MakeAttributeContainerAccessor(&Txop::SetMaxCws, - &Txop::GetMaxCws), + MakeAttributeContainerAccessor(&Txop::SetMaxCws, &Txop::GetMaxCws), MakeAttributeContainerChecker(MakeUintegerChecker())) .AddAttribute( "Aifsn", @@ -101,8 +99,7 @@ Txop::GetTypeId() "The values of AIFSN for all the links", TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time AttributeContainerValue(), - MakeAttributeContainerAccessor(&Txop::SetAifsns, - &Txop::GetAifsns), + MakeAttributeContainerAccessor(&Txop::SetAifsns, &Txop::GetAifsns), MakeAttributeContainerChecker(MakeUintegerChecker())) .AddAttribute("TxopLimit", "The TXOP limit: the default value conforms to non-QoS " @@ -112,14 +109,13 @@ Txop::GetTypeId() MakeTimeAccessor((void(Txop::*)(Time)) & Txop::SetTxopLimit, (Time(Txop::*)() const) & Txop::GetTxopLimit), MakeTimeChecker()) - .AddAttribute( - "TxopLimits", - "The values of TXOP limit for all the links", - TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time - AttributeContainerValue(), - MakeAttributeContainerAccessor(&Txop::SetTxopLimits, - &Txop::GetTxopLimits), - MakeAttributeContainerChecker(MakeTimeChecker())) + .AddAttribute("TxopLimits", + "The values of TXOP limit for all the links", + TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time + AttributeContainerValue(), + MakeAttributeContainerAccessor(&Txop::SetTxopLimits, + &Txop::GetTxopLimits), + MakeAttributeContainerChecker(MakeTimeChecker())) .AddAttribute("Queue", "The WifiMacQueue object", PointerValue(),