ReferenceContainers

The ReferenceContainer is one of the core classes of the framework and allows to create trees of references of any complexity.

../../_images/ReferenceContainer-1.png

Given that the ReferenceContainer inherits from Object, all the strategies described in the References section also apply.

//This is a valid way of creating a new ReferenceContainer
ReferenceT<ReferenceContainer> ref("ReferenceContainer", GlobalObjectsDatabase::Instance()->GetStandardHeap());

Note

The access to the container is protected by an internal FastPollingMutexSem whose timeout can be specified.

The ReferenceContainer Purge method must be called when the object is no longer needed (typically when the application terminates).

The Purge function will destroy each element of the container and break any reference loops (the father contains a reference to the children and the children contains a reference to the father).

This method must also be used to implement any house-cleaning that is required before the elements are removed from the container (e.g. stopping threads that use these Objects). An example can be found in the Purge method of the StateMachine class.

Warning

The smart pointer mechanism guarantees that when there are no references pointing at a given object, this object is destroyed. Nevertheless, this does not prevent developers from creating internal references, so that there will be at least one reference pointing at the Object and thus it never gets destroyed.

An example:

  • A is a specialised ReferenceContainer and has a member b which is also a ReferenceContainer.

  • b contains an element C (b.Get(0)) which in turn contains a reference to A.

  • A will then always have at least one unreachable reference pointing at it, as b can only be destroyed (thus calling Delete(0)) when A is destroyed, but A cannot be destroyed because it has a reference pointing at it.

../../_images/ReferenceContainer-2.png

This can be handled by specialing the Object Purge method (see the Example below).

Nevertheless, avoid designs where classes have ReferenceContainer members. If you have to have such variables in your class, make sure that the Purge method is specialised to manually break any link (see ReferenceContainer), as otherwise unattended circular references may prevent the underlying Objects from being destroyed.

The preferred strategy is to have the class directly inheriting from ReferenceContainer and to add any references to it. Note also that the ReferenceContainer may hold, contemporarily, references to any number of class types.

Examples

The following example shows how to add/get references to/from a container.

ReferenceContainer example (ReferenceExample5)
  1/**
  2 * @file ReferencesExample5.cpp
  3 * @brief Source file for class ReferencesExample5
  4 * @date 14/03/2018
  5 * @author Andre' Neto
  6 *
  7 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
  8 * the Development of Fusion Energy ('Fusion for Energy').
  9 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 10 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 11 * You may not use this work except in compliance with the Licence.
 12 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 13 *
 14 * @warning Unless required by applicable law or agreed to in writing, 
 15 * software distributed under the Licence is distributed on an "AS IS"
 16 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 17 * or implied. See the Licence permissions and limitations under the Licence.
 18
 19 * @details This source file contains the definition of all the methods for
 20 * the class ReferencesExample5 (public, protected, and private). Be aware that some
 21 * methods, such as those inline could be defined on the header file, instead.
 22 */
 23
 24#define DLL_API
 25
 26/*---------------------------------------------------------------------------*/
 27/*                         Standard header includes                          */
 28/*---------------------------------------------------------------------------*/
 29
 30/*---------------------------------------------------------------------------*/
 31/*                         Project header includes                           */
 32/*---------------------------------------------------------------------------*/
 33#include "AdvancedErrorManagement.h"
 34#include "ClassRegistryDatabase.h"
 35#include "ErrorLoggerExample.h"
 36#include "Object.h"
 37#include "Reference.h"
 38#include "ReferenceContainer.h"
 39#include "ReferenceT.h"
 40#include "StreamString.h"
 41
 42/*---------------------------------------------------------------------------*/
 43/*                           Static definitions                              */
 44/*---------------------------------------------------------------------------*/
 45
 46/*---------------------------------------------------------------------------*/
 47/*                           Method definitions                              */
 48/*---------------------------------------------------------------------------*/
 49namespace MARTe2Tutorial {
 50
 51
 52class ControllerEx1: public MARTe::Object {
 53public:
 54    CLASS_REGISTER_DECLARATION()
 55
 56    /**
 57     * @brief NOOP.
 58     */
 59ControllerEx1    () {
 60        gain = 0u;
 61    }
 62
 63    virtual ~ControllerEx1() {
 64        using namespace MARTe;
 65        if (GetName() != NULL) {
 66            REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
 67            		"pointing at %s [%s]. The Object will "
 68            		"be safely deleted.", GetName(), GetClassProperties()->GetName());
 69        }
 70    }
 71
 72    virtual void AFunction () {
 73        using namespace MARTe;
 74        REPORT_ERROR_STATIC(ErrorManagement::Information, "AFunction called @ %s.", GetName());
 75    }
 76
 77    /**
 78     * A property.
 79     */
 80    MARTe::uint32 gain;
 81};
 82
 83CLASS_REGISTER(ControllerEx1, "")
 84
 85class PIDEx1: public ControllerEx1 {
 86public:
 87    CLASS_REGISTER_DECLARATION()
 88
 89    /**
 90     * @brief NOOP.
 91     */
 92PIDEx1    () {
 93        property3 = 0u;
 94    }
 95
 96    virtual ~PIDEx1() {
 97        using namespace MARTe;
 98        REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
 99        		"pointing at %s [%s]. The Object will "
100        		"be safely deleted.", GetName(), GetClassProperties()->GetName());
101    }
102
103    virtual void AFunction () {
104        using namespace MARTe;
105        REPORT_ERROR_STATIC(ErrorManagement::Information, "AFunction called @ %s.", GetName());
106    }
107    /**
108     * A property.
109     */
110    MARTe::uint32 property3;
111};
112
113CLASS_REGISTER(PIDEx1, "")
114
115
116class ControllerContainerEx1: public MARTe::ReferenceContainer {
117public:
118    CLASS_REGISTER_DECLARATION()
119
120	/**
121	 * @brief NOOP.
122	 */
123	ControllerContainerEx1() {
124    }
125
126    virtual ~ControllerContainerEx1() {
127        using namespace MARTe;
128        REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
129        		"pointing at %s [%s]. The Object will "
130        		"be safely deleted.", GetName(), GetClassProperties()->GetName());
131    }
132
133    /**
134     * @brief Calls AFunction on all of its elements
135     */
136    void Do() {
137    	using namespace MARTe;
138    	uint32 i;
139    	uint32 size = Size();
140    	for (i = 0u; i < size; i++) {
141    		ReferenceT<ControllerEx1> r = Get(i);
142    	    if (r.IsValid()) {
143    	    	r->AFunction();
144    	    }
145    	}
146    }
147
148};
149CLASS_REGISTER(ControllerContainerEx1, "")
150
151}
152
153int main(int argc, char **argv) {
154    using namespace MARTe;
155    using namespace MARTe2Tutorial;
156    SetErrorProcessFunction(&ErrorProcessExampleFunction);
157
158    CCString className1 = "ControllerEx1";
159    CCString className2 = "PIDEx1";
160
161    //Automatically generate a new object instance based on the class
162    //name and on the correct Heap and with the template reference.
163    ReferenceT<ControllerEx1> ref1(className1,
164    		GlobalObjectsDatabase::Instance()->GetStandardHeap());
165    if (ref1.IsValid()) {
166        ref1->SetName("ControllerInstance1");
167        REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
168        		"created an instance of %s", className1.GetList());
169    }
170    ReferenceT<ControllerEx1> ref2(className2,
171    		GlobalObjectsDatabase::Instance()->GetStandardHeap());
172    if (ref2.IsValid()) {
173        ref2->SetName("ControllerInstance2");
174        REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
175        		"created an instance of %s", className2.GetList());
176    }
177    //This mechanism also works with compatible subclasses PIDEx1->ControllerEx1
178    ReferenceT<PIDEx1> ref3(className2,
179    		GlobalObjectsDatabase::Instance()->GetStandardHeap());
180    if (ref3.IsValid()) {
181        ref3->SetName("PIDInstance1");
182        REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
183        		"created an instance of %s", className2.GetList());
184    }
185
186    ReferenceT<ControllerContainerEx1> controllerContainer("ControllerContainerEx1",
187    		GlobalObjectsDatabase::Instance()->GetStandardHeap());
188    if (controllerContainer.IsValid()) {
189    	controllerContainer->SetName("Container");
190    	controllerContainer->Insert(ref1);
191    	controllerContainer->Insert(ref2);
192    	controllerContainer->Insert(ref3);
193    	controllerContainer->Do();
194    }
195    return 0;
196}

This example highlights the circular reference problem. By specialising the Purge method, the ContainerEx2 class will break the link and allow all Objects to be destroyed.

Circular reference (ReferenceExample6)
  1/**
  2 * @file ReferencesExample6.cpp
  3 * @brief Source file for class ReferencesExample6
  4 * @date 14/03/2018
  5 * @author Andre' Neto
  6 *
  7 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
  8 * the Development of Fusion Energy ('Fusion for Energy').
  9 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 10 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 11 * You may not use this work except in compliance with the Licence.
 12 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 13 *
 14 * @warning Unless required by applicable law or agreed to in writing, 
 15 * software distributed under the Licence is distributed on an "AS IS"
 16 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 17 * or implied. See the Licence permissions and limitations under the Licence.
 18
 19 * @details This source file contains the definition of all the methods for
 20 * the class ReferencesExample6 (public, protected, and private). Be aware that some
 21 * methods, such as those inline could be defined on the header file, instead.
 22 */
 23
 24#define DLL_API
 25
 26/*---------------------------------------------------------------------------*/
 27/*                         Standard header includes                          */
 28/*---------------------------------------------------------------------------*/
 29
 30/*---------------------------------------------------------------------------*/
 31/*                         Project header includes                           */
 32/*---------------------------------------------------------------------------*/
 33#include "AdvancedErrorManagement.h"
 34#include "ClassRegistryDatabase.h"
 35#include "ErrorLoggerExample.h"
 36#include "Object.h"
 37#include "ObjectRegistryDatabase.h"
 38#include "Reference.h"
 39#include "ReferenceContainer.h"
 40#include "ReferenceT.h"
 41#include "StreamString.h"
 42
 43/*---------------------------------------------------------------------------*/
 44/*                           Static definitions                              */
 45/*---------------------------------------------------------------------------*/
 46
 47/*---------------------------------------------------------------------------*/
 48/*                           Method definitions                              */
 49/*---------------------------------------------------------------------------*/
 50namespace MARTe2Tutorial {
 51
 52class ObjectEx1: public MARTe::Object {
 53public:
 54	CLASS_REGISTER_DECLARATION()
 55
 56	/**
 57	 * @brief NOOP.
 58	 */
 59	ObjectEx1 () {
 60	}
 61
 62	virtual ~ObjectEx1() {
 63		using namespace MARTe;
 64		if (GetName() != NULL) {
 65			REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
 66					"pointing at %s [%s]. The Object will "
 67					"be safely deleted.", GetName(), GetClassProperties()->GetName());
 68		}
 69	}
 70
 71	void SetParent(MARTe::Reference ref) {
 72		parent = ref;
 73	}
 74
 75	/**
 76	 * A private container. This should be avoided!
 77	 */
 78	MARTe::Reference parent;
 79
 80};
 81
 82CLASS_REGISTER(ObjectEx1, "")
 83
 84/**
 85 * Does not implement the Purge method and as such a cyclic link will not be broken.
 86 */
 87class ContainerEx1: public MARTe::ReferenceContainer {
 88public:
 89	CLASS_REGISTER_DECLARATION()
 90
 91	/**
 92	 * @brief NOOP.
 93	 */
 94	ContainerEx1 () {
 95	}
 96
 97	virtual ~ContainerEx1() {
 98		using namespace MARTe;
 99		REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
100				"pointing at %s [%s]. The Object will "
101				"be safely deleted.", GetName(), GetClassProperties()->GetName());
102	}
103
104	void CreateLoop(MARTe::Reference aReferenceIn) {
105		aReference.Insert(aReferenceIn);
106	}
107
108	MARTe::ReferenceContainer aReference;
109};
110CLASS_REGISTER(ContainerEx1, "")
111
112/**
113 * Implements the Purge method.
114 */
115class ContainerEx2: public ContainerEx1 {
116public:
117	CLASS_REGISTER_DECLARATION()
118
119	/**
120	 * @brief NOOP.
121	 */
122	ContainerEx2 () {
123	}
124
125	virtual ~ContainerEx2() {
126		using namespace MARTe;
127		REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
128				"pointing at %s [%s]. The Object will "
129				"be safely deleted.", GetName(), GetClassProperties()->GetName());
130	}
131
132	/**
133	 * Destroys an existent links
134	 */
135	virtual void Purge(MARTe::ReferenceContainer &purgeList) {
136		using namespace MARTe;
137		//Destroy the circular link
138		aReference.Purge();
139		//Alternative which would also destroy the link
140		/*ReferenceT<ObjectEx1> ref = aReference.Get(0);
141		if (ref.IsValid()) {
142			//Set an invalid reference to break the link.
143			ref->SetParent(Reference());
144		}*/
145		//Do not forget to call the parent implementation
146		ReferenceContainer::Purge(purgeList);
147	}
148
149};
150CLASS_REGISTER(ContainerEx2, "")
151
152}
153
154int main(int argc, char **argv) {
155	using namespace MARTe;
156	using namespace MARTe2Tutorial;
157	SetErrorProcessFunction(&ErrorProcessExampleFunction);
158
159	CCString className1 = "ObjectEx1";
160	CCString className2 = "ContainerEx1";
161	CCString className3 = "ContainerEx2";
162
163	//Automatically generate a new object instance based on the class
164	//name and on the correct Heap and with the template reference.
165	ReferenceT<ObjectEx1> ref1(className1,
166			GlobalObjectsDatabase::Instance()->GetStandardHeap());
167	if (ref1.IsValid()) {
168		ref1->SetName("ObjectExIntance1");
169		REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
170				"created an instance of %s", className1.GetList());
171	}
172	ReferenceT<ObjectEx1> ref2(className1,
173			GlobalObjectsDatabase::Instance()->GetStandardHeap());
174	if (ref2.IsValid()) {
175		ref2->SetName("ObjectExIntance2");
176		REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
177				"created an instance of %s", className1.GetList());
178	}
179	ReferenceT<ContainerEx1> ref3(className2,
180			GlobalObjectsDatabase::Instance()->GetStandardHeap());
181	if (ref3.IsValid()) {
182		ref3->SetName("ContainerExIntance1");
183		ref3->Insert(ref1);
184		//This will create a circular dependency
185		ref3->CreateLoop(ref1);
186		ref1->SetParent(ref3);
187		REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
188				"created an instance of %s", className2.GetList());
189
190	}
191	ReferenceT<ContainerEx2> ref4(className3,
192			GlobalObjectsDatabase::Instance()->GetStandardHeap());
193	if (ref4.IsValid()) {
194		ref4->SetName("ContainerExIntance2");
195		ref4->Insert(ref2);
196		ref4->CreateLoop(ref2);
197		ref2->SetParent(ref4);
198		REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
199				"created an instance of %s", className3.GetList());
200	}
201
202	if (ref3.IsValid()) {
203		ref3->Purge();
204	}
205	if (ref4.IsValid()) {
206		ref4->ReferenceContainer::Purge();
207	}
208
209	if (ref1.IsValid() && ref2.IsValid() && ref3.IsValid() && ref4.IsValid()) {
210		REPORT_ERROR_STATIC(ErrorManagement::Information, "%s "
211				"number of references: %d", ref1->GetName(),
212				ref3->NumberOfReferences());
213		REPORT_ERROR_STATIC(ErrorManagement::Information, "%s "
214				"number of references: %d", ref2->GetName(),
215				ref2->NumberOfReferences());
216		REPORT_ERROR_STATIC(ErrorManagement::Information, "%s "
217				"number of references: %d", ref3->GetName(),
218				ref3->NumberOfReferences());
219		REPORT_ERROR_STATIC(ErrorManagement::Information, "%s "
220				"number of references: %d", ref4->GetName(),
221				ref4->NumberOfReferences());
222
223		if (ref1->NumberOfReferences() == 2u
224				&& ref3->NumberOfReferences() == 2u) {
225			REPORT_ERROR_STATIC(ErrorManagement::Warning,
226					"As expected %s and %s have a circular dependency and thus cannot be destroyed."
227					, ref1->GetName(), ref3->GetName());
228		}
229	}
230	return 0;
231}