Jason H Posted August 17, 2011 Report Share Posted August 17, 2011 I'm new to OOP, there may be a common method to solve my problem that I'm not aware of. The problem is this: I have a parent class "CNC table". It contains private data that all CNC tables have, for example, the minimum and maximum ranges for x, y, and z axes. I have have child classes which represent specific CNC tables, say different models. They control the hardware, and when I say "move to point" the proper subvi will be dynamically dispatched to control the hardware. The problem is that each child must have its own unique values for the x, y, and z ranges because different models of table will have different ranges. When I run the parent's "is point in range" function (which is called inside of the child's overridden "move to point" function), I want to read in the child's defaulted values for those specific private data members and compare against those. Optimally, I'd like to override the defaults of those data fields, and require all children to override them. If there was a constructor, I could just read in the defaults off of the child's front panel and then populate the parent's fields, which would be a little more work. However, from what I can tell neither of those are possible. So how can I make sure the parent's private data fields are set appropriately to what the child's defaults are? Quote Link to comment
jgcode Posted August 17, 2011 Report Share Posted August 17, 2011 Optimally, I'd like to override the defaults of those data fields, and require all children to override them. If there was a constructor, I could just read in the defaults off of the child's front panel and then populate the parent's fields, which would be a little more work. However, from what I can tell neither of those are possible. So how can I make sure the parent's private data fields are set appropriately to what the child's defaults are? Sure you can - check out the video Create Constructor From Template here, that is the style I like to use. Just set the parent accessors in that VI with the default data (from constants etc...) then call this VI instead of dropping of e.g. a Class Constant on the BD. Cheers -JG Quote Link to comment
Jason H Posted August 17, 2011 Author Report Share Posted August 17, 2011 (edited) Thanks jgcode. I'm trying to wrap my head around what you're saying here. From what I can tell, national instruments doesn't have any type of constructor built into LVOOP. What you are saying is that to essentially create one and work around that, I can simply create a method inside the class where the object is created that does the initialization stuff. Then when I want to use that object in a program I just plop down the constructor.vi on the block diagram and use it's output terminal as the object instance. I don't put the bare object constant on the block diagram as is typically done. Unless someone here can tell me that this isn't the best way to accomplish what I'm trying to do, I'll try it out and see if it works. Unfortunately I don't think there is a way to "force" someone to use constructor.vi on the block diagram instead of the object constant, so the programmer will have to keep that detail in mind. Also, there is no way to "force" those certain data fields to be initialized in the constructors of each child that I can see. Edited August 17, 2011 by jasonh_ Quote Link to comment
Michael Aivaliotis Posted August 17, 2011 Report Share Posted August 17, 2011 You can set the default child data in the class data control. So then that data will be used when placing the object constant. Of course this data can only be set at edit time. But I assume this is what you want. Quote Link to comment
jgcode Posted August 17, 2011 Report Share Posted August 17, 2011 You can set the default child data in the class data control. So then that data will be used when placing the object constant. Of course this data can only be set at edit time. But I assume this is what you want. From what I read, the OP wants to override parent data (which is not available in the child by LVOOP design). From what I can tell, national instruments doesn't have any type of constructor built into LVOOP. What you are saying is that to essentially create one and work around that, I can simply create a method inside the class where the object is created that does the initialization stuff. Then when I want to use that object in a program I just plop down the constructor.vi on the block diagram and use it's output terminal as the object instance. I don't put the bare object constant on the block diagram as is typically done... ...Unfortunately I don't think there is a way to "force" someone to use constructor.vi on the block diagram instead of the object constant, so the programmer will have to keep that detail in mind. Also, there is no way to "force" those certain data fields to be initialized in the constructors of each child that I can see. Yes that is correct. You may or may want to call it constructor (as LVOOP has no constructor) the constructor is always the Class.ctl if you will. You cannot force this operation and restrict the use of the Class.ctl as you have to use it subVI boundaries, as part of datatypes, as default value etc... Unless someone here can tell me that this isn't the best way to accomplish what I'm trying to do, I'll try it out and see if it works. IMHO I don't think this is the best approach to learning - you should try stuff anyways and form your own decision, then provide feedback back to LAVA to continue the learning cycle Quote Link to comment
PaulL Posted August 17, 2011 Report Share Posted August 17, 2011 I think I am probably saying pretty much the same thing but in different words. We usually put an object constant on a diagram, but where appropriate the first thing we do is call a MyObject.init() method. This method initializes the object values where required, usually reading the values from some sort of configuration file. Further, we never explicitly set the default values in an object control. We don't rely on the default values in any way, since there is no easy way to enforce these don't change and really no indication that they did. Therefore we explicitly set values as we need to in an initialization method (otherwise we treat them as undefined). This is much easier to read and far less risky. It is true that we don't have a way to force someone to call this VI, but everyone on our team knows to do so. (It's pretty obvious where and when to use this in our case.) I realize that it might make sense to do this as part of a constructor, but LabVIEW doesn't support that directly (although jgcode demonstrated a nice way to do just that). We do instantiation plus initialization by convention to accomplish the same goal. I will say jgcode's constructor method approach has the advantage over mine in that one could have the convention that one must only instantiate objects via a constructor method. Quote Link to comment
Samapico Posted August 17, 2011 Report Share Posted August 17, 2011 Another option could be to have VI's such as 'GetRangeXYZ', put it in the parent class with the property 'Require descendant classes to override this dynamic dispatch VI'. Then you create these override VI's in child classes so that they simply output constants. Quote Link to comment
jgcode Posted August 18, 2011 Report Share Posted August 18, 2011 Another option could be to have VI's such as 'GetRangeXYZ', put it in the parent class with the property 'Require descendant classes to override this dynamic dispatch VI'. Then you create these override VI's in child classes so that they simply output constants. This seems like a lot of work in comparison - do you use this often and prefer it over the constructor/init method? Are you able to comment on pros/cons? Cheers -JG Quote Link to comment
Samapico Posted August 18, 2011 Report Share Posted August 18, 2011 This seems like a lot of work in comparison - do you use this often and prefer it over the constructor/init method? Are you able to comment on pros/cons? Cheers -JG I never really used this, to be honest, I usually use a constructor method. An advantage would be that you can't "forget" to use the constructor method on your object, and you're forced to create the override, or it will simply not compile. I'm guessing that if you have many independent properties, it would be a lot of work, but in jasonh_'s case, if the X/Y/Z limits can be given by a single VI, it could be worth it. It's just one more VI to create (just like you'd need to create a constructor anyway). And it's really just right-click->override... -> change some constants -> save Quote Link to comment
Jason H Posted August 18, 2011 Author Report Share Posted August 18, 2011 I've created a constructor.vi and am forcing all children to override this vi. Inside the parent's private data cluster, I have another cluster called "data for override" and a single accessor can write the entire cluster. That cluster is also a type-def, which is used on the front panel of the child as well. So all I have to do in the constructor is take that cluster and write it to the parent's cluster. Quote Link to comment
jgcode Posted August 18, 2011 Report Share Posted August 18, 2011 I've created a constructor.vi and am forcing all children to override this vi. Inside the parent's private data cluster, I have another cluster called "data for override" and a single accessor can write the entire cluster. That cluster is also a type-def, which is used on the front panel of the child as well. So all I have to do in the constructor is take that cluster and write it to the parent's cluster. FWIW the constructor method I present in the video link does not use dynamic input/outputs. Using type-defs in Classes is a whole 'nother thread. However, using a cluster is not the best interface - you should use separate accessors methods for each property IMHO. Quote Link to comment
PaulL Posted August 18, 2011 Report Share Posted August 18, 2011 I thought a little more about this and realized I missed what I consider to be an important point. Another reason I don't use default values to set information is that in case the values need to change, I don't want to recompile my code. I put this information in configuration files (XML configuration files, by the way) so that if the values change I only have to update the configuration files, not the code. Paul Quote Link to comment
Jason H Posted August 18, 2011 Author Report Share Posted August 18, 2011 FWIW the constructor method I present in the video link does not use dynamic input/outputs. Is there a specific reason for that? Using type-defs in Classes is a whole 'nother thread. However, using a cluster is not the best interface - you should use separate accessors methods for each property IMHO. I'm going to make that accessor vi protected, so that only the children can see it. That way, nothing outside of the object can ever access the entire cluster, but the children still have full access. The only reason the parent even has this data is as a placeholder to be overridden. Also, I think that accessor is only ever going to be used once - in the constructors. I can't think of a reason to use it anywhere else. Quote Link to comment
Samapico Posted August 18, 2011 Report Share Posted August 18, 2011 Is there a specific reason for that? I haven't watched that video, but I didn't use dynamic dispatch inputs either when I created my constructor methods because they force you to use the same inputs for every child class... what if you have a specific property in one of these classes, and you want to initialize its value in its constructor? Also, if you don't use dynamic dispatch, you can use the convention to never place a lvclass object constant on the block diagram, but you have to use constructor methods that output an initialized object. Quote Link to comment
jgcode Posted August 19, 2011 Report Share Posted August 19, 2011 ...because they force you to use the same inputs for every child class... what if you have a specific property in one of these classes, and you want to initialize its value in its constructor? Yes - you can have different constructor CP's for each Class. The only things I can currently think of are: You must have a unique name for each Constructor VI for Classes with children Using a Constructor in a pure LVOOP Plug-in architecture may require a Wrapper VI so the API (i.e. Interface) is standardised across all public methods. Quote Link to comment
lvb Posted August 19, 2011 Report Share Posted August 19, 2011 It would be nice if LabVIEW had a way to force a constructor. Does anyone know why it is not an option to prevent a class object from being dropped on a block diagram? It would be excellent to have "callbacks" in classes such as OnCreate, but that would seem to break data flow. This may seem like a hack, but what about putting a boolean flag in the parent private data that is set only by constructor VIs? This way you could determine if the user called the constructor in the related methods... Quote Link to comment
jgcode Posted August 19, 2011 Report Share Posted August 19, 2011 It would be nice if LabVIEW had a way to force a constructor. Does anyone know why it is not an option to prevent a class object from being dropped on a block diagram? As mentioned above: You cannot force this operation and restrict the use of the Class.ctl as you have to use it subVI boundaries, as part of datatypes, as default value etc... I am sure AQ has commented on this before? This may seem like a hack, but what about putting a boolean flag in the parent private data that is set only by constructor VIs? This way you could determine if the user called the constructor in the related methods... What would you do with the flag? You would have to run precheck code in every public method and handle exceptions. That sounds like a lot of work. Quote Link to comment
Jason H Posted August 21, 2011 Author Report Share Posted August 21, 2011 It would be nice if LabVIEW had a way to force a constructor. Does anyone know why it is not an option to prevent a class object from being dropped on a block diagram? I was going to post this suggestion on the Idea Exchange. I think it would be a good way to effectively give us constructors, without all the headaches of a real constructor that AQ has posted about. Quote Link to comment
Daklu Posted August 22, 2011 Report Share Posted August 22, 2011 I'll throw in my $.02 and agree with what the others have said. I build creator methods for all my classes similar to what JG does. It seems to have become a LVOOP idiom and fairly standard practice, though I will still use a class constant for type information, such as with the downcast function. A couple notes on my convention: - I call them "creators" as opposed to "constructors" to maintain differentiation. Objects are "constructed" at some point in time during execution, it's just not exposed to us as developers. I think of it as happening when the RTE executes a class constant on the block diagram (thought that isn't strictly correct in all situations.) - My naming convention is "Create MyClass." If a class has multiple constructors using different types of arguments I'll describe that in the method names. (i.e. "Create MyClass(XmlString)" or "Create MyClass(Path)".) - Generally, I don't put a class input terminal on my creators. The idea of a creator is that I'm generating a new object, not modifying an existing one, so a class input terminal doesn't make sense. (The one exception is with plug-ins where the application needs to create an unknown child object from disk.) Unfortunately I don't think there is a way to "force" someone to use constructor.vi on the block diagram instead of the object constant... Nope, there isn't. I struggled with that for a while before giving up and deciding there's only so much hand-holding I can do as a class designer. Also, there is no way to "force" those certain data fields to be initialized in the constructors of each child that I can see. Nope. If a developer is going to create a child class, it's his responsibility to understand the parent well enough to create the child correctly. It would be nice if LabVIEW had a way to force a constructor. Does anyone know why it is not an option to prevent a class object from being dropped on a block diagram? To expand a bit on what Jon said, G doesn't allow us to specify a type without also specifying data. There are several functions that require type information inputs but ignore the actual data. What do we do? We create a data constant and wire it up. What you're asking for (and what I've asked for in the past) is to be able to freely use the class constant as a type definition while preventing it from being used as a data source. Conceptually it is possible, but it changes some of the fundamental concepts of the language and is inconsistent with current usage. It would be excellent to have "callbacks" in classes such as OnCreate, but that would seem to break data flow. See here. This may seem like a hack, but what about putting a boolean flag in the parent private data that is set only by constructor VIs? This way you could determine if the user called the constructor in the related methods... I've done that before. Usually the additional cost in development time isn't worth the payoff. Once people get used to the idea of using creators instead of class constants the problem that addresses goes away. Quote Link to comment
jgcode Posted August 23, 2011 Report Share Posted August 23, 2011 The idea of a creator is that I'm generating a new object, not modifying an existing one, so a class input terminal doesn't make sense. (The one exception is with plug-ins where the application needs to create an unknown child object from disk.) The reason for the Class input terminal is so that a Child may call the Parent's Constructor as e.g. part of it's Constructor. It is not a Dynamic Dispatch input, but rather a Static input however, as it is LVOOP a Child will be passed out of the Parent's Constructor method if one is wired in. Quote Link to comment
Daklu Posted August 24, 2011 Report Share Posted August 24, 2011 The reason for the Class input terminal is so that a Child may call the Parent's Constructor as e.g. part of it's Constructor. I should have said it doesn't make sense to me. I do think it is a stylistic difference more than a practical difference. Typically I accomplish that by simply creating protected accessors and let the child class developer be responsible for correct implementation. They can use the accessors in Child.Create to set up the parent data. (Lots of times I want more control over the parent data than I would get from a public creator method.) If there's a lot of code that would require duplication in each child class sometimes I'll create a protected Initialize method that wraps it up for convenience. Quote Link to comment
jgcode Posted August 25, 2011 Report Share Posted August 25, 2011 I should have said it doesn't make sense to me. I do think it is a stylistic difference more than a practical difference. Typically I accomplish that by simply creating protected accessors and let the child class developer be responsible for correct implementation. They can use the accessors in Child.Create to set up the parent data. (Lots of times I want more control over the parent data than I would get from a public creator method.) If there's a lot of code that would require duplication in each child class sometimes I'll create a protected Initialize method that wraps it up for convenience. I build creator methods for all my classes similar to what JG does... ...I call them "creators" as opposed to "constructors" to maintain differentiation. But wouldn't you want to reuse your parent constructors in your child constructors (most of the time)? Having that Class Input allows that. Quote Link to comment
Aristos Queue Posted August 25, 2011 Report Share Posted August 25, 2011 Does anyone know why it is not an option to prevent a class object from being dropped on a block diagram? Because it wouldn't solve anything.Instead of a constant, a user might drop a control. The data may come from an Unflatten From String instantiating the default value of the class. There's not really a good way to bottleneck and say "always call this constructor here". Worse, LV data is live from the moment a control is dropped to be copied/pasted around to controls and constants, which means any constructor function would need to be invoked as soon as the control drops on a panel. But the problem is that you might be editing the class right then... there's no guarantee that such a constructor is runnable at any given moment. Originally, I had this idea that we could run the "default value constructor.vi" when the class gets reserved for running, since there's only one copy of the default value and we could update it once. That idea fell apart because RT needs an independent copy of the default value for every allocation. That left me with the idea of flagging every memory allocation that hasn't yet been properly constructed yet and run it on those memory locations whenever the class gets reserved. That option seems viable, although I am worried about the "I just edited the default value constructor VI, now LV needs to update all the memory locations to the new value" problem. Probably not an issue, but I haven't spent any time working on the default constructor issue in the last couple years... the times when someone has asked for this feature has been very rare compared to other requests. It's really not been a priority to find a solution. 1 Quote Link to comment
jgcode Posted August 25, 2011 Report Share Posted August 25, 2011 I am sure AQ has commented on this before? If not he has now Thanks AQ for clearing that up. Quote Link to comment
Daklu Posted August 25, 2011 Report Share Posted August 25, 2011 But wouldn't you want to reuse your parent constructors in your child constructors (most of the time)? Nope... almost never in fact. I'm not constructing a parent object, I'm constructing a child object, so calling a parent constructor seems out of place to me and makes the code more confusing (IMO.) The child constructor may or may not (often not) have the same creation requirements on the parent class data fields. Unit testing is one example. Some of my class creators obtain a "private" queue for use internally. To test the class I might create a Test Spy child class that monitors the queue and records what messages are sent on it. The construction requirements for the parent and child are: Parent - Obtain a queue and don't expose it to the rest of the application. This prevents other application code from doing something it shouldn't, like flush the queue or enqueue a message to the front. Child - Obtain a queue and inject it into the parent class data field so I can monitor what messages each method sends. With protected accessors to the queue I have a lot more flexibility in what I want a child class to do than I do if I was forced to use the parent class' public constructor. Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.