Jump to content

How To Properly Cast Object to Child Class?


Recommended Posts

I have an application that contains the following objects:

Database.lvclass

Signal.lvclass

Diagnostic Signal.lvclass

Input Signal.lvclass

Output Signal.lvclass

A "Signal" object array is initially created, but it is possible for an attribute to be added at run-time to these signals which makes the object a more "specific" class (diagnostic/input/output). The class type is specified by an attribute that is set AFTER a signal object is created. The creation of the Signal Object contains a lot of accessors (around 20). Initially I was thinking I could "cast" the Signal Object to a more specific class that would copy the Signal Object data to the appropriate descendant Diganostic/Input/Output Object. However, this will return error:

Error 1448 occurred at To More Specific Class in CAN Signal.lvclass:Cast Signal To Type.vi

Possible reason(s):

LabVIEW: Bad type cast. LabVIEW cannot treat the run-time value of this LabVIEW class as an instance of the given LabVIEW class.

According to the information from here, this is not possible and the current procedure for creating a descendant from an ancestor is to use accessors for every possible data item up the chain.

However, a LAVA thread posted here seems to state otherwise, except it is an "old" lava post and the example cannot be downloaded!

1. What best procedure for creating a descendant from an ancestor? If more data items are added to the ancestor, is there a technique that will warn/errror if the descendant creator does not include?

I have attached my initial attempt that has caused the Error 1448. Any help would be appreciated!

post-4274-0-58420200-1290031329_thumb.pn

  • Like 1
Link to comment

Try this - I think this is what the deleted example does. The class controls and indicators are all parents. If you wire a child to the control indicated, then you'll get a instance of the child on the parent class indicator with the parent data members set. (LV2009 - may not be available in 8.x - I can't remember)

Mark

post-1322-0-00821900-1290036308_thumb.gi

Link to comment

A "Signal" object array is initially created

.....

Initially I was thinking I could "cast" the Signal Object to a more specific class

Do I understand it correct, that you first want to create a generic object and then in some cases transform it to a more specific type?

You can't just type cast it, you have to create a new Specific Object and copy the data over the new object.

Cheers,

Mikael

Link to comment

Do I understand it correct, that you first want to create a generic object and then in some cases transform it to a more specific type?

You can't just type cast it, you have to create a new Specific Object and copy the data over the new object.

Cheers,

Mikael

Here's an example of how to do what Mikael suggested

Wire the parent instance to the "Parent" and a new child constant to the "Parent class control (Wire child here)". Now, the data you initialized the parent instance with gets copied to the child instance. All you have to do at runtime is wire the correct child class constant in a case statement or such to this method and you can create a "deep copy" of the parent into a new child and dispose of the original parent. It behaves as if you cast the parent to a child, but you don't (because you can't - a cast of an instance implies you just change the type pointer (reference) to an object in memory. A cast from a child to parent is allowed because all of the parent's data is there to access. If you were allowed to cast a parent to a child the child's data members would be undefined and nowhere in memory. If you then tried to access the child's data members, you'd be pointing to some memory location that likely has been allocated to something else (or not allocated at all)). So you create new instances of the child with a copy of the parent's data so all data members of the child are defined in memory.

Mark

post-1322-0-81867400-1290085472_thumb.gi

  • Like 1
Link to comment

I can't say this is the best procedure, but it's how I've solved the problem in the past. Create a child class with the attributes you want. The parent data in the child class will all be set to the default values. Then copy the parent data from the existing parent object into the new child object's parent data. I've attached an example using 2010.

Thanks for the example. I completely understand that creating a manual "constructor" to "deep copy" the parent to child is possible. I see two issues with this approach:

1. For objects of large size and multiple inheritance, a lot of manual work will be required to create the CopyParentToChild method.

2. There is no error checking if new private data is added to any objects in the inheritance chain.

My main concern would be issue #2 stated above. It would be great if there was some method of "flagging" that I want to make sure ALL data from ancestors is copied. Any ideas on how this could be done?

  • Like 1
Link to comment

Thanks for the example. I completely understand that creating a manual "constructor" to "deep copy" the parent to child is possible. I see two issues with this approach:

1. For objects of large size and multiple inheritance, a lot of manual work will be required to create the CopyParentToChild method.

2. There is no error checking if new private data is added to any objects in the inheritance chain.

My main concern would be issue #2 stated above. It would be great if there was some method of "flagging" that I want to make sure ALL data from ancestors is copied. Any ideas on how this could be done?

First, LabVIEW does not support multiple inheritance (thank God!)- you can have an ancestor tree for a given subclass, but multiple inheritance implies that a subclass inherits from more than one distinct superclass. Secondly, you can minimize the work done making a deep copy by wrapping the parent class (and subclass) data members in a cluster and then accessing that cluster in the unbundle/bundle nodes. Typedef that cluster and include it in your class (subclass) library. Then, if you add/remove data to the cluster everything updates and the copy is still valid because the bundel/unbundle operates on the cluster. You will need one specific method for each subclass that looks a lot like my screenshot above that unbundles the data from the direct parent of that class and copies it into a new instance of the child class.

Mark

Link to comment
First, LabVIEW does not support multiple inheritance (thank God!)- you can have an ancestor tree for a given subclass, but multiple inheritance implies that a subclass inherits from more than one distinct superclass. Secondly, you can minimize the work done making a deep copy by wrapping the parent class (and subclass) data members in a cluster and then accessing that cluster in the unbundle/bundle nodes. Typedef that cluster and include it in your class (subclass) library. Then, if you add/remove data to the cluster everything updates and the copy is still valid because the bundel/unbundle operates on the cluster. You will need one specific method for each subclass that looks a lot like my screenshot above that unbundles the data from the direct parent of that class and copies it into a new instance of the child class.

It seems we were posting at the same time! After reading your most recent post, I attempted to create a typedef and use that as the Cluster of class private data, but LabVIEW was giving errors. Any reason a typedef cannot be used as it would simplify this deep copy process..

I also noticed that a class VI snippet will have broken wires after opening...

-Brian

post-4274-0-21321400-1290094319_thumb.pn

post-4274-0-77076900-1290094380_thumb.pn

Edited by brianafischer
Link to comment

Brian,

First, the VI snippet probably breaks because it gets de-coupled from its class library (the snippet probably claims to be part of the library but the library doesn't think so).

Second, here's an example project that demonstrates the technique I would use to have a single general purpose constructor that can create any subclass with the parent class data you define and also includes a single method that lets you create any new subclass instance from a parent instance and preserves the parents data ( a deep copy but looks a lot like type casting the parent to a child).

Mark

DeepCopyExample (LV2009).zip

Link to comment

1. For objects of large size and multiple inheritance, a lot of manual work will be required to create the CopyParentToChild method.

By 'multiple inheritance' I assume you mean multiplel levels of inheritance? If you're going to be downtyping through multiple levels of inheritance I'd take a hard look at your class relationships. Inheritance may not be the best way to accomplish what you're trying to do. In fact, since you have to create the signal object before knowing what kind of signal it is, I'd definitely look for an aggregation relationship instead of an inheritance relationship.

Perhaps you could add a SignalProperties class as a member of the Signal class that contains the 20 attributes you referred to. Then when you know your signal is a diagnostic signal, you create a new DiagnosticSignal object and insert the SignalProperties class.

2. There is no error checking if new private data is added to any objects in the inheritance chain.

As Mark said, typedefs are one way to address this, though I probably wouldn't use one. (Personal preference.) Each class is responsible for implementing it's own CopyData method to transfer data between different instances. If you're concerned that future developers might change the class data and forget to update the CopyData method, drop a comment on the class .ctl reminding them to update CopyData.

My main concern would be issue #2 stated above. It would be great if there was some method of "flagging" that I want to make sure ALL data from ancestors is copied. Any ideas on how this could be done?

Do you mean "all data from an ancestor" or "all data from all ancestors?" For the former, Mark's typedef suggestion, paying attention to changes, and unit testing can all help prevent errors. For the latter case, a string of CopyData methods from each of the ancestors clearly shows your intent and should be easy to verify by examination. (But like I said above, I'd look for a better way to accomplish your goal than forced downtyping. You code will be clearer and easier to maintain in the long run.)

Second, here's an example project...

Mark,

Why do you use the PRC prim in your copy method? The data copy from the source to the destination has already taken place, so by raising an error if the cast fails all you're doing is putting restrictions on how the copy method can be used. For example, the PRC prevents the user from copying parent data from a SubSubClass object to a SubClass object, or copying parent data between sibling classes. Did you intentionally implement that restriction, or is it to help make sure users are wiring the sub vi correctly? (Discovering the new use cases prompted me to relabel the terminals on my copy method to 'Source' and 'Destination' rather than 'Parent' and 'Child.')

post-7603-0-28673700-1290186925_thumb.pn

DowncastingObjects2.zip

  • Like 1
Link to comment
First, the VI snippet probably breaks because it gets de-coupled from its class library (the snippet probably claims to be part of the library but the library doesn't think so).

Snippets aren't VIs... they don't have memory of being part of a library or not. They're just nodes that join the VI that they are dropped on.

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.