Recommended Posts

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

 

Share this post


Link to post
Share on other sites

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

 

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites

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.

 

Share this post


Link to post
Share on other sites

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? 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

Got it. Thanks for the detailed explanations; much appreciated.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now