From 04:00 PM CDT – 08:00 PM CDT (09:00 PM UTC – 01:00 AM UTC) Tuesday, April 16, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW Idea Exchange

cancel
Showing results for 
Search instead for 
Did you mean: 
0 Kudos
Timmar

Class override methods, allow override vis Object Reference.

Before I make My suggestion I would like to say that Objects in labview have been my savior. My code complexity and reusability have improved ten fold.  It has allowed me to integrate templates including glyphs and artwork into base classes which greatly speeds coding.

In recent times, LV has allowed auto-coersion of references to objects to a lower, common class - yey!.

 

I would like to see polymorphism/function override support "ByRef" dynamic dispatch as well.

 

Class Byref.png

 

Right now I have to create a base class byRef function to access the object and then call a polymorphic VI to execute it.

This forces the creation of an extra vi per function call when I use objects byref.

iTm - Senior Systems Engineer
uses: LABVIEW 2012 SP1 x86 on Windows 7 x64. cFP, cRIO, PXI-RT
10 Comments
tst
Knight of NI Knight of NI
Knight of NI

I don't really understand your idea. Is this the same as this idea - http://forums.ni.com/t5/LabVIEW-Idea-Exchange/Have-Dynamic-Dispatching-terminals-accept-Data-Value-r... ? If not, how is it different?


___________________
Try to take over the world!
srdfrn
NI Employee (retired)
Status changed to: Duplicate
Timmar
Active Participant

THIS IS NOT A DUPLICATE (of the link anyway)

 

The link speaks of automaticaly converting the by reference to the actual object and back.

This is an interesting concept, butas a power user I understand why it is not done this way.

Too many possibilities.

 

My request is to allow dynamic dispatch based on the class of the reference.

 

I use a number of different data types when acquiring data - Analogue, digital, comms etc, which are accessed from mululiple locations byref.

I would like to be able to call the "clear data" [DAq Data] base class method by ref and have the dynamic method detect that that it is a digital and call the appropriate override method from the [Daq Digital] class.

iTm - Senior Systems Engineer
uses: LABVIEW 2012 SP1 x86 on Windows 7 x64. cFP, cRIO, PXI-RT
Timmar
Active Participant

Class polymorp by refClass polymorph by ref.png

iTm - Senior Systems Engineer
uses: LABVIEW 2012 SP1 x86 on Windows 7 x64. cFP, cRIO, PXI-RT
AristosQueue (NI)
NI Employee (retired)

This idea is a duplicate of the link because the operations are exactly equivalent despite the difference in syntax.

 

> My request is to allow dynamic dispatch based on the class of the reference.

 

To do this, the reference must be locked (to prevent parallel destruction), the inner data must be accessed (to figure out what type of object is actually in there) and then dispatched. The only way to dispatch is on the actual data inside the reference where there's a real type.

Timmar
Active Participant

Aristos,

I don't agree, Does this mean that each time i convert a reference to a more specific/more generic type i am locking/unlocking the object?

 

Why not use the same mechsnism to achieve the "Auto Change" example, this is static code determined at compile.

 

As to the dynamic method, what is wrong with: lock,get type, un lock - typecast, call dynamic "by ref" method.

It has got to be better than the overheads of calling a sub.vi

 

Is the problem here efficiency? This functionality would save me hours of coding and reduce overall complexity and make it a bit more intuitive.

 

 

iTm - Senior Systems Engineer
uses: LABVIEW 2012 SP1 x86 on Windows 7 x64. cFP, cRIO, PXI-RT
AristosQueue (NI)
NI Employee (retired)

> Does this mean that each time i convert a reference to a more specific/more generic type i am locking/unlocking the object?

 

More specific type: Yes.

More generic type: No.

 

The To More Generic primitive is always a no-op. It exists strictly to eliminate coercion dots for people who don't like seeing them on their diagrams, even though coercion dots caused by upcasting are also no-ops. The wire type is sufficient to know when a more generic cast is safe. You can always take a reference of Dog and make it a reference to Animal regardless of whether the Dog inside is actually a LabradorDog or a PitBullDog. All Dogs are Animals.

 

The To More Specific cast requires us to know the actual data being carried. You can't take a reference to an Animal and change it to a reference to a Dog without being sure that there's actually a Dog in there and not a Cat. Not all Animals are Dogs.

 

> Why not use the same mechsnism to achieve the "Auto Change" example, this is static code determined at compile.

 

If you want that kind of static changing, build a polymorphic VI containing the VIs where each VI has a different reference type. But that kind of static analysis is not what your idea asked for.

 

> As to the dynamic method, what is wrong with: lock,get type, un lock - typecast, call dynamic "by ref" method.

> It has got to be better than the overheads of calling a sub.vi

 

No, it doesn't have to be better. And it wouldn't be. 🙂 You'd be acquiring the lock twice, once to make the function call and once to actually do the operation inside the subVI. Way better is to acquire the lock once at an Inplace Element Structure and then do a dynamic dispatch on the object therein.  There's also the problem of the reference being closed. Where should LV report the error when you try to make one of these calls on an invalid refnum? Not all subVIs have error out terminals.

Timmar
Active Participant

I just created a polymorphic vi to do this job, it took me 15 minutes for 3 classes and I will have to register a new one for each function for each class.....

10 functions x 15 classes = 150 actions...

 

If it was built into the class library, I would right click - create class for override, select the function.

The rest would be done automaticaly for me, icon, terminals etc.

If I add another class later, I don't have to remember to add/register all the functions to the polymorphic vi,

 

My understanding of this forum is to make Labview easier to use without causing confusion.  I can't see how this doesn't fit the criteria. You have suggested a workaround, which would take more time the improvement.

 

For the dynamic selection - As far as being ineffecient, sure, i understand, but there a plenty of labview vi's out there that have have unnecessary fat in them.

I have already trimmed down a number of waveform manipulation VI's, removing certain functionality that I don't use to make them more efficient for my specific application.

 

If a ref lock-unlock takes so many rescources, perhaps we should go back to using functional global variables instead of OOP.

I have already noticed that writing data to an object (not by ref) is 16 times slower than in standard code. 

OOP users tend to be after more readble, maintainable, easy to write, easy to use code and don't mind the minor performance hit to do it.

 

Perhaps you can find a way of coding it so it doesn't need to lock it - it is a read operation after all if it no longer exists, return a null ref and assume parent type, the downstream code should be able to handle this anyway.  By the same logic, index array should have error terminal(s) for when you request a vaue out of range.

 

This is why you don't see C++ very often in the embedded world.

 

I am not the first to ask for this and I am sure as LV GOOP catches on I won't be the last.

 

iTm - Senior Systems Engineer
uses: LABVIEW 2012 SP1 x86 on Windows 7 x64. cFP, cRIO, PXI-RT
AristosQueue (NI)
NI Employee (retired)

> If it was built into the class library, I would right click - create class for override, select the function.

 

I think my problem is you keep asking for an "override", which inherently means "decide at runtime", so I kept trying to figure out how this would work at runtime. I wasn't even thinking about an edit time issue -- I mentioned the poly VIs as an aside, not realizing that was directly related to what you were intending with your overall idea.

 

Let's keep the runtime and the compile time firmly separate going forward. So, let me address the dynamic runtime questions first because I think we can finish off that discussion, then I'll address the compile time idea.

 

Runtime case:

> If a ref lock-unlock takes so many rescources,

 

My core objection has nothing to do with the resources a reference takes. It has to do with timing -- which programmer (the API developer or the API user) is consciously thinking about the duration of the lock? Are they designing the API to be properly atomic? Are they even worrying about the possibility of interleving functions? The vast majority of parallel programmers do not even stop to think about these questions when dealing with references. The current model at least encourages some block locking, and at best encourages a programmer to at least contemplate the issue. To me, this is an excellent thing. Anything that LV does in this area to guess has as much of a chance of being right as of being wrong, and all we've done is weaken the probability of a programmer catching the mistake.

 

Having said that, as small as the resource overhead is, I don't like the replication of that overhead. Thread synchronization can easily overwhelm the advantages of a parallel system, so a programmer should be working to minimize that in code, not relying on language features that bloat it. And bloating is the only option LabVIEW could compile to choose because the locks have to be released before the subVI call.

 

> I have already noticed that writing data to an object (not by ref) is 16 times slower than in standard code.

 

By "standard code", I'm guessing you mean a cluster. I can build the same benchmark. I can also build you one that shows that writing to the object is faster than writing to a cluster. And one where they're tied. For a "read-modify-write" sequence, for example, it makes a major difference whether you do the unbundle and bundle through accessor VIs or directly on a block diagram. There are many other variations. There's nothing inherently slower about classes v clusters, but the two types do have different specialties, so it matters what you're actually doing with the data.

 

> OP users tend to be after more readble, maintainable, easy to write, easy to use code and don't mind the minor performance hit to do it.

> This is why you don't see C++ very often in the embedded world.

 

If Stroustrop was dead, you'd have him spinning in his grave. 🙂 C++ is all about high performance OO code, and the kernel authors who use it care A LOT about performance.  C++ has really spread over the last five years, even into deep levels of code. I can't cite numbers, but based on my own annecdotal experience, I'd be very surprised if C++ wasn't used more commonly than plain C even at the embedded level.

 

Now, let's flip back to the main idea...

 

Compile Time:

Ok. Fresh from the top...

 

Your idea: You want to be able to create classes X, Y and Z (where X is the parent of Y and Y is the parent of Z) and then create a VI Q in each of those classes that takes in the DVR of X or Y or Z (respectively) and selects, at the moment the node is wired in the diagram, which implementation to pick. This is functionally equivalent to building a polyVI that contains X:Q, Y:Q and Z:Q, but doesn't require the maintenance of that polyVI.

 

Assuming I have the idea correct, let me say this: What you are asking for sounds like a reasonable request, but it poses some open questions to me. Let's continue talking in this thread about those questions and aim to, at some point, post a new idea to the exchange that covers those issues.

 

First question is the creation of X:Q, Y:Q and Z:Q. LabVIEW does not allow a child class to have a VI with the same name as a VI in the parent class unless those VIs have a dynamic dispatch relationship. The reason for this restriction is discussed in The Decisions Behind The Design. Shadowing a parent function has been the source of too many bugs in other programming languages and there's no way I want to see LV tainted with that.

 

Given that, this idea requires one of two things: either we introduce a new terminal attribute (for example, a new pop up menu item for This Terminal Is... >> Static Dispatch Input), or we apply some sort of named attribute to the child VIs that defines the relationship between the child VI and the parent VI (for example, class X could define Q.vi. Then class Y could define R.vi and tag it with "This VI is a polymorphic variation of R.vi"). I'm not sure which approach would be better.

 

Second question: How common is it to need this behavior only in a single hierarchy? In other words, if I was using the poly VI workaround, I could put VIs into that polyVI that were from completely different inheritance trees. With a terminal attribute scheme, the polymorphism is limited to a single hierarchy. With a named attribute scheme, it can be open across hierarchies.

 

Third question: Is there any reason to limit the rest of the connector pane to matching? I have two classes right now that I'm playing with that both have an Initialize function. Those don't have the same connector pane at all, so one is named Initialize Class A.vi and the other is Initialize Class B.vi. If I put an Initialize.vi in the common parent class, and tied both of these to it, now I have a quick poly VI behavior, but it's value is if the conpane is allowed to vary. Are there any use cases that you have where variation would be a bad thing?

 

Fourth question: Given the above, is there any reason to limit this feature to LV classes? Are we really talking about easily extensible polymorphic VIs? In other words, is there something so inherent about class types and the inheritance relationship that makes it worth building a narrowly-tailored language feature for polymorphism? Or should we instead be aiming to find a way to say "See that polymorphic VI over there? I'm part of it any time I'm in memory, whether the polyVI knows it or not" -- and, maybe even more ideal, to do all of that without even having the polyVI exist at all.

 

Fifth question: The poly VI workaround... you mentioned how long it took to write that polyVI. It was slow. But when you had it finished, was it the functionality that you wanted? If yes, then perhaps we should be looking at this from an editor perspective (how to make polyVIs build faster) instead of from a language perspective (how to define a new relationship between VIs that has never existed before). Thoughts?

 

It's an interesting design puzzle.

Timmar
Active Participant

Aristos, Thanks for the thoughtful response.

 

My applications, both past and present are large applications with multiple developers of varying skills.

There is significant gains in having a rigid framework with flexability for the specific modules.

 

I am managing data acquisition and control from multiple sources using multiple data types, eg, Analogue Waveform, Digital Events, Async. Communications data, all from different sources.

My architecures have:

1. A "Driver" Parent Class for Acquiring the Signals (Chunking Methods),

2. A "Data" Parent Class for storing, processing analysing and evaluating the data.

 

The functional blocks are almost all identical, Create, init, Arm, Start, Stop, Exit, Clear, Evaluate, configure.. lending themselves nicely to Override methods of polymorphism, My code need only operate on the parent class.

There are a couple of unavoidable exectptions such as Data Set, Data Get, wich are called from their Drivers, or end consumers which are operating with specific data type anyway.

The catch for me is that I have multiple consumers (and 1 producer) requiring access to the data and it's methods.  This creates the need for a by reference architecture.

The native object Unlocking-Locking is a perfect fit for my architecture.

 

On initialisation I create the objects, Convert to By Ref, Bundle them into a control thread, and share the references around to the consumer modules that utilise the data.

This way I can Configure, Arm, Start, stop, exit these threads as a group.  It was a godsend when LV introduced the ability to change object refs to a more generic class.

Unfortunately, I have to wrapper these references, Inplace convert to an object before executing the override method.

Theoreticaly, I may not even want to operate on the objects, For example, My comms module doesn't have an Arm function, so i can save time by not Unlock-Locking the ref.

 

To use a C Parable,

Standard coding calls functions by name eg: "uint MyGetData()", A VI Call equivalence.

More structured programming uses callbacks "void MyGetData(MyStoreDataFn) Which as the equivalency of a VI called byref. The MyGetData Function doesn't need to know much about the callback funtion, It's function prototype should be common and I believe fit's well with my request.

 

As far as embedded C vs C++ is concerned, The use of dynamic C++ Objects is notorious for fragmenting RAM.  The embedded sytems that I have worked on need to operate continuosly for more than 10 years with limited rescources. Predictable resouce usage is important. There are even a set of rules to code by.

 

I can say hand on my heart that these features would improve the readability and ongoing development of my code

 

Kind regards,

iTm - Senior Systems Engineer
uses: LABVIEW 2012 SP1 x86 on Windows 7 x64. cFP, cRIO, PXI-RT