core: Add an attribute value storing a std::tuple

This commit is contained in:
Stefano Avallone
2021-11-16 12:18:12 +01:00
parent d860f02187
commit 6ec6fc2eb6
6 changed files with 913 additions and 0 deletions

View File

@@ -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<A, B>
* PointerValue
* PriomapValue
* QueueSizeValue
* RectangleValue
* SsidValue
* TimeValue
* TupleValue<Args...>
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
+++++++++++++++++++

View File

@@ -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

534
src/core/model/tuple.h Normal file
View File

@@ -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 <stavallo@unina.it>
*/
#ifndef TUPLE_H
#define TUPLE_H
#include <ns3/attribute-helper.h>
#include <ns3/string.h>
#include <sstream>
#include <type_traits>
#include <utility>
#include <tuple>
#include <algorithm>
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 <class... Args>
std::ostream &
operator << (std::ostream &os, const std::tuple<Args...> &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<Args...>.
* \tparam Args \explicit The list of AttributeValues to be held by this TupleValue
*/
template <class... Args>
class TupleValue : public AttributeValue
{
public:
/** Type of value stored in the TupleValue. */
typedef std::tuple<Args...> value_type;
/** Type returned by Get or passed in Set. */
typedef std::tuple<std::invoke_result_t<decltype(&Args::Get),Args>...> result_type;
TupleValue ();
/**
* Construct this TupleValue from a std::tuple
*
* \param [in] value Value with which to construct.
*/
TupleValue (const result_type &value);
Ptr<AttributeValue> Copy (void) const override;
bool DeserializeFromString (std::string value, Ptr<const AttributeChecker> checker) override;
std::string SerializeToString (Ptr<const AttributeChecker> 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<AV> 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 <typename T>
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 <std::size_t... Is>
bool SetValueImpl (std::index_sequence<Is...>, const std::vector<Ptr<AttributeValue>>& values);
value_type m_value; //!< Tuple of attribute values
};
/**
* Create a TupleValue object. Enable to write code like this snippet:
*
* \code
* typedef std::tuple<uint16_t, double> Tuple;
* typedef std::tuple<UintegerValue, DoubleValue> Pack;
*
* TupleValue<UintegerValue, DoubleValue> t = MakeTupleValue<Pack> (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 <class T1, class T2>
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<Ptr<const AttributeChecker>>& 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 <class... Args, class... Ts>
Ptr<const AttributeChecker> 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 <class... Args, class T1>
Ptr<const AttributeAccessor> 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 <class... Args, class T1, class T2>
Ptr<const AttributeAccessor> MakeTupleAccessor (T1 a1, T2 a2);
} // namespace ns3
/*****************************************************************************
* Implementation below
*****************************************************************************/
namespace ns3 {
template <class... Args>
TupleValue<Args...>::TupleValue ()
: m_value (std::make_tuple (Args ()...))
{
}
template <class... Args>
TupleValue<Args...>::TupleValue (const result_type &value)
{
Set (value);
}
template <class... Args>
Ptr<AttributeValue>
TupleValue<Args...>::Copy (void) const
{
return Create <TupleValue <Args...>> (Get ());
}
template <class... Args>
template <std::size_t... Is>
bool
TupleValue<Args...>::SetValueImpl (std::index_sequence<Is...>, const std::vector<Ptr<AttributeValue>>& values)
{
auto valueTuple = std::make_tuple (DynamicCast<Args> (values[Is])...);
bool ok = ((std::get<Is> (valueTuple) != nullptr) && ...);
if (ok)
{
m_value = std::make_tuple (Args (*std::get<Is> (valueTuple))...);
}
return ok;
}
template <class... Args>
bool
TupleValue<Args...>::DeserializeFromString (std::string value, Ptr<const AttributeChecker> checker)
{
auto tupleChecker = DynamicCast<const TupleChecker> (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<Ptr<AttributeValue>> 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<Args...>{}, values);
}
template <class... Args>
std::string
TupleValue<Args...>::SerializeToString (Ptr<const AttributeChecker> checker) const
{
std::ostringstream oss;
oss << "{" << Get () << "}";
return oss.str ();
}
template <class... Args>
typename TupleValue<Args...>::result_type
TupleValue<Args...>::Get (void) const
{
return std::apply ([](Args... values)
{
return std::make_tuple (values.Get ()...);
},
m_value);
}
template <class... Args>
void
TupleValue<Args...>::Set (const typename TupleValue<Args...>::result_type &value)
{
m_value = std::apply ([](auto&&... args)
{
return std::make_tuple (Args (args)...);
},
value);
}
template <class... Args>
typename TupleValue<Args...>::value_type
TupleValue<Args...>::GetValue (void) const
{
return m_value;
}
template <class... Args>
template <typename T>
bool
TupleValue<Args...>::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... Args>
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 <class... Ts>
TupleChecker (Ts... checkers)
: m_checkers {checkers...}
{
}
const std::vector<Ptr<const AttributeChecker>>& GetCheckers (void) const override
{
return m_checkers;
}
bool Check (const AttributeValue &value) const override
{
const TupleValue<Args...>* v = dynamic_cast<const TupleValue<Args...>*> (&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<AttributeValue> Create (void) const override
{
return ns3::Create<TupleValue<Args...>> ();
}
bool Copy (const AttributeValue &source, AttributeValue &destination) const override
{
const TupleValue<Args...> *src = dynamic_cast<const TupleValue<Args...> *> (&source);
TupleValue<Args...> *dst = dynamic_cast<TupleValue<Args...> *> (&destination);
if (src == 0 || dst == 0)
{
return false;
}
*dst = *src;
return true;
}
private:
std::vector<Ptr<const AttributeChecker>> 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 <class... Args>
struct TupleHelper
{
/**
* \copydoc ns3::MakeTupleChecker
*/
template <class... Ts>
static Ptr<const AttributeChecker> MakeTupleChecker (Ts... checkers)
{
return Create <internal::TupleChecker<Args...>> (checkers...);
}
/**
* \copydoc ns3::MakeTupleAccessor(T1)
*/
template <class T1>
static Ptr<const AttributeAccessor> MakeTupleAccessor (T1 a1)
{
return MakeAccessorHelper<TupleValue<Args...>> (a1);
}
/**
* \copydoc ns3::MakeTupleAccessor(T1,T2)
*/
template <class T1, class T2>
static Ptr<const AttributeAccessor> MakeTupleAccessor (T1 a1, T2 a2)
{
return MakeAccessorHelper<TupleValue<Args...>> (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 <class... Args>
struct TupleHelper<std::tuple<Args...>>
{
/**
* \copydoc ns3::MakeTupleValue
*/
static TupleValue<Args...> MakeTupleValue (const typename TupleValue<Args...>::result_type& t)
{
return TupleValue<Args...> (t);
}
/**
* \copydoc ns3::MakeTupleChecker
*/
template <class... Ts>
static Ptr<const AttributeChecker> MakeTupleChecker (Ts... checkers)
{
return Create <internal::TupleChecker<Args...>> (checkers...);
}
/**
* \copydoc ns3::MakeTupleAccessor(T1)
*/
template <class T1>
static Ptr<const AttributeAccessor> MakeTupleAccessor (T1 a1)
{
return MakeAccessorHelper<TupleValue<Args...>> (a1);
}
/**
* \copydoc ns3::MakeTupleAccessor(T1,T2)
*/
template <class T1, class T2>
static Ptr<const AttributeAccessor> MakeTupleAccessor (T1 a1, T2 a2)
{
return MakeAccessorHelper<TupleValue<Args...>> (a1, a2);
}
};
} // namespace internal
template <class T1, class T2>
auto MakeTupleValue (T2 t)
{
return internal::TupleHelper<T1>::MakeTupleValue (t);
}
template <class... Args, class... Ts>
Ptr<const AttributeChecker> MakeTupleChecker (Ts... checkers)
{
return internal::TupleHelper<Args...>::template MakeTupleChecker<Ts...> (checkers...);
}
template <class... Args, class T1>
Ptr<const AttributeAccessor> MakeTupleAccessor (T1 a1)
{
return internal::TupleHelper<Args...>::template MakeTupleAccessor<T1> (a1);
}
template <class... Args, class T1, class T2>
Ptr<const AttributeAccessor> MakeTupleAccessor (T1 a1, T2 a2)
{
return internal::TupleHelper<Args...>::template MakeTupleAccessor<T1, T2> (a1, a2);
}
} // namespace ns3
#endif // TUPLE_H

View File

@@ -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 <stavallo@unina.it>
*/
#include <ns3/test.h>
#include <ns3/log.h>
#include <ns3/tuple.h>
#include <ns3/double.h>
#include <ns3/uinteger.h>
#include <ns3/string.h>
#include <ns3/ptr.h>
#include <ns3/object.h>
#include <ns3/enum.h>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <utility>
#include <tuple>
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<StringValue, StringValue, EnumValue>; //!< Tuple1 attribute value
using Tuple1 = Tuple1Value::result_type; //!< tuple of values
using Tuple1Pack = Tuple1Value::value_type; //!< tuple of attribute values
using Tuple2 = std::tuple <double, uint16_t, std::string>; //!< 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<Object> ()
.SetGroupName("Test")
.AddConstructor<TupleObject> ()
.AddAttribute ("StringStringEnumTuple", "Tuple1: string, string, enum",
MakeTupleValue<Tuple1Pack> (Tuple1 {"Hey", "Jude", TupleObject::VALUE1}),
MakeTupleAccessor<Tuple1Pack> (&TupleObject::m_tuple1),
MakeTupleChecker<Tuple1Pack> (MakeStringChecker (), MakeStringChecker (),
MakeEnumChecker (TupleObject::VALUE1, "VALUE1",
TupleObject::VALUE2, "VALUE2")))
.AddAttribute ("DoubleUintStringTuple", "Tuple2: double, uint16_t, string",
TupleValue <DoubleValue, UintegerValue, StringValue> ({6.022, 23, "Avogadro"}),
MakeTupleAccessor <DoubleValue, UintegerValue, StringValue> (&TupleObject::SetTuple2,
&TupleObject::GetTuple2),
MakeTupleChecker<DoubleValue, UintegerValue, StringValue> (MakeDoubleChecker<double> (1.0, 10.0),
MakeUintegerChecker<int> (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 <TupleObject> ();
// 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::Tuple1Pack> (TupleObject::Tuple1 {"Norwegian", "Wood", TupleObject::VALUE2}));
NS_TEST_ASSERT_MSG_EQ (ret1, true, "Setting valid values to tuple 1 failed");
TupleValue<StringValue, StringValue, EnumValue> 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 <DoubleValue, UintegerValue, StringValue> ({8.987, 9, "Coulomb"}));
NS_TEST_ASSERT_MSG_EQ (ret2, true, "Setting valid values to tuple 2 failed");
TupleValue <DoubleValue, UintegerValue, StringValue> 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<StringValue, StringValue> ({"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::Tuple1Pack> (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 <DoubleValue, UintegerValue, StringValue, StringValue> ({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 <DoubleValue, UintegerValue, StringValue> ({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 <DoubleValue, UintegerValue, StringValue> ({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

View File

@@ -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',

View File

@@ -1576,6 +1576,10 @@ PrintAttributeImplementations (std::ostream & os)
PrintAttributeValueWithName (os, "Pair", "std::pair<A, B>", "pair.h");
PrintMakeChecker (os, "Pair", "pair.h");
PrintAttributeValueSection (os, "Tuple", false);
PrintAttributeValueWithName (os, "Tuple", "std::tuple<Args...>", "tuple.h");
PrintMakeChecker (os, "Tuple", "tuple.h");
PrintAttributeValueSection (os, "AttributeContainer", false);
PrintAttributeValueWithName (os, "AttributeContainer", "AttributeContainer", "attribute-container.h");
PrintMakeChecker (os, "AttributeContainer", "attribute-container.h");