Skip to content

Commit fba6f52

Browse files
committed
core: (fixes #1132) Add a function to register template classes inside a namespace
1 parent 033dc84 commit fba6f52

7 files changed

Lines changed: 209 additions & 0 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This file is a best-effort approach to solving this issue; we will do our best b
1818

1919
* (core) The `Time` class now declares an explicit `operator==` on MSVC builds (guarded by `NS_MSVC`), to work around an MSVC 18 (2026) STL issue that otherwise breaks compilation. It is semantically identical to the defaulted comparison and has no behavioral effect on any platform.
2020
* Centralization of ``PPP`` and ``IEEE802`` numbers. These are now contained in network model in ``iana-ppp-numbers.h`` and ``iana-ieee802-numbers.h`` respectively.
21+
* (core) The new `NS_OBJECT_TEMPLATE_CLASS_WITH_NS_DEFINE` macro enables the registration of template classes inside a namespace.
2122

2223
### Changes to existing API
2324

doc/doxygen.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2273,6 +2273,7 @@ PREDEFINED = NS3_ASSERT_ENABLE \
22732273
NS_OBJECT_ENSURE_REGISTERED()=1 \
22742274
NS_OBJECT_TEMPLATE_CLASS_DEFINE()=1 \
22752275
NS_OBJECT_TEMPLATE_CLASS_TWO_DEFINE()=1 \
2276+
NS_OBJECT_TEMPLATE_CLASS_WITH_NS_DEFINE()=1 \
22762277
NS3_BUILD_PROFILE_DEBUG \
22772278
NS3_BUILD_PROFILE_RELEASE \
22782279
NS3_BUILD_PROFILE_OPTIMIZED \

doc/manual/source/new-models.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,40 @@ An example for the ``CounterCalculator<uint32_t>``::
406406

407407
More details can be found in `issue #761 <https://gitlab.com/nsnam/ns-3-dev/-/issues/761>`_.
408408

409+
Template classes using namespaces should use a different macro:
410+
``NS_OBJECT_TEMPLATE_CLASS_WITH_NS_DEFINE(nst, type, nsp, param);`` where ``nst`` is the
411+
namespace of the type, and ``nsp`` is the namespace of the param.
412+
413+
An example is::
414+
415+
//.h file
416+
namespace ns3
417+
{
418+
namespace alpha
419+
{
420+
template <typename T>
421+
class AlphaProt : public std::enable_if_t<std::is_same_v<Object, T>, T>
422+
{
423+
public:
424+
static TypeId GetTypeId()
425+
{
426+
std::string name = GetTemplateClassName<AlphaProt<T>>();
427+
static TypeId tid = TypeId(name).SetParent<Object>();
428+
return tid;
429+
}
430+
};
431+
} // namespace alpha
432+
} // namespace ns3
433+
434+
//.cc file
435+
#include "ns3/header-file-name.h"
436+
namespace ns3
437+
{
438+
NS_OBJECT_TEMPLATE_CLASS_WITH_NS_DEFINE(alpha, AlphaProt, ns3, Object);
439+
}
440+
441+
More details can be found in `issue #1132 <https://gitlab.com/nsnam/ns-3-dev/-/issues/1132>`_.
442+
409443

410444
Including External Files
411445
++++++++++++++++++++++++

src/core/examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ set(base_examples
1212
sample-show-progress
1313
sample-simulator
1414
system-path-examples
15+
templated-classes
1516
test-string-value-formatting
1617
)
1718

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* SPDX-License-Identifier: GPL-2.0-only
3+
*/
4+
5+
/**
6+
* @file
7+
* Example to demonstrate and test the registration of templated classes.
8+
*/
9+
10+
#include "ns3/core-module.h"
11+
12+
NS_LOG_COMPONENT_DEFINE("TemplatedClassesExample");
13+
14+
namespace ns3
15+
{
16+
namespace alphaTest
17+
{
18+
19+
/**
20+
* Test class, inside the namespace ns3::alphaTest
21+
*/
22+
template <typename T>
23+
class AlphaProt : public std::enable_if_t<std::is_same_v<Object, T>, T>
24+
{
25+
public:
26+
/**
27+
* @brief Get the TypeId
28+
*
29+
* @return The TypeId for this class
30+
*/
31+
static TypeId GetTypeId()
32+
{
33+
std::string name = GetTemplateClassName<AlphaProt<T>>();
34+
35+
static TypeId tid = TypeId(name).SetParent<Object>();
36+
return tid;
37+
}
38+
};
39+
40+
} // namespace alphaTest
41+
42+
/**
43+
* Test class, inside the namespace ns3
44+
*/
45+
template <typename T>
46+
class BetaProt : public std::enable_if_t<std::is_same_v<Object, T>, T>
47+
{
48+
public:
49+
/**
50+
* @brief Get the TypeId
51+
*
52+
* @return The TypeId for this class
53+
*/
54+
static TypeId GetTypeId()
55+
{
56+
std::string name = GetTemplateClassName<BetaProt<T>>();
57+
58+
static TypeId tid = TypeId(name).SetParent<Object>();
59+
return tid;
60+
}
61+
};
62+
63+
NS_OBJECT_TEMPLATE_CLASS_WITH_NS_DEFINE(alphaTest, AlphaProt, ns3, Object);
64+
NS_OBJECT_TEMPLATE_CLASS_DEFINE(BetaProt, Object);
65+
66+
} // namespace ns3
67+
68+
using namespace ns3;
69+
70+
int
71+
main(int argc, char* argv[])
72+
{
73+
Ptr<alphaTest::AlphaProt<Object>> alpha = CreateObject<alphaTest::AlphaProt<Object>>();
74+
75+
std::string alphaName("ns3::alphaTest::AlphaProt<Object>");
76+
std::string alphaClassName(alpha->GetInstanceTypeId().GetName());
77+
std::string alphaClassNameDirect(GetTemplateClassName<alphaTest::AlphaProt<Object>>());
78+
79+
if (alphaClassName != alphaName)
80+
{
81+
std::cerr << "Unexpected templated class name (\"" << alphaClassName << "\" instead of \""
82+
<< alphaName << "\")";
83+
return -1;
84+
}
85+
if (alphaClassNameDirect != alphaName)
86+
{
87+
std::cerr << "Unexpected templated class name (\"" << alphaClassNameDirect
88+
<< "\" instead of \"" << alphaName << "\")";
89+
return -1;
90+
}
91+
92+
Ptr<BetaProt<Object>> beta = CreateObject<BetaProt<Object>>();
93+
94+
std::string betaName("ns3::BetaProt<Object>");
95+
std::string betaClassName(beta->GetInstanceTypeId().GetName());
96+
std::string betaClassNameDirect(GetTemplateClassName<BetaProt<Object>>());
97+
98+
if (betaClassName != betaName)
99+
{
100+
std::cerr << "Unexpected templated class name (\"" << betaClassName << "\" instead of \""
101+
<< betaName << "\")";
102+
return -1;
103+
}
104+
if (betaClassNameDirect != betaName)
105+
{
106+
std::cerr << "Unexpected templated class name (\"" << betaClassNameDirect
107+
<< "\" instead of \"" << betaName << "\")";
108+
return -1;
109+
}
110+
111+
return 0;
112+
}

src/core/model/object-base.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,65 @@
8282
} \
8383
} Object##type##param##RegistrationVariable
8484

85+
/**
86+
* @ingroup object
87+
* @brief Explicitly instantiate a template class with one template parameter
88+
* and register the resulting instance with the TypeId system.
89+
*
90+
* This version of the macro allows the user to specify a namespace
91+
* for the template class and a different namespace for the template parameter.
92+
*
93+
* The `nst` parameter is the namespace of the templated class, which must be
94+
* in the `ns3` namespace and can be in a nested namespace, e.g., `ns3::aodv`.
95+
* Adding the leading `ns3` namespace is optional for nested namespace, while for
96+
* classes in the `ns3` namespace is mandatory.
97+
*
98+
* The `nsp` parameter is the namespace of the template parameter, and can be
99+
* any namespace, including `std`, `ns3`, or a nested namespace like `ns3::aodv`.
100+
* Also in this case, adding the leading `ns3` namespace is optional for nested
101+
* namespace, while for objects in the `ns3` namespace is mandatory.
102+
* The parameter can also be in the `std` namespace, e.g., `std::uint16_t` will have
103+
* `nsp` set to `std` and `param` to `uint16_t`.
104+
*
105+
* The result of the function ``GetTemplateClassName`` depends on these values, and
106+
* typically will be something like `ns3::nst::type<nsp::param>`.
107+
*
108+
* @sa NS_OBJECT_TEMPLATE_CLASS_DEFINE
109+
*
110+
* @note This macro must be used in the `ns3` namespace.
111+
*
112+
* @param nst the namespace of the template class
113+
* @param type the template class
114+
* @param nsp the namespace of the parameter (typically `ns3`)
115+
* @param param the first template parameter
116+
*/
117+
#define NS_OBJECT_TEMPLATE_CLASS_WITH_NS_DEFINE(nst, type, nsp, param) \
118+
template class nst::type<nsp::param>; \
119+
template <> \
120+
std::string DoGetTemplateClassName<nst::type<nsp::param>>() \
121+
{ \
122+
constexpr std::string_view prefix = "ns3::"; \
123+
std::string cName = std::string(#nst) + "::" + #type; \
124+
if (cName.starts_with(prefix)) \
125+
cName.erase(0, prefix.size()); \
126+
std::string pName = std::string(#nsp) + "::" + #param; \
127+
if (pName.starts_with(prefix)) \
128+
pName.erase(0, prefix.size()); \
129+
return std::string("ns3::") + cName + std::string("<") + pName + std::string(">"); \
130+
} \
131+
namespace nst \
132+
{ \
133+
static struct Object##type##nsp##param##RegistrationClass \
134+
{ \
135+
Object##type##nsp##param##RegistrationClass() \
136+
{ \
137+
ns3::TypeId tid = type<nsp::param>::GetTypeId(); \
138+
tid.SetSize(sizeof(type<nsp::param>)); \
139+
tid.GetParent(); \
140+
} \
141+
} Object##type##nsp##param##RegistrationVariable; \
142+
}
143+
85144
/**
86145
* @ingroup object
87146
* @brief Explicitly instantiate a template class with two template parameters

src/core/test/examples-to-run.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
("main-random-variable", "True", "False"),
1515
("sample-random-variable", "True", "True"),
1616
("system-path-examples", "True", "True"),
17+
("templated-classes", "True", "True"),
1718
("test-string-value-formatting", "True", "True"),
1819
]
1920

0 commit comments

Comments
 (0)