bms Posted June 11, 2021 Share Posted June 11, 2021 Is it possible to nest classes in DF? class MINMAXCLASS local min local max endclass class WSCLASS local status MINMAXCLASS temperature MINMAXCLASS windspeed endclass global ws = new(WSCLASS) ws.temperature.max = 10 Quote Link to comment Share on other sites More sharing options...
AzeoTech Posted June 11, 2021 Share Posted June 11, 2021 Yes, but not in the way you are doing. DAQFactory isn't strictly typed and objects are referenced by normal numeric variables. As such they also aren't created when the enclosing class is created so you have to do it manually. Something like: class MINMAXCLASS local min local max endclass class WSCLASS local status local temperature local windspeed function init() temperature = new(CMinMaxClass) windspeed = new(CMinMaxClass) endfunction endclass global ws = new(wsclass) ws.init() ws.temperature.max = 10 Quote Link to comment Share on other sites More sharing options...
bms Posted June 15, 2021 Author Share Posted June 15, 2021 OK thanks. For clarity I'm assuming the leading C in CMinMaxClass is a typo? I've come across some curious behavior. This was actually working for me last week: class MINMAXCLASS local min local max endclass global min_max_t = new (MINMAXCLASS) class WSCLASS local status min_max_t temperature min_max_t windspeed endclass global ws = new(wsclass) ws.temperature.max = 10 But now gives an error. But if I do this: class MINMAXCLASS local min local max endclass class WSCLASS local status temperature = new (MINMAXCLASS) windspeed = new (MINMAXCLASS) endclass global ws = new(wsclass) ws.temperature.max = 10 Then change to the first code, it works with no error message. Am I just grabbing some garbage value that hasn't been collected yet? Quote Link to comment Share on other sites More sharing options...
AzeoTech Posted June 15, 2021 Share Posted June 15, 2021 Yes, sorry, I always prefix my classes with C. It is an MFC thing and old habit. As to the rest, just because it kind of works doesn't make it right. What is happening is that when you create a class, DAQFactory actually instantiates it, mainly just creating and initializing the member variables. When you then instantiate the class with new() it creates a copy of those variables. The thing is, it doesn't work with local variables that are initialized with new() because while x = y makes a copy of y if a scalar or string, x = y creates a copy of the reference to the object, not the object itself if y is an object. As such, you can't do "local temp = new(someClass)". For this same reason, if you modify a class and add a new member variable, you have to reinstantiate all the objects you created from the class as those objects won't have the copy of the new local, but instead will reference the internal object. Its all a subtle effect of how objects are handled in DAQFactory as a dynamic language. Your script, however, is incorrect in both examples. In the first, you try and type temperature and windspeed during declaration and as I mentioned before, there is no strict typing outside string and everything else. In the second, you omit the word "local". I'm not sure what will happen with the temperature and windspeed variables in this case. It is undefined, and even though you don't get an error message, it does not mean it is actually working. Quote Link to comment Share on other sites More sharing options...
bms Posted June 15, 2021 Author Share Posted June 15, 2021 Sorry the omitted local is a typo. I am still a little confused by what is happening behind the scenes. Am I right in saying when this runs: class WSCLASS local status local temperature = new (MINMAXCLASS) local windspeed = new (MINMAXCLASS) endclass Then temperature and windspeed hold pointers to MINMAXCLASS, not the objects themselves? Then when I do: global ws = new(wsclass) ws will correctly hold the object wsclass with a copy of the member variable "status"? But the nested members "min" and "max" don't actually exist as copies in ws? So when altering them: ws.temperature.max = 10 This is actually updating the memory that was allocated when the class was first defined, not memory belonging to the object ws? And therefore considered a runtime error with undefined behavior? Quote Link to comment Share on other sites More sharing options...
AzeoTech Posted June 18, 2021 Share Posted June 18, 2021 No. Not quite. And it is important to remember that as a dynamic language, i.e. one that allows changing of script (and the rest of the application) while the application is running, things work differently than one would expect from a true compiled language. When DAQFactory sees a class/endclass declaration it will actually instantiate the class (without calling any init functions), so that internally there will exist each of the member functions, along with each of the local variables along with their initialized values. When you then use new() to instantiate your own object, it copies all the variables along with those initial values to the new object. When you do myobject.mylocalvariable it gets the copied values. When you do myObject.myLocalFunction() it will call the the function that is in that internal object it instantiated. The functions aren't copied, but the local variables are. So, if you do new(minMaxClass) as an initial value for a local variable, you are instantiating a new minMaxClass and storing a reference to it IN THE INTERNAL OBJECT. The problem is that when you then do new(myClass) it will copy all the local variables. This is fine for regular values, since x = y does a copy of the value of y to x. So, changes in y don't affect x, and vice-versa. But, object variables are actually references, so w = z does not actually copy the object in z to w, but instead, copies the reference to the object stored in z to w. W and Z now point to the same object. So w.var and z.var are the same. So, in general you have to keep initialization simple, to scalar values or static arrays, so: local x = 3 local y = {3,4,5} If you want to instantiate objects, create an init() function that does it, as I showed before. On a side note, this same thing crops up when you add a local variable to a class and you have existing objects. So, if you have: class myClass local x = 3 endclass global myOb = new(myClass) global myOtherOb = new(myClass) And then you change myClass to: class myClass local x = 5 local y = 4 endclass Two important things happen: 1) x does not change to 5 in myOb (or myOtherOb) because the 3 has already been copied over, and more importantly: 2) myOb.y doesn't exist in myOb (or myOtherOb) because myOb was instantiated before you edited the class. So, when you reference myOb.y it won't find it in myOb and will instead look to its parent, the internal object associated with the class. That variable is read-only, so myOb.y will always return 4, and myOb.y = 7 won't do anything. Likewise, myOtherOb.y and myOb.y are the same and will show 4. In order to pick up the new variable you have to reinstantiate myOb and myOtherOb. So, in general, you can modify member functions of classes all you want and those changes will affect existing objects (once any running code exits the function), but if you add a member variable you will have to reinstantiate all existing objects. Quote Link to comment Share on other sites More sharing options...
bms Posted June 20, 2021 Author Share Posted June 20, 2021 Got it. Thanks for the detailed explanations; much appreciated. Quote Link to comment Share on other sites More sharing options...
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.
Note: Your post will require moderator approval before it will be visible.