diff --git a/doc/manual/source/attributes.rst b/doc/manual/source/attributes.rst index f916b0d04..ff6d8037f 100644 --- a/doc/manual/source/attributes.rst +++ b/doc/manual/source/attributes.rst @@ -258,6 +258,94 @@ canvas and see a hierarchical, organized list of parameters that are settable on the node and its constituent member objects, and help text and default values for each parameter. +Available AttributeValue Types +++++++++++++++++++++++++++++++ + +* AddressValue + +* AttributeContainerValue + +* BooleanValue + +* BoxValue + +* CallbackValue + +* DataRateValue + +* DoubleValue + +* EmptyAttributeValue + +* EnumValue + +* IntegerValue + +* Ipv4AddressValue + +* Ipv4MaskValue + +* Ipv6AddressValue + +* Ipv6PrefixValue + +* LengthValue + +* Mac16AddressValue + +* Mac48AddressValue + +* Mac64AddressValue + +* ObjectFactoryValue + +* ObjectPtrContainerValue + +* PairValue + +* PointerValue + +* PriomapValue + +* QueueSizeValue + +* RectangleValue + +* SsidValue + +* TimeValue + +* TupleValue + + A TupleValue is capable of storing values of different types, hence it is suitable for + structured data. A prominent example is the ChannelSettings attribute of WifiPhy, which + consists of channel number, channel width, PHY band and primary 20 MHz channel index. + In this case the values have to be mutually consistent, which makes it difficult to set them + as individual Attributes. Capturing them in a TupleValue simplifies this problem, see + ``src/wifi/model/wifi-phy.cc``. + + Values stored in a TupleValue object can be set/get through a std::tuple object or + can be serialized to/deserialized from a string containing a comma-separated sequence of + the values enclosed in a pair of curly braces (e.g., "{36, 20, BAND_5GHZ, 0}"). + + The usage of the TupleValue attribute is illustrated in + ``src/core/test/tuple-value-test-suite.cc``. + +* TypeIdValue + +* UanModesListValue + +* UintegerValue + +* Vector2DValue + +* Vector3DValue + +* WaypointValue + +* WifiModeValue + + Defining Attributes +++++++++++++++++++ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 164e8e20e..f9135cf73 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -254,6 +254,7 @@ set(header_files model/traced-callback.h model/traced-value.h model/trickle-timer.h + model/tuple.h model/type-id.h model/type-name.h model/type-traits.h @@ -291,6 +292,7 @@ set(test_sources test/timer-test-suite.cc test/traced-callback-test-suite.cc test/trickle-timer-test-suite.cc + test/tuple-value-test-suite.cc test/type-id-test-suite.cc test/type-traits-test-suite.cc test/watchdog-test-suite.cc diff --git a/src/core/model/tuple.h b/src/core/model/tuple.h new file mode 100644 index 000000000..59123abe4 --- /dev/null +++ b/src/core/model/tuple.h @@ -0,0 +1,534 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2021 Universita' degli Studi di Napoli Federico II + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Stefano Avallone + */ + +#ifndef TUPLE_H +#define TUPLE_H + +#include +#include + +#include +#include +#include +#include +#include + +namespace ns3 { + +/** + * \brief Stream insertion operator. + * See https://en.cppreference.com/w/cpp/utility/apply + * + * Prints tuple values separated by a comma. E.g., if the tuple contains + * v1, v2 and v3, then "v1, v2, v3" will be added to the stream. + * + * \tparam Args \deduced Tuple arguments + * \param os the output stream + * \param t the tuple + * \returns a reference to the stream + */ +template +std::ostream & +operator << (std::ostream &os, const std::tuple &t) +{ + std::apply ([&os](auto&&... args) + { + std::size_t n{0}; + ((os << args << (++n != sizeof...(Args) ? ", " : "")), ...); + }, + t); + return os; +} + + +// Doxygen for this class is auto-generated by +// utils/print-introspected-doxygen.h + +/** + * Hold objects of type std::tuple. + * \tparam Args \explicit The list of AttributeValues to be held by this TupleValue + */ +template +class TupleValue : public AttributeValue +{ +public: + /** Type of value stored in the TupleValue. */ + typedef std::tuple value_type; + /** Type returned by Get or passed in Set. */ + typedef std::tuple...> result_type; + + TupleValue (); + + /** + * Construct this TupleValue from a std::tuple + * + * \param [in] value Value with which to construct. + */ + TupleValue (const result_type &value); + + Ptr Copy (void) const override; + bool DeserializeFromString (std::string value, Ptr checker) override; + std::string SerializeToString (Ptr checker) const override; + + /** + * Get the stored values as a std::tuple. + * + * This differs from the actual value stored in the object which is + * a tuple of Ptr where AV is a class derived from AttributeValue. + * \return stored values as a std::tuple + */ + result_type Get (void) const; + /** + * Set the stored values. + */ + void Set (const result_type &value); + + /** + * Get the attribute values as a tuple. + * \return the attribute values as a tuple + */ + value_type GetValue (void) const; + + /** + * Set the given variable to the values stored by this TupleValue object. + * + * \tparam T \deduced the type of the given variable (normally, the argument type + * of a set method or the type of a data member) + * \return true if the given variable was set + */ + template + bool GetAccessor (T &value) const; + +private: + /** + * Set the attribute values starting from the given values. + * Used by DeserializeFromString method. + * + * \tparam Is \deduced index sequence + * \param values the given attribute values + * \return true if the attribute values of this object were set + */ + template + bool SetValueImpl (std::index_sequence, const std::vector>& values); + + value_type m_value; //!< Tuple of attribute values +}; + +/** + * Create a TupleValue object. Enable to write code like this snippet: + * + * \code + * typedef std::tuple Tuple; + * typedef std::tuple Pack; + * + * TupleValue t = MakeTupleValue (Tuple {10, 1.5}); + * \endcode + * + * \tparam T1 \explicit A std::tuple of the AttributeValue types included in TupleValue + * \tparam T2 \deduced A std::tuple of the type of elements stored by TupleValue + * \param t the tuple of elements stored by TupleValue + * \return a TupleValue object + */ +template +auto MakeTupleValue (T2 t); + + +/** + * Checker for attribute values storing tuples. + */ +class TupleChecker : public AttributeChecker +{ +public: + /** + * Get the checkers for all tuple elements. + * + * \return the checkers for all tuple elements + */ + virtual const std::vector>& GetCheckers (void) const = 0; +}; + + +/** + * Create a TupleChecker from AttributeCheckers associated with TupleValue elements. + * + * \tparam Args \explicit Attribute value types + * \tparam Ts \deduced Attribute checker types + * \param checkers attribute checkers + * \return Pointer to TupleChecker instance. + */ +template +Ptr MakeTupleChecker (Ts... checkers); + + +/** + * Create an AttributeAccessor for a class data member of type tuple, + * or a lone class get functor or set method. + * + * \tparam Args \explicit Attribute value types + * \tparam T1 \deduced The type of the class data member, + * or the type of the class get functor or set method. + * \param a1 The address of the data member, + * or the get or set method. + * \return the AttributeAccessor + */ +template +Ptr MakeTupleAccessor (T1 a1); + +/** + * Create an AttributeAccessor using a pair of get functor + * and set methods from a class. + * + * \tparam Args \explicit Attribute value types + * \tparam T1 \deduced The type of the class data member, + * or the type of the class get functor or set method. + * \tparam T2 \deduced The type of the getter class functor method. + * \param a2 The address of the class method to set the attribute. + * \param a1 The address of the data member, or the get or set method. + * \return the AttributeAccessor + */ +template +Ptr MakeTupleAccessor (T1 a1, T2 a2); + + +} // namespace ns3 + +/***************************************************************************** + * Implementation below + *****************************************************************************/ + +namespace ns3 { + +template +TupleValue::TupleValue () + : m_value (std::make_tuple (Args ()...)) +{ +} + +template +TupleValue::TupleValue (const result_type &value) +{ + Set (value); +} + +template +Ptr +TupleValue::Copy (void) const +{ + return Create > (Get ()); +} + +template +template +bool +TupleValue::SetValueImpl (std::index_sequence, const std::vector>& values) +{ + auto valueTuple = std::make_tuple (DynamicCast (values[Is])...); + + bool ok = ((std::get (valueTuple) != nullptr) && ...); + + if (ok) + { + m_value = std::make_tuple (Args (*std::get (valueTuple))...); + } + return ok; +} + +template +bool +TupleValue::DeserializeFromString (std::string value, Ptr checker) +{ + auto tupleChecker = DynamicCast (checker); + if (tupleChecker == nullptr) + { + return false; + } + + auto count = tupleChecker->GetCheckers ().size (); + if (count != sizeof...(Args)) + { + return false; + } + + if (value.empty () || value.front () != '{' || value.back () != '}') + { + return false; + } + + value.erase (value.begin ()); + value.pop_back (); + std::replace (value.data (), value.data () + value.size (), ',', ' '); + + std::istringstream iss (value); + std::vector> values; + std::size_t i = 0; + + while (iss >> value) + { + if (i >= count) + { + return false; + } + values.push_back (tupleChecker->GetCheckers ().at (i++)->CreateValidValue (StringValue (value))); + if (values.back () == nullptr) + { + return false; + } + } + + if (i != count) + { + return false; + } + + return SetValueImpl (std::index_sequence_for{}, values); +} + +template +std::string +TupleValue::SerializeToString (Ptr checker) const +{ + std::ostringstream oss; + oss << "{" << Get () << "}"; + return oss.str (); +} + +template +typename TupleValue::result_type +TupleValue::Get (void) const +{ + return std::apply ([](Args... values) + { + return std::make_tuple (values.Get ()...); + }, + m_value); +} + +template +void +TupleValue::Set (const typename TupleValue::result_type &value) +{ + m_value = std::apply ([](auto&&... args) + { + return std::make_tuple (Args (args)...); + }, + value); +} + +template +typename TupleValue::value_type +TupleValue::GetValue (void) const +{ + return m_value; +} + +template +template +bool +TupleValue::GetAccessor (T &value) const +{ + value = T (Get ()); + return true; +} + + + + +// This internal class defines templated TupleChecker class that is instantiated +// in MakeTupleChecker. The non-templated base ns3::TupleChecker is returned in that +// function. This is the same pattern as ObjectPtrContainer. +namespace internal { + +/** + * Internal checker class templated to each AttributeChecker + * for each entry in the tuple. + */ +template +class TupleChecker : public ns3::TupleChecker +{ +public: + /** + * Constructor. + * \tparam Ts \deduced the type of the attribute checkers + * \param checkers the attribute checkers for individual elements of the tuple + */ + template + TupleChecker (Ts... checkers) + : m_checkers {checkers...} + { + } + const std::vector>& GetCheckers (void) const override + { + return m_checkers; + } + bool Check (const AttributeValue &value) const override + { + const TupleValue* v = dynamic_cast*> (&value); + if (v == nullptr) + { + return false; + } + return std::apply ([this](Args... values) + { + std::size_t n{0}; + return (m_checkers[n++]->Check (values) && ...); + }, + v->GetValue ()); + } + std::string GetValueTypeName (void) const override + { + return "ns3::TupleValue"; + } + bool HasUnderlyingTypeInformation (void) const override + { + return false; + } + std::string GetUnderlyingTypeInformation (void) const override + { + return ""; + } + Ptr Create (void) const override + { + return ns3::Create> (); + } + bool Copy (const AttributeValue &source, AttributeValue &destination) const override + { + const TupleValue *src = dynamic_cast *> (&source); + TupleValue *dst = dynamic_cast *> (&destination); + if (src == 0 || dst == 0) + { + return false; + } + *dst = *src; + return true; + } + +private: + std::vector> m_checkers; //!< attribute checkers +}; + +/** + * Helper class defining static methods for MakeTupleChecker and MakeTupleAccessor + * that are called when user specifies the list of AttributeValue types included + * in a TupleValue type. + */ +template +struct TupleHelper +{ + /** + * \copydoc ns3::MakeTupleChecker + */ + template + static Ptr MakeTupleChecker (Ts... checkers) + { + return Create > (checkers...); + } + /** + * \copydoc ns3::MakeTupleAccessor(T1) + */ + template + static Ptr MakeTupleAccessor (T1 a1) + { + return MakeAccessorHelper> (a1); + } + /** + * \copydoc ns3::MakeTupleAccessor(T1,T2) + */ + template + static Ptr MakeTupleAccessor (T1 a1, T2 a2) + { + return MakeAccessorHelper> (a1, a2); + } +}; + +/** + * Helper class defining static methods for MakeTupleValue, MakeTupleChecker and + * MakeTupleAccessor that are called when user provides a std::tuple of the + * AttributeValue types included in a TupleValue type. + * This struct is a partial specialization of struct TupleHelper. + */ +template +struct TupleHelper> +{ + /** + * \copydoc ns3::MakeTupleValue + */ + static TupleValue MakeTupleValue (const typename TupleValue::result_type& t) + { + return TupleValue (t); + } + /** + * \copydoc ns3::MakeTupleChecker + */ + template + static Ptr MakeTupleChecker (Ts... checkers) + { + return Create > (checkers...); + } + /** + * \copydoc ns3::MakeTupleAccessor(T1) + */ + template + static Ptr MakeTupleAccessor (T1 a1) + { + return MakeAccessorHelper> (a1); + } + /** + * \copydoc ns3::MakeTupleAccessor(T1,T2) + */ + template + static Ptr MakeTupleAccessor (T1 a1, T2 a2) + { + return MakeAccessorHelper> (a1, a2); + } +}; + +} // namespace internal + + + +template +auto MakeTupleValue (T2 t) +{ + return internal::TupleHelper::MakeTupleValue (t); +} + +template +Ptr MakeTupleChecker (Ts... checkers) +{ + return internal::TupleHelper::template MakeTupleChecker (checkers...); +} + +template +Ptr MakeTupleAccessor (T1 a1) +{ + return internal::TupleHelper::template MakeTupleAccessor (a1); +} + +template +Ptr MakeTupleAccessor (T1 a1, T2 a2) +{ + return internal::TupleHelper::template MakeTupleAccessor (a1, a2); +} + + + +} // namespace ns3 + +#endif // TUPLE_H diff --git a/src/core/test/tuple-value-test-suite.cc b/src/core/test/tuple-value-test-suite.cc new file mode 100644 index 000000000..d6c85555b --- /dev/null +++ b/src/core/test/tuple-value-test-suite.cc @@ -0,0 +1,283 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2021 Universita' degli Studi di Napoli Federico II + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Stefano Avallone + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("TupleTestSuite"); + +/** Object with attribute values storing tuples */ +class TupleObject : public Object +{ +public: + /** + * Test enum type + */ + enum TupleTestEnum + { + VALUE1, + VALUE2, + VALUE3 + }; + + TupleObject (); + virtual ~TupleObject (); + + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (); + + // NOTE EnumValue::Get() return an int, so the tuple element type must be an int + // in place of the enum type + using Tuple1Value = TupleValue; //!< Tuple1 attribute value + using Tuple1 = Tuple1Value::result_type; //!< tuple of values + using Tuple1Pack = Tuple1Value::value_type; //!< tuple of attribute values + + using Tuple2 = std::tuple ; //!< Tuple2 typedef + + /** + * Set tuple1 + * \param tuple tuple value + */ + void SetTuple1 (const Tuple1& tuple); + /** + * Get tuple1 + * \return tuple1 + */ + Tuple1 GetTuple1 (void) const; + /** + * Set tuple2 + * \param tuple tuple value + */ + void SetTuple2 (const Tuple2& tuple); + /** + * Get tuple2 + * \return tuple2 + */ + Tuple2 GetTuple2 (void) const; + +private: + Tuple1 m_tuple1; //!< first tuple + Tuple2 m_tuple2; //!< second tuple +}; + +TypeId +TupleObject::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::TupleObject") + .SetParent () + .SetGroupName("Test") + .AddConstructor () + .AddAttribute ("StringStringEnumTuple", "Tuple1: string, string, enum", + MakeTupleValue (Tuple1 {"Hey", "Jude", TupleObject::VALUE1}), + MakeTupleAccessor (&TupleObject::m_tuple1), + MakeTupleChecker (MakeStringChecker (), MakeStringChecker (), + MakeEnumChecker (TupleObject::VALUE1, "VALUE1", + TupleObject::VALUE2, "VALUE2"))) + .AddAttribute ("DoubleUintStringTuple", "Tuple2: double, uint16_t, string", + TupleValue ({6.022, 23, "Avogadro"}), + MakeTupleAccessor (&TupleObject::SetTuple2, + &TupleObject::GetTuple2), + MakeTupleChecker (MakeDoubleChecker (1.0, 10.0), + MakeUintegerChecker (1, 30), + MakeStringChecker ())) + ; + return tid; +} + +TupleObject::TupleObject () +{ +} + +TupleObject::~TupleObject () +{ +} + +void +TupleObject::SetTuple1 (const Tuple1& tuple) +{ + m_tuple1 = tuple; +} + +TupleObject::Tuple1 +TupleObject::GetTuple1 (void) const +{ + return m_tuple1; +} + +void +TupleObject::SetTuple2 (const Tuple2& tuple) +{ + m_tuple2 = tuple; +} + +TupleObject::Tuple2 +TupleObject::GetTuple2 (void) const +{ + return m_tuple2; +} + + +/** Test instantiation, initialization, access */ +class TupleValueTestCase : public TestCase +{ +public: + TupleValueTestCase (); + virtual ~TupleValueTestCase () {} + +private: + virtual void DoRun (); +}; + +TupleValueTestCase::TupleValueTestCase () + : TestCase ("test TupleValue attribute value") +{ +} + +void +TupleValueTestCase::DoRun () +{ + auto tupleObject = CreateObject (); + + // Test that default values have been assigned to tuple 1 + auto t1 = tupleObject->GetTuple1 (); + NS_TEST_ASSERT_MSG_EQ ((std::get<0> (t1) == "Hey"), true, "First element of tuple 1 not correctly set"); + NS_TEST_ASSERT_MSG_EQ ((std::get<1> (t1) == "Jude"), true, "Second element of tuple 1 not correctly set"); + NS_TEST_ASSERT_MSG_EQ (std::get<2> (t1), (int)(TupleObject::VALUE1), "Third element of tuple 1 not correctly set"); + + // Test that default values have been assigned to tuple 2 + auto t2 = tupleObject->GetTuple2 (); + NS_TEST_ASSERT_MSG_EQ (std::get<0> (t2), 6.022, "First element of tuple 2 not correctly set"); + NS_TEST_ASSERT_MSG_EQ (std::get<1> (t2), 23, "Second element of tuple 2 not correctly set"); + NS_TEST_ASSERT_MSG_EQ ((std::get<2> (t2) == "Avogadro"), true, "Third element of tuple 2 not correctly set"); + + // Test that we can correctly set and get new values for tuple 1 + bool ret1 = tupleObject->SetAttributeFailSafe ("StringStringEnumTuple", + MakeTupleValue (TupleObject::Tuple1 {"Norwegian", "Wood", TupleObject::VALUE2})); + NS_TEST_ASSERT_MSG_EQ (ret1, true, "Setting valid values to tuple 1 failed"); + + TupleValue tupleValue1; + ret1 = tupleObject->GetAttributeFailSafe ("StringStringEnumTuple", tupleValue1); + NS_TEST_ASSERT_MSG_EQ (ret1, true, "Getting values for tuple 1 failed"); + + t1 = tupleValue1.Get (); + NS_TEST_ASSERT_MSG_EQ ((std::get<0> (t1) == "Norwegian"), true, "First element of tuple 1 not correctly set"); + NS_TEST_ASSERT_MSG_EQ ((std::get<1> (t1) == "Wood"), true, "Second element of tuple 1 not correctly set"); + NS_TEST_ASSERT_MSG_EQ (std::get<2> (t1), (int)(TupleObject::VALUE2), "Third element of tuple 1 not correctly set"); + + // Test that we can correctly set and get new values for tuple 2 + bool ret2 = tupleObject->SetAttributeFailSafe ("DoubleUintStringTuple", + TupleValue ({8.987, 9, "Coulomb"})); + NS_TEST_ASSERT_MSG_EQ (ret2, true, "Setting valid values to tuple 2 failed"); + + TupleValue tupleValue2; + ret2 = tupleObject->GetAttributeFailSafe ("DoubleUintStringTuple", tupleValue2); + NS_TEST_ASSERT_MSG_EQ (ret2, true, "Getting values for tuple 2 failed"); + + t2 = tupleValue2.Get (); + NS_TEST_ASSERT_MSG_EQ (std::get<0> (t2), 8.987, "First element of tuple 2 not correctly set"); + NS_TEST_ASSERT_MSG_EQ (std::get<1> (t2), 9, "Second element of tuple 2 not correctly set"); + NS_TEST_ASSERT_MSG_EQ ((std::get<2> (t2) == "Coulomb"), true, "Third element of tuple 2 not correctly set"); + + // Test that we can set tuple 1 from string + ret1 = tupleObject->SetAttributeFailSafe ("StringStringEnumTuple", StringValue ("{Come, Together, VALUE1}")); + NS_TEST_ASSERT_MSG_EQ (ret1, true, "Setting valid values to tuple 1 failed"); + + t1 = tupleObject->GetTuple1 (); + NS_TEST_ASSERT_MSG_EQ ((std::get<0> (t1) == "Come"), true, "First element of tuple 1 not correctly set"); + NS_TEST_ASSERT_MSG_EQ ((std::get<1> (t1) == "Together"), true, "Second element of tuple 1 not correctly set"); + NS_TEST_ASSERT_MSG_EQ (std::get<2> (t1), (int)(TupleObject::VALUE1), "Third element of tuple 1 not correctly set"); + + // Test that we can set tuple 2 from string + ret2 = tupleObject->SetAttributeFailSafe ("DoubleUintStringTuple", StringValue ("{2.99, 8, LightSpeed}")); + NS_TEST_ASSERT_MSG_EQ (ret2, true, "Setting valid values to tuple 2 failed"); + + t2 = tupleObject->GetTuple2 (); + NS_TEST_ASSERT_MSG_EQ (std::get<0> (t2), 2.99, "First element of tuple 2 not correctly set"); + NS_TEST_ASSERT_MSG_EQ (std::get<1> (t2), 8, "Second element of tuple 2 not correctly set"); + NS_TEST_ASSERT_MSG_EQ ((std::get<2> (t2) == "LightSpeed"), true, "Third element of tuple 2 not correctly set"); + + // Test that setting invalid values fails + ret1 = tupleObject->SetAttributeFailSafe ("StringStringEnumTuple", + TupleValue ({"Get", "Back"})); + NS_TEST_ASSERT_MSG_EQ (ret1, false, "Too few values"); + NS_TEST_ASSERT_MSG_EQ ((tupleObject->GetTuple1 () == std::make_tuple ("Come", "Together", (int)(TupleObject::VALUE1))), true, + "Tuple modified after failed assignment"); + + ret1 = tupleObject->SetAttributeFailSafe ("StringStringEnumTuple", + MakeTupleValue (TupleObject::Tuple1 {"Get", "Back", TupleObject::VALUE3})); + NS_TEST_ASSERT_MSG_EQ (ret1, false, "Invalid enum value"); + NS_TEST_ASSERT_MSG_EQ ((tupleObject->GetTuple1 () == std::make_tuple ("Come", "Together", (int)(TupleObject::VALUE1))), true, + "Tuple modified after failed assignment"); + + ret2 = tupleObject->SetAttributeFailSafe ("DoubleUintStringTuple", + TupleValue ({4.83, 14, "Josephson", "constant"})); + NS_TEST_ASSERT_MSG_EQ (ret2, false, "Too many values"); + NS_TEST_ASSERT_MSG_EQ ((tupleObject->GetTuple2 () == std::make_tuple (2.99, 8, "LightSpeed")), true, + "Tuple modified after failed assignment"); + + ret2 = tupleObject->SetAttributeFailSafe ("DoubleUintStringTuple", + TupleValue ({48.3, 13, "Josephson"})); + NS_TEST_ASSERT_MSG_EQ (ret2, false, "Double value out of range"); + NS_TEST_ASSERT_MSG_EQ ((tupleObject->GetTuple2 () == std::make_tuple (2.99, 8, "LightSpeed")), true, + "Tuple modified after failed assignment"); + + ret2 = tupleObject->SetAttributeFailSafe ("DoubleUintStringTuple", + TupleValue ({4.83, 130, "Josephson"})); + NS_TEST_ASSERT_MSG_EQ (ret2, false, "Uinteger value out of range"); + NS_TEST_ASSERT_MSG_EQ ((tupleObject->GetTuple2 () == std::make_tuple (2.99, 8, "LightSpeed")), true, + "Tuple modified after failed assignment"); + + +} + + +/** Test suite */ +class TupleValueTestSuite : public TestSuite +{ + public: + TupleValueTestSuite (); +}; + +TupleValueTestSuite::TupleValueTestSuite () + : TestSuite ("tuple-value-test-suite", UNIT) +{ + AddTestCase (new TupleValueTestCase (), TestCase::QUICK); +} + +static TupleValueTestSuite g_tupleValueTestSuite; //!< Static variable for test initialization diff --git a/src/core/wscript b/src/core/wscript index 2654cf4df..ab1f6cafa 100644 --- a/src/core/wscript +++ b/src/core/wscript @@ -257,6 +257,7 @@ def build(bld): 'test/many-uniform-random-variables-one-get-value-call-test-suite.cc', 'test/one-uniform-random-variable-many-get-value-calls-test-suite.cc', 'test/pair-value-test-suite.cc', + 'test/tuple-value-test-suite.cc', 'test/sample-test-suite.cc', 'test/simulator-test-suite.cc', 'test/time-test-suite.cc', @@ -346,6 +347,7 @@ def build(bld): 'model/object-vector.h', 'model/object-map.h', 'model/pair.h', + 'model/tuple.h', 'model/deprecated.h', 'model/abort.h', 'model/names.h', diff --git a/utils/print-introspected-doxygen.cc b/utils/print-introspected-doxygen.cc index c3da85ce7..e9a39fb0b 100644 --- a/utils/print-introspected-doxygen.cc +++ b/utils/print-introspected-doxygen.cc @@ -1576,6 +1576,10 @@ PrintAttributeImplementations (std::ostream & os) PrintAttributeValueWithName (os, "Pair", "std::pair", "pair.h"); PrintMakeChecker (os, "Pair", "pair.h"); + PrintAttributeValueSection (os, "Tuple", false); + PrintAttributeValueWithName (os, "Tuple", "std::tuple", "tuple.h"); + PrintMakeChecker (os, "Tuple", "tuple.h"); + PrintAttributeValueSection (os, "AttributeContainer", false); PrintAttributeValueWithName (os, "AttributeContainer", "AttributeContainer", "attribute-container.h"); PrintMakeChecker (os, "AttributeContainer", "attribute-container.h");