Jump to content

Am I understanding the reason why I have to use Preserve RT class correctly


Recommended Posts

I have some config classes

Base class: Config

Child classes: XML Config, INI Config, CSV config, etc

In the base class I have a load and a save method which must be overriden by all child classes. To read the XML config I am using GXML which returns a variant then I use variant to data to convert to my XML Config class. But, when I wire this output to the dynamic dispatch terminal coming out of the VI, I get a broken run arrow with the error run-time type not propgated from dynamic input to dynamic output terminal. So, I fixed this by using preserve runtime class (see screenshot)

So my question is as follows:

1.Is the reason I have to use preserve run time class because if I had another class inheriting from XML config (say class ThisApplicationsXmlConfig) and it didn't have this method implemented, the parent method (i.e. the pictured method) would be called and the variant to data doesn't know the runtime class on the parent wire is actually a child class, so I have to tell it explicitly?

2. Side question: Does the output class terminal have to be dynamic dispatch or will this cause unexpected results? If I change the output from being dynamic dispatch to just recommended, how does it change the output?

Thanks.

post-15770-0-88442700-1345611536_thumb.p

Edited by for(imstuck)
Link to comment

I have not tested this, but I think you need the 'Preserve runtime class' is because for dynamic dispatching you need the same (not branched) class data between the input and output.

Sine you use variant to class you have a branch, by using the 'preserve runtime class' you instruct LabVIEW that it is indeed the same data.

Ton

Link to comment

My understanding is that according to the wire flow LV cannot (to 100%) guarantee that the object type on the wire is EXACTLY the same (not a child, not a parent) as the input but you're basically telling the compiler "Chill bro, I've got it under control".

If for any reason (sn error for example) the object you are changing the run-time type on IS different, you'll get a run-time error. The compiler should be telling you "Pfft, under control you say....".

I may have paraphrased some typical compiler jargon....

Shane.

  • Like 1
Link to comment

I might be missing something here but if I understand correctly then the obj xml config should be a class and in its cluster you should have a xml control.

Now, into the parser you pass the unbundled xml control from the class cluster and you do the same when you convert from variant to data.

Finally you need to bundle this xml back into the class.

This way the class should be fine going out back from the dynamic dispatch.

Link to comment

Finally you need to bundle this xml back into the class.

The XML isn't taken from or passed back into the class, it is a different representation OF the class. i.e. it's a serialisation / deserialisation of the entire class which is being performed.

I agree though that it should be OK, you just need tobe careful not to introduce nasty run-time bugs this way. It softens up the strict typing of classes just a little to let some nasty bugs in if you're not vigilant enough.

Shane.

Link to comment

I know that the preserve run time option is ok. I just think that if possible it should be avoided by the way I described that doesn't do any deserialization of the class.

However, I didn't go over the GXML tool so, again, I might be missing something.

Link to comment

I can see the benefits and drawbacks of both ways, so I will just have to make a decision at some point. For now I'll leave it as-is.

My follow up question is this. Why can't the compiler guarantee the classes are 100% the same? Is it because you could have a child class on the wire, but the variant to data doesn't recognize this? I'm just trying to understand what is happening in the background.

In this case, I know preserve RT class is safe because this VI is must override. So, the only object that can be on this wire is an XML Config object. Therefore, I *should* be able to guarantee this will be safe.

Link to comment

You need the preserve runtime class is needed to do exactly what the name implies: ensure the class of the value going along the in/out wire is preserved at run-time.

To do dynamic dispatching, you need to fulfill a contract that the type of value riding on the wire does not change as it goes from input to output. Also remember that potentially any class can ride along a given class wire so long as the value derives from whatever wire type you're working with.

When you use the Variant To Data primitive (or the similar To More Specific Class), you're deciding what class you want to test for based on the wire type in your block diagram. This decision is made when you edit your code and the VI compiles, it is a static test. Compare this with the Preserve Run-Time Class primitive which evaluates at run-time what the value type is riding on the wire. The run-time test is dynamic: the wire type in your block diagram hasn't changed, but the type of the value riding on that wire may have. If I supply a child class along a parent's wire, the run-time check will be aware of the difference and test accordingly.

How about an example? What happens if I have an XHTML Config class which inherits from your XML Config class, and I pass it to the VI you showed?

By using the Variant To Data, you've only guaranteed that the value you're returning is definitely an XML Config value. But I supplied a more specific XHTML Config value to your VI, it's not until you perform the dynamic run-time check with the Preserve Run-Time Class that you can be sure your XML Config wire has preserved the type across the entire VI.

Does that clear things up?

  • Like 1
Link to comment

I think mje pretty well summed it up, but the explanation I like the most is from AQ on this NI blog (https://decibel.ni.com/content/groups/large-labview-application-development/blog/2012/06/02/when-should-the-to-more-specific-or-preserve-tun-time-class-primitives-be-used-with-oop-in-labview) describing the difference . The post referenced is worth a read as well.

"...The "To More Specific" tests the incoming object vs the type of the wire at the middle terminal. The "Preserve Run-Time Class" tests the incoming object vs. the type of the object on the wire at the middle terminal. "

Link to comment

By using the Variant To Data, you've only guaranteed that the value you're returning is definitely an XML Config value. But I supplied a more specific XHTML Config value to your VI, it's not until you perform the dynamic run-time check with the Preserve Run-Time Class that you can be sure your XML Config wire has preserved the type across the entire VI.

Does that clear things up?

Yup, this is pretty much what I was getting at. I think I understood correctly but used improper terminology between object and class when stating "Is it because you could have a child class on the wire, but the variant to data doesn't recognize this". I should have said, "the actual object on the wire is not recognized by variant to data, only the wire type is recognized by variant to data"

In my brain it made sense; writing it out, not so much.

Edited by for(imstuck)
Link to comment

I am concerned whenever I see code like the above... people try to write "Load" as a dynamic dispatch function, but that doesn't work most of the time. It does work in some cases, and since I can't find any place where I have typed this up before, I'm going to list off some scenarios... see which one yours matches.

Situation A: There are no child classes to your XML Config class. If you are not creating any child classes, then you shouldn't have any of the VIs be dynamic dispatch. Turn it off and remove the need for the Preserve Run-Time Class function.

Situation B: The config file lists the name of which child class you want to create. In this case, you can't pass in the right object because you do not know it yet when you make the function call -- you haven't read the file yet. In this situation, you have to read the file, create an object of the right type and then *initialize its fields* ... that can be a dynamic dispatch function, but it is not replacing the entire object but rather filling in each field one by one by parsing the XML file. In this case, the Preserve Run-Time Class function will actually work against you because it will keep resetting your object back to the base class that it was when you originally made the function call.

Situation C: You have written a really intelligent XML-to-object parser in that subroutine that is always going to construct the correct child class of the class passed in in the original variant. In that case, this function works fine for all child classes and does not need to be dynamic dispatch at all and the Preserve Run-Time Class is not needed. Note: If you have a parent class of XML Config that is something like "Any File Config", you can then argue that you do need the Load to be dynamic dispatch since you're abstracting which file type is being read. If that's the case then you do need the code as written, but you should wire a constant to the To Variant node so that the conversion to variant will be constant folded and save yourself some performance overhead.

Situation D: This one works with your code. You do have child classes of XML Config. You are opening the config file and doing enough parsing to extract the name of the class so that you know which child class to create. You create the object and then you read the XML file again using this function. It requires you to do double parsing of part of the XML file (once to get the class name and once to actually parse the data) or it requires you to record the class name twice (once for your pre-reader and once in the actual XML of the object). This solution requires that every class *must* override the parent implementation so that the type wired into the Variant To Data function is the exact right *wire* type for every single child class.

  • Like 1
Link to comment

Version 1: Would a more suitable OOP approach not be to write some private class methods to pick the values out of the variant and write them to the original input class, thus not breaking the object wire between input and output? On error, values are not retained, no dynamic dispatch problems.

I find the trust level that the right input object has the right filename to output exactly the right output object type a bit too high.

Or perhaps version 2: Read in the object from file, try to convert it to the same as the input object (so that you have the input object AND the loaded object) but then transfer the data using private class functions (Accessors) instead of simply outputting this loaded class. This too prevents breakage in the dynamic dispatch wire path. In the case of an error, the input object is simply passed out as the output object, no dynamic dispatch problems.

Shane.

Link to comment

Situation C: You have written a really intelligent XML-to-object parser in that subroutine that is always going to construct the correct child class of the class passed in in the original variant. In that case, this function works fine for all child classes and does not need to be dynamic dispatch at all and the Preserve Run-Time Class is not needed. Note: If you have a parent class of XML Config that is something like "Any File Config", you can then argue that you do need the Load to be dynamic dispatch since you're abstracting which file type is being read. If that's the case then you do need the code as written, but you should wire a constant to the To Variant node so that the conversion to variant will be constant folded and save yourself some performance overhead.

Situation D: This one works with your code. You do have child classes of XML Config. You are opening the config file and doing enough parsing to extract the name of the class so that you know which child class to create. You create the object and then you read the XML file again using this function. It requires you to do double parsing of part of the XML file (once to get the class name and once to actually parse the data) or it requires you to record the class name twice (once for your pre-reader and once in the actual XML of the object). This solution requires that every class *must* override the parent implementation so that the type wired into the Variant To Data function is the exact right *wire* type for every single child class.

I think I currently have something similar to situation C in the *note* and maybe situation D. The load method is set to must override in my base class "config". So, I can ensure that every class that inherits from this class will have this load method, so there is no chance the parent class's load VI will be called with a child object on the wire. Is this the point you were getting at?

I need to digest situation D a little more before I comment on that one in much detail. Are you stating with this situation you could have a parent method which extracts the type of the class in the config file (possibly a child class) then reads the file and casts the variant to the specific data type? I'm afraid I'm not fully understanding this situation and the need for the implementation or the benefits/draw backs as compared to Situation C.

Edited by for(imstuck)
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.