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
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/**
 * @file ReferencesExample5.cpp
 * @brief Source file for class ReferencesExample5
 * @date 14/03/2018
 * @author Andre' Neto
 *
 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 * the Development of Fusion Energy ('Fusion for Energy').
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 *
 * @warning Unless required by applicable law or agreed to in writing, 
 * software distributed under the Licence is distributed on an "AS IS"
 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the Licence permissions and limitations under the Licence.

 * @details This source file contains the definition of all the methods for
 * the class ReferencesExample5 (public, protected, and private). Be aware that some
 * methods, such as those inline could be defined on the header file, instead.
 */

#define DLL_API

/*---------------------------------------------------------------------------*/
/*                         Standard header includes                          */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                         Project header includes                           */
/*---------------------------------------------------------------------------*/
#include "AdvancedErrorManagement.h"
#include "ClassRegistryDatabase.h"
#include "ErrorLoggerExample.h"
#include "Object.h"
#include "Reference.h"
#include "ReferenceContainer.h"
#include "ReferenceT.h"
#include "StreamString.h"

/*---------------------------------------------------------------------------*/
/*                           Static definitions                              */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                           Method definitions                              */
/*---------------------------------------------------------------------------*/
namespace MARTe2Tutorial {


class ControllerEx1: public MARTe::Object {
public:
    CLASS_REGISTER_DECLARATION()

    /**
     * @brief NOOP.
     */
ControllerEx1    () {
        gain = 0u;
    }

    virtual ~ControllerEx1() {
        using namespace MARTe;
        if (GetName() != NULL) {
            REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
            		"pointing at %s [%s]. The Object will "
            		"be safely deleted.", GetName(), GetClassProperties()->GetName());
        }
    }

    virtual void AFunction () {
        using namespace MARTe;
        REPORT_ERROR_STATIC(ErrorManagement::Information, "AFunction called @ %s.", GetName());
    }

    /**
     * A property.
     */
    MARTe::uint32 gain;
};

CLASS_REGISTER(ControllerEx1, "")

class PIDEx1: public ControllerEx1 {
public:
    CLASS_REGISTER_DECLARATION()

    /**
     * @brief NOOP.
     */
PIDEx1    () {
        property3 = 0u;
    }

    virtual ~PIDEx1() {
        using namespace MARTe;
        REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
        		"pointing at %s [%s]. The Object will "
        		"be safely deleted.", GetName(), GetClassProperties()->GetName());
    }

    virtual void AFunction () {
        using namespace MARTe;
        REPORT_ERROR_STATIC(ErrorManagement::Information, "AFunction called @ %s.", GetName());
    }
    /**
     * A property.
     */
    MARTe::uint32 property3;
};

CLASS_REGISTER(PIDEx1, "")


class ControllerContainerEx1: public MARTe::ReferenceContainer {
public:
    CLASS_REGISTER_DECLARATION()

	/**
	 * @brief NOOP.
	 */
	ControllerContainerEx1() {
    }

    virtual ~ControllerContainerEx1() {
        using namespace MARTe;
        REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
        		"pointing at %s [%s]. The Object will "
        		"be safely deleted.", GetName(), GetClassProperties()->GetName());
    }

    /**
     * @brief Calls AFunction on all of its elements
     */
    void Do() {
    	using namespace MARTe;
    	uint32 i;
    	uint32 size = Size();
    	for (i = 0u; i < size; i++) {
    		ReferenceT<ControllerEx1> r = Get(i);
    	    if (r.IsValid()) {
    	    	r->AFunction();
    	    }
    	}
    }

};
CLASS_REGISTER(ControllerContainerEx1, "")

}

int main(int argc, char **argv) {
    using namespace MARTe;
    using namespace MARTe2Tutorial;
    SetErrorProcessFunction(&ErrorProcessExampleFunction);

    CCString className1 = "ControllerEx1";
    CCString className2 = "PIDEx1";

    //Automatically generate a new object instance based on the class
    //name and on the correct Heap and with the template reference.
    ReferenceT<ControllerEx1> ref1(className1,
    		GlobalObjectsDatabase::Instance()->GetStandardHeap());
    if (ref1.IsValid()) {
        ref1->SetName("ControllerInstance1");
        REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
        		"created an instance of %s", className1.GetList());
    }
    ReferenceT<ControllerEx1> ref2(className2,
    		GlobalObjectsDatabase::Instance()->GetStandardHeap());
    if (ref2.IsValid()) {
        ref2->SetName("ControllerInstance2");
        REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
        		"created an instance of %s", className2.GetList());
    }
    //This mechanism also works with compatible subclasses PIDEx1->ControllerEx1
    ReferenceT<PIDEx1> ref3(className2,
    		GlobalObjectsDatabase::Instance()->GetStandardHeap());
    if (ref3.IsValid()) {
        ref3->SetName("PIDInstance1");
        REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
        		"created an instance of %s", className2.GetList());
    }

    ReferenceT<ControllerContainerEx1> controllerContainer("ControllerContainerEx1",
    		GlobalObjectsDatabase::Instance()->GetStandardHeap());
    if (controllerContainer.IsValid()) {
    	controllerContainer->SetName("Container");
    	controllerContainer->Insert(ref1);
    	controllerContainer->Insert(ref2);
    	controllerContainer->Insert(ref3);
    	controllerContainer->Do();
    }
    return 0;
}

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
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/**
 * @file ReferencesExample6.cpp
 * @brief Source file for class ReferencesExample6
 * @date 14/03/2018
 * @author Andre' Neto
 *
 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 * the Development of Fusion Energy ('Fusion for Energy').
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 *
 * @warning Unless required by applicable law or agreed to in writing, 
 * software distributed under the Licence is distributed on an "AS IS"
 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the Licence permissions and limitations under the Licence.

 * @details This source file contains the definition of all the methods for
 * the class ReferencesExample6 (public, protected, and private). Be aware that some
 * methods, such as those inline could be defined on the header file, instead.
 */

#define DLL_API

/*---------------------------------------------------------------------------*/
/*                         Standard header includes                          */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                         Project header includes                           */
/*---------------------------------------------------------------------------*/
#include "AdvancedErrorManagement.h"
#include "ClassRegistryDatabase.h"
#include "ErrorLoggerExample.h"
#include "Object.h"
#include "ObjectRegistryDatabase.h"
#include "Reference.h"
#include "ReferenceContainer.h"
#include "ReferenceT.h"
#include "StreamString.h"

/*---------------------------------------------------------------------------*/
/*                           Static definitions                              */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                           Method definitions                              */
/*---------------------------------------------------------------------------*/
namespace MARTe2Tutorial {

class ObjectEx1: public MARTe::Object {
public:
	CLASS_REGISTER_DECLARATION()

	/**
	 * @brief NOOP.
	 */
	ObjectEx1 () {
	}

	virtual ~ObjectEx1() {
		using namespace MARTe;
		if (GetName() != NULL) {
			REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
					"pointing at %s [%s]. The Object will "
					"be safely deleted.", GetName(), GetClassProperties()->GetName());
		}
	}

	void SetParent(MARTe::Reference ref) {
		parent = ref;
	}

	/**
	 * A private container. This should be avoided!
	 */
	MARTe::Reference parent;

};

CLASS_REGISTER(ObjectEx1, "")

/**
 * Does not implement the Purge method and as such a cyclic link will not be broken.
 */
class ContainerEx1: public MARTe::ReferenceContainer {
public:
	CLASS_REGISTER_DECLARATION()

	/**
	 * @brief NOOP.
	 */
	ContainerEx1 () {
	}

	virtual ~ContainerEx1() {
		using namespace MARTe;
		REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
				"pointing at %s [%s]. The Object will "
				"be safely deleted.", GetName(), GetClassProperties()->GetName());
	}

	void CreateLoop(MARTe::Reference aReferenceIn) {
		aReference.Insert(aReferenceIn);
	}

	MARTe::ReferenceContainer aReference;
};
CLASS_REGISTER(ContainerEx1, "")

/**
 * Implements the Purge method.
 */
class ContainerEx2: public ContainerEx1 {
public:
	CLASS_REGISTER_DECLARATION()

	/**
	 * @brief NOOP.
	 */
	ContainerEx2 () {
	}

	virtual ~ContainerEx2() {
		using namespace MARTe;
		REPORT_ERROR_STATIC(ErrorManagement::Information, "No more references "
				"pointing at %s [%s]. The Object will "
				"be safely deleted.", GetName(), GetClassProperties()->GetName());
	}

	/**
	 * Destroys an existent links
	 */
	virtual void Purge(MARTe::ReferenceContainer &purgeList) {
		using namespace MARTe;
		//Destroy the circular link
		aReference.Purge();
		//Alternative which would also destroy the link
		/*ReferenceT<ObjectEx1> ref = aReference.Get(0);
		if (ref.IsValid()) {
			//Set an invalid reference to break the link.
			ref->SetParent(Reference());
		}*/
		//Do not forget to call the parent implementation
		ReferenceContainer::Purge(purgeList);
	}

};
CLASS_REGISTER(ContainerEx2, "")

}

int main(int argc, char **argv) {
	using namespace MARTe;
	using namespace MARTe2Tutorial;
	SetErrorProcessFunction(&ErrorProcessExampleFunction);

	CCString className1 = "ObjectEx1";
	CCString className2 = "ContainerEx1";
	CCString className3 = "ContainerEx2";

	//Automatically generate a new object instance based on the class
	//name and on the correct Heap and with the template reference.
	ReferenceT<ObjectEx1> ref1(className1,
			GlobalObjectsDatabase::Instance()->GetStandardHeap());
	if (ref1.IsValid()) {
		ref1->SetName("ObjectExIntance1");
		REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
				"created an instance of %s", className1.GetList());
	}
	ReferenceT<ObjectEx1> ref2(className1,
			GlobalObjectsDatabase::Instance()->GetStandardHeap());
	if (ref2.IsValid()) {
		ref2->SetName("ObjectExIntance2");
		REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
				"created an instance of %s", className1.GetList());
	}
	ReferenceT<ContainerEx1> ref3(className2,
			GlobalObjectsDatabase::Instance()->GetStandardHeap());
	if (ref3.IsValid()) {
		ref3->SetName("ContainerExIntance1");
		ref3->Insert(ref1);
		//This will create a circular dependency
		ref3->CreateLoop(ref1);
		ref1->SetParent(ref3);
		REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
				"created an instance of %s", className2.GetList());

	}
	ReferenceT<ContainerEx2> ref4(className3,
			GlobalObjectsDatabase::Instance()->GetStandardHeap());
	if (ref4.IsValid()) {
		ref4->SetName("ContainerExIntance2");
		ref4->Insert(ref2);
		ref4->CreateLoop(ref2);
		ref2->SetParent(ref4);
		REPORT_ERROR_STATIC(ErrorManagement::Information, "Successfully "
				"created an instance of %s", className3.GetList());
	}

	if (ref3.IsValid()) {
		ref3->Purge();
	}
	if (ref4.IsValid()) {
		ref4->ReferenceContainer::Purge();
	}

	if (ref1.IsValid() && ref2.IsValid() && ref3.IsValid() && ref4.IsValid()) {
		REPORT_ERROR_STATIC(ErrorManagement::Information, "%s "
				"number of references: %d", ref1->GetName(),
				ref3->NumberOfReferences());
		REPORT_ERROR_STATIC(ErrorManagement::Information, "%s "
				"number of references: %d", ref2->GetName(),
				ref2->NumberOfReferences());
		REPORT_ERROR_STATIC(ErrorManagement::Information, "%s "
				"number of references: %d", ref3->GetName(),
				ref3->NumberOfReferences());
		REPORT_ERROR_STATIC(ErrorManagement::Information, "%s "
				"number of references: %d", ref4->GetName(),
				ref4->NumberOfReferences());

		if (ref1->NumberOfReferences() == 2u
				&& ref3->NumberOfReferences() == 2u) {
			REPORT_ERROR_STATIC(ErrorManagement::Warning,
					"As expected %s and %s have a circular dependency and thus cannot be destroyed."
					, ref1->GetName(), ref3->GetName());
		}
	}
	return 0;
}