Object Orientation with CS
Prior to LabVIEW version 8.0, object orientated techniques were not available in native LabVIEW. Therefore, CS
uses/implements an object oriented add-on for LabVIEW. In an early state, CS
was based on a third party toolkit, ObjectVIEW. However, we decided to implement our own object oriented approach within the CS
framework. This was motivated by the need for a better performance and simplicity. The basis of object orientation within CS
is described in the following.
Remark: Starting with version 8.0, LabVIEW provides native object oriented programming (LVOOP). However, migrating CS
to use LVOOP would require major changes within CS
and is not envisaged in the near future.
- No tool is required for inheritance and object orientation.
- An instance of a class (an object) is created by calling the "new" operator.
- An instance of a class is destroyed by calling the "delete" operator.
- Parent classes of a class "MyClass" are defined by calling the constructor(s) of the parent class(es) in the constructor of "MyClass" and be calling the desctructor(s) of the parent class(es) in the destructor of "MyClass".
- Multiple inheritance is allowed. However, one object will only have only one "instance" for each of its super classes. Example: If MyClass inherits from the classes BaseProcess and BaseGUI, MyClass will inherit all properties of BaseProcess as well as BaseGUI. However, BaseProcess and BaseGUI are child classes of the CSObj class - only one set of attribute data of the CSObj class is being created for MyClass.
- All resources required for a class are allocated in the constructor of a class and released in the destructor of a class. Resources include attribute data as well as active code like loops etc.
- Directory structure:
- All classes must be below a directory name "CSClass".
- All methods, definitions ... of a class "CLASSNAME" must be located below a directory "CLASSNAME".
- All methods must follow the name convention CLASSNAME.MethodName.vi.
- A thread is active code like a loop.
- A thread is NOT an object but can be part of an object.
- A thread is created and started by the operator CSSystem._startThread.vi.
- A thread is stopped and destroyed by the operator CSSystem._stopThread.vi.
- The code for a thread must be provided in the form of a reentrant VI using the option "Preallocate clone for each instance". If the thread is part of a class it must provide a control named "object ref". The value of this control will be set to the reference of the object to which the thread belongs.
- An object should be created
- by using the "CreateObject" (or "LoadProcess") event of the SuperProc object
- via the "new operator" CSSystem._new.vi.
- An object should be destroyed
- by using the "DestroyObject" (or "UnloadProcess") event of the SuperProc object
- via the "delete operator" CSSystem._delete.vi.
- One should not create an object of class CLASSNAME by calling the CLASSNAME.constructor directly.
- One should not destroy an object of class CLASSNAME by calling the CLASSNAME.destructor directly.
- Like in C++, it is the responsibility of the developer to program proper constructor and destructor code.
- Active code is called a thread. If a thread belongs to an object, it is created (destroyed) in the constructor (destructor) method of a class.
Guidelines for development
- Don't use too many levels of inheritance
- Don't use wrappers - they are a source of inconsistency and use resources.
- Typically, classes inherit from the CSCore.BaseProcess, CSCore.BaseSM or CSCore.BaseGUI classes. For very basic things, inherit from the CSCore.CSObj class. Of course, you may choose other classes as parent classes as well.
- Design your class carefully before starting to implement it.
- All classes need the methods
- Classname.constructor (a good place to do some things after an object is created).
- Classname.destructor (a good place to do some things before an object is destroyed)
- If the parent (or grand parent) class is BaseProcess, you have to provide the methods
- ClassName.ProcCases (put everything here that is triggered by events) and
- ClassName.ProcPeriodic (put everything here that is executed periodically).
Comment 1: For overloading a method of the parent class you may define a event with identical name in the ClassName.ProcEvents of your class and call the overloaded method from ClassName.ProcCases.
Comment 2: The method ParentClassName.ProcCases should be called in the default case of the method ClassName.ProcCases. Do not enter code directly in methods like Classname.ProcCases. Instead, create own methods that act on the attributes and call the methods from Classname.ProcCases. You can then call your methods via events ( a "call") or by direct method calls.
Comment 3: If you would like to use the method ParentClassName.ProcPeriodic you have to call this explicitly in the method ClassName.ProcPeriodic.
- If the parent (or grand parent) class is BaseSM, you have to provide the method ClassName.ProcState. It is recommended to call the method ParentClassName.ProcState in the default case of ClassName.ProcState.
- If the parent (or grand paent) class is BaseGUI, you have to provide a reentrant VI using the option "Preallocate clone for each instance" that serves as a GUI. Typically, the reentrant VI for children of BaseGUI is named ClassName.panel.vi; then, there is no need to specify the name for this reentrant VI (without ClassName) when creating the object.
- Use the Classname.i attribute data as the data of your class. Don't use local or global variables.
- 16 Mar 2007