LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Basic Poymorphism in LabVIEW

Solved!
Go to solution

All of my work in LabVIEW has been procedural but I would like to begin implementing more OO Design Patterns to my software. My question is composed of two (2) parts:

  1. Asking for a basic C++ analog
  2. Overcoming duplicate VI file names

1. Asking for a basic C++ analog.

The following is a basic demonstration of the Strategy Pattern in C++ (I apologize for verbosity, but it really does drive home my question):

// << Interface >>
class FrequencyReader {
    public:
        virtual ~FrequencyReader()     = default;
        virtual double readFrequency() = 0;
};
class AnalogFrequencyReader : public FrequencyReader {
    public:
        virtual ~AnalogFrequencyReader() = default;
        double readFrequency() override { // code in here // }; 
};
class DigitalFrequencyReader : public FrequencyReader{
    public:
        virtual ~DigitalFrequencyReader() = default;
        double readFrequency() override { // code in here //}; 
};
// << Interface >>
class VoltageReader {
    public:
        virtual ~VoltageReader()     = default;
        virtual double readVoltage() = 0;
};
class AnalogVoltageReader : public VoltageReader {
    public:
        virtual ~AnalogVoltageReader() = default;
        double readVoltage() override { // code in here // }; 
};
class DigitalVoltageReader : public VoltageReader {
    public:
        virtual ~DigitalVoltageReader() = default;
        virtual double readVoltage() override { // code in here // }; 
};

// STRATEGY PATTERN "Client" INTERFACE
// <<Interface>>
class iDevice {
    protected:
        FrequencyReader* fReader;
        VoltageReader*   vReader;
        // PROTECTED CTOR TO PREVENT BASE CLASS CONTRUCTION
        iDevice(FrequencyReader* fr, VoltageReader* vr) : 
            fReader(fr), vReader(vr)
        {}
    public:
        virtual ~iDevice() { // delete pointers // };
        virtual double getFrequency()                            = 0;
        virtual double getVoltage()                              = 0;
};
class Device1 : public iDevice {
    public:
        virtual ~Device1() = default;
        Device1() : iDevice(new AnalogFrequencyReader, new AnalogVoltageReader) 
            {}
        double getFrequency() override {
            return fReader->readFrequency();
        }
        double getVoltage() override {
            return vReader->readVoltage();
        }
};
class Device2 : public iDevice {
    public:
        virtual ~Device2() = default;
        Device2() : iDevice(new DigitalFrequencyReader, new DigitalVoltageReader) 
            {}
        double getFrequency() override {
            return fReader->readFrequency();
        }
        double getVoltage() override {
            return vReader->readVoltage();
        }
};

 The point of this is that, let's say we get a new DMM, all we have to do is provide new implementations for VoltageReader and FrequencyReader via some factory functionality, and the rest of the code will be untouched.

 

double frequency(iDevice* device) {
  return device->readFrequency();
}
double voltage(iDevice* device) {
  return device->readVoltage();
}

 

So... I've been playing with classes in LabVIEW, have read some documentation and watched this video series (Future Proof Your Code! Introduction to Object Oriented Programming in LabVIEW), but am still struggling with basic Interface / Implementation concepts.

I understand that by selecting new "VI from Dynamic Dispatch Template", you create somewhat of a virtual function analog. I understand how to change the inheritance structure, but am unclear as to how to make the data within the parent class protected instead of private.

 

2. Overcoming duplicate filenames

When I choose to create a "New VI for Override" in a child class, it reproduces the name of the virtual function of the parent. The problem is that I now how two VI's with the exact same name. Certainly, I am doing something wrong with this.

 

Thank you in advance for taking the time to read this. Any advice is appreciated.

 

0 Kudos
Message 1 of 5
(1,708 Views)
Solution
Accepted by topic author george_ford

@george_ford wrote:

I understand that by selecting new "VI from Dynamic Dispatch Template", you create somewhat of a virtual function analog. I understand how to change the inheritance structure, but am unclear as to how to make the data within the parent class protected instead of private.


Class data in LabVIEW is *always* private. No exceptions. Other classes must use accessor VI's to get to parent class data. The accessors themselves can be made protected, but getting the data (via an Unbundle terminal) is always private.

 

You may be overthinking just a bit. I'd ignore LV Interfaces for now (while you're learning) and just do regular classes. You'll have parent/virtual classes with "Read Voltage.vi" in which nothing is actually done; it simply defines the connector pane and name of the function. Same as a virtual method in C++. (Of course there is nothing preventing this function from doing anything, and in fact it's often very useful- use the "Call Parent Method" node to force a child VI to call its parent implementation. For "Read Voltage" the parent probably doesn't need to actually do anything, but it can!)

 

Create your virtual methods with the Dynamic Dispatch template and save them. Then go to your child class, right click the class name, and pick New- VI for Override. This will create a child VI with only a Call Parent Method function. By default this function simply calls its parent implementation, but you can delete the Call Parent Method function if your parent doesn't do anything, or even if *just* that class doesn't need to call the parent method. It's up to you.

 


@george_ford wrote:

2. Overcoming duplicate filenames

When I choose to create a "New VI for Override" in a child class, it reproduces the name of the virtual function of the parent. The problem is that I now how two VI's with the exact same name. Certainly, I am doing something wrong with this.


Nope, you're doing the right thing. In fact, the files must have the same name. You just put them in separate folders. This is a good idea anyway, as each class should really have its own folder. Unique folders handle Windows namespacing, and project namespacing is handled by the files being part of a class. In other words, from the LV Project's PoV, the actual names are ParentClass::Update.vi and ChildClass::Child.vi.

 

When developing a program, you'll use the Factory Pattern (or something as simple as a case structure) to select a specific Child class, but all of your "business logic" should use the Parent class's VI's. The wire will look like the Parent class, but will contain data of a Child class. Dynamic Dispatch VI's will use the specific Child's implementation, despite the icon on the block diagram showing the Parent class's wire.

 

 

 

Message 2 of 5
(1,684 Views)

@BertMcMahan wrote:


Class data in LabVIEW is *always* private. No exceptions. Other classes must use accessor VI's to get to parent class data. The accessors themselves can be made protected, but getting the data (via an Unbundle terminal) is always private.

Thank you for the quick reply.

Would you therefore say, as a general rule, if someone is going to implement a Base Class Interface, that the Base Class should always provide Protected getter / setter functions?

 


@BertMcMahan wrote:

Nope, you're doing the right thing. In fact, the files must have the same name. You just put them in separate folders. This is a good idea anyway, as each class should really have its own folder. Unique folders handle Windows namespacing, and project namespacing is handled by the files being part of a class. In other words, from the LV Project's PoV, the actual names are ParentClass::Update.vi and ChildClass::Child.vi.

 



 Thanks. That's what I was doing to remedy that. I appreciate it.

0 Kudos
Message 3 of 5
(1,674 Views)

@george_ford wrote:

Would you therefore say, as a general rule, if someone is going to implement a Base Class Interface, that the Base Class should always provide Protected getter / setter functions?


 It must for any data that you want to access from outside of the class. Whether or not they are protected depends on the specific requirements you have. There is no general rule.  Parent class VI's can be called on the wire of any descendent class, so unless the child is changing the functionality, you don't need the child VI.

Message 4 of 5
(1,646 Views)

@george_ford wrote:

Would you therefore say, as a general rule, if someone is going to implement a Base Class Interface, that the Base Class should always provide Protected getter / setter functions?

No, not as a general rule. Make them protected if they need to be; otherwise, don't worry about it.

 

For example, a DMM class might have a "Channel name" property that's stored in the Parent class. There's no reason for this data to be Protected scope, as the user of the function will be the one to name it- not a child class. In fact, I'd go as far as to say as a general rule, make Public accessor VI's unless you have a reason to make it Protected. In my (admittedly limited) experience, data in the Parent class is almost always basic stuff settable by the user (like Channel name, scale factors, etc). It's not always, of course, but usually.

 

Generally speaking, each class handles its own private class data, so you actually *don't* want the Child class to be the only ones calling getters and setters. (Getters are of course fine).

Message 5 of 5
(1,585 Views)