core: Add an attribute value storing a std::tuple
This commit is contained in:
@@ -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
|
||||
+++++++++++++++++++
|
||||
|
||||
|
||||
@@ -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
534
src/core/model/tuple.h
Normal 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
|
||||
283
src/core/test/tuple-value-test-suite.cc
Normal file
283
src/core/test/tuple-value-test-suite.cc
Normal 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
|
||||
@@ -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',
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user