Jump to content

When Should the 'To More Specific' or 'Preserve Run-Time Class' Primitives be Used with OOP in LabVIEW?


Recommended Posts

I think the thing that confuses people the most about these two primitives is the run-time behavior. You stated the proper behavior in your blog comment, but the distinction is at best very subtle the way you put it. Furthermore the help really distinguishes them by use-cases, not their underlying behavior which I think would be more helpful.

To More Specific tests against the compile-time wire type, whereas the preserve run-time class tests against the actual run-time type of the object on the wire which might not be the same as the wire type.

One is essentially a static type of cast where you're always testing against a known type, and the other is a dynamic cast which evaluates the type at run-time. However I hesitate to use the static/dynamic language because it carries with it carries with it some legacy baggage from C++.

  • Like 1
Link to comment

LabVIEW 2011 help that is.

I Slightly modified the language from the help file to make it clearer to myself.

""Preserve Run-Time Class Details

Use this function in VIs that meet the following criteria:

*The VI accepts a LabVIEW class as an input and returns the same LabVIEW class as an output.

*You want to call the VI as a subVI.

*You want to guarantee that the output class type is the same as the input class type (ex: child in -> child out)

Normally, when you wire a child class to a parent class input on a SubVI LabVIEW will automatically downcast the output class to child.

SubVIs like these do not require you to use this function on their block diagram.

However, sometimes it is impossible for LabVIEW to verify class type preservation in a subVI.

Such a situation could occur if your class is being converted to a reference, then operated upon, then reconverted back to a class inside the sub vi.

Because LabVIEW cannot guarantee at edit time that the class will be preserved you get a broken wire outside the sub VI.

In these cases, if you know that the class type does not change across the subVI, you can use this function in the subVI

to downcast the class immediately before returning it to the caller.

Because this function always returns an object of the same type as target object, LabVIEW recognizes that it can downcast the returned class to that type. Therefore, this function notifies LabVIEW that it is safe to downcast the class output of the subVI node to match the input class type.""

Link to comment
  • 2 years later...

Just to briefly revive an old thread, I still do not really fully understand the difference between To More Specific Class and Preserve Run-Time Class

 

can somebody check my understanding here for this particular scenario

  • I have a class hierarchy, say parent class A and child classes B and C.
  • Actual class is only determined at run-time, so my wire is of class A
  • Under certain conditions I wish to run a method that is only implemented in class B so I should use To More Specific Class, rather than preserve run-time class

Is this correct? This is pretty much how I have always done it in the past, I thought it would be worth re-visiting incase I am not quite using things correctly.

Link to comment

"Preserve Run-Time Class" will CHANGE the type of your object to match that of the selector (The RUN-TIME type of the object is guaranteed to match) whereas "To more specific" will leave it unchanged or return an error (and when it's successful, the EDIT-TIME type changes to match the selector).

 

If the input types of your "Preserve Run-Time Class" do not match exactlym the output object will be different than the input object.  This can be very dangerous if you have any references / resources inititalised in the incoming object.  What I don't know is how LV retains / removes certain fields of the objects if a type change is required within a tree of inheritance or whether it just returns a default object (I think it's a default object).

 

I only use "èReserve Run-Time Class" within either DD functiona or functions which accept a static Object input but outputs the same type.  By wiring the output to "Preserve Run-Time Class" (and knowing that my code should ALWAYS return the same type as the input) LV then autoadapts the output type of the Sub-VI to match the input, even if the VI connectors are not DD.

 

In your example, you would most definitely use "to more specific" rather than "Preserve run-time class".

 

Shane.


If your object is a visitor to NI Week:

 

Lets say the inheritance hierarchy is

NI Week Visitor -> NI Week Presenter -> Jeff K

 

Right?  Jeff K is a presenter and a visitor, but clearly not all visitors are Jeff K.

 

Using "To more Specific" on a Presenter with "Jeff K" as a selector will either allow you access to the Jeff K functionality on the SAME person or it will fail because the person just looked like him but isn't him and we all get to have a laugh.

If you use "Preserve Run-Time Class" and the guy isn't actually Jeff K, the function will actually clone Jeff K for you to make sure that the object returned actually IS Jeff K, but because memories and skills are not cloneable, he's pretty much useless to you int hat state.  On the other hand, using "Preserve Run-Time Class" on Jeff K (who's presenting himself as a visitor only) ends up being similar to "To more specific Class" as it doesn't actually change the object.

 

So the function "Preserve Run-Time Class" is to be used only when the TYPE of the object being forced is more important than the data contained within.

  • Like 1
Link to comment

I don't really see any other use case for my code at least.  I find it useful when I input an object to a generic function, do something to it and then return it.  I as the programmer know the object does not change, but LV can sometimes not know this explicitly so here "Preserve Run-Time class" prevents all objects exiting the sub-VI not being automatically cast (edit-time type) to a more generic class.

Link to comment

The behavior of the primitive is that if the cast succeeds, the object is passed through with no effect. If the cast fails, you get the default value of whatever run-time type is wired to the center terminal.

 

I don't really see any other use case for my code at least.  I find it useful when I input an object to a generic function, do something to it and then return it.  I as the programmer know the object does not change, but LV can sometimes not know this explicitly so here "Preserve Run-Time class" prevents all objects exiting the sub-VI not being automatically cast (edit-time type) to a more generic class.

 

This is really the point of the primitive-- being able to create generic methods where by using the primitive you are telling LabVIEW, "The output of this VI will always be the same type as the input." This is done automatically with dynamic dispatch paired terminals, but there are cases where you wish to do this with other terminals.

 

Think for example how many of the LabVIEW VIs have a default value input terminal and a corresponding output terminal that somehow automatically adapts to the same type as wired to the input. File I/O, and variant attributes are two that come to mind which I use daily, there are many others. In object oriented programming, the preserve run-time class primitive is how you would achieve this behavior if you wanted to write such a VI.

 

The important thing to remember when dealing with this is run-time object type is not the same as wire type on your block diagram. A wire can carry any object type that inherits from whatever type you're wiring. You don't even need to know the type of classes that can come in ahead of time.

 

To More Specific Class tests against the wire type. The class it tests against does not change, it is set in stone as soon as you wire up that terminal.

 

Preserve Run-Time Class tests against the run-time object type of the data on the wire. When it runs it determines the class that is wired up to the center terminal and evaluates if the input object is of the same class.

Link to comment

I am getting there... slowly... sometimes I think my understanding of something is like a really strong permanent magnet that resists all attempts to change it, but once it is finally done it is done for good  :P . At the moment my understanding is resisting change!

 

Can anybody furnish me with a simple example of when Preserve Run-Time Class is recommended/necessary?

Link to comment

Something posted above makes me less confident about my use, but I have a set of objects which store configuration information and a set of objects which present a UI for that configuration. However, I don't want the config objects to know about the UI objects *and* this all has to be in a plugin system. I want it so be {some subset of N}:{some subset of N}. That is, editor A may support configurations 1 and 2, but configuration 2 may also be used by editor B.

 

So what I did was I made sure the editors know what classes they can support and use preserve runtime class to test every available configuration object (1, 2, 3) against the configuration objects supported by every editor (A, B, C). This seems to successfully let me know if (unknown config object) is supported by Editor A, B, or C. I don't know if there is a better way to determine class hierarchies in the runtime environment, but this is what I came up with.

Link to comment

My general experience with Preserve Run-Time Class is that you'll know when you need it, because you'll code something that should work and realize that the class types don't match. As an example, I posted an image in this thread (http://lavag.org/topic/18377-decoupling-message-based-systems-that-communicate-across-a-network/#entry110228) showing the need for Preserve Run-Time Class, and the code for it is here: http://lavag.org/topic/16696-ah-yes-another-oo-architecture-question/#entry102428. I have a Datatype class, and an Action class. I want to perform Action on some Datatype. We can't do dynamic dispatch on two terminals at once, so there are two levels of dynamic dispatch - one on the Datatype, and inside that, a second one on Action. At the top level, we can see that we pass a specific action into the Do VI, but the Action (or Visitor) input to Do is necessarily static dispatch on the parent type, so the output wire is also of the parent type. Preserve Run-Time Class lets us tell the compiler that the output really is the same as the input.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.