Shop OBEX P1 Docs P2 Docs Learn Events
Declaring new objects — Parallax Forums

Declaring new objects

David A.David A. Posts: 2
edited 2008-08-21 02:53 in Propeller 1
Hi All,

I'm a beginner spin coder, so I need some help. I'm trying to create a LinkedList object
which would allow a user to store a dynamically sized list of objects or variables (could be
wrapped if necessary). My questions are, is it possible to create a pointer to a generic object
(the type of which is unknown)? Are nulls ever encountered in spin and if so, how are they handled?
Also, is it possible to declare a new instance of an object within a Pub or Pri method?

For example:
VAR
   ?Pointer/Object? currentNode
OBJ
   headerNode: "LinkedListNode"
PUB init
   currentNode := headerNode
PUB add(object) | nextNode
[b]
   currentNode := ?new? "LinkedListNode"
[/b]
   nextNode.setContents(object)
   currentNode.setNext(nextNode)




(BTW: I'm coming from a Java perspective, so if there are any comparisons you can make, that would
be great).

Thanks,
David

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2008-08-20 03:05
    No, there is no way to create a generic object in Spin. You could create a linked list node object that simply contains a pointer to an arbitrary piece of memory, but the linked list node object would then deal with arbitrary pointers which is a specific case.

    You can't declare an object within a method. They're all global to the object. All parameters and local variables for a method are longs. You can declare arrays of longs as local variables, but that's the only exception.

    Spin is not a general object-oriented language, particularly it doesn't have dynamic types of any kind.
  • scanlimescanlime Posts: 106
    edited 2008-08-20 05:16
    It seems to me that there are actually three distinct issues within your question:

    1. Generic objects

    No, Spin doesn't provide any way to do inheritance, function pointers, virtual methods, etc. There are some dirty tricks that let you kinda sorta implement object interfaces, but I won't go there. This is kind of a shortcoming in the language, but just think of it as motivation to try different design strategies [noparse]:)[/noparse]

    2. Linked lists

    Linked lists are pretty easy to do in either Spin or Assembly. Since Spin doesn't give you any compile-time abstractions, you can't really create a generic linked list object- you have to think lower level than that. One technique I'm fond of is to adopt a memory layout convention that I use in all my "objects". For example, I could plan to use the first word of memory in each "object" as a pointer to the next object in my linked list. Now I can always traverse a linked list in the same way, no matter what kind of object is in it:

    nextObject := WORD[noparse][[/noparse]thisObject]
    


    3. Dynamic memory allocation

    Using the "new" operator and dynamically sizing a list implies that you also want to use a dynamic amount of memory. This isn't a built-in feature of the Propeller, or of any CPU. It's up to some runtime library to define a memory heap, and the algorithms for dynamically allocating memory from it. There's a heap object on the object exchange, but it's actually relatively rare that you actually need dynamic memory allocation in an embedded system like a Propeller project. In fact, allocating all of your memory statically forces you to think ahead about the practical limitations in your system. How long can a line of input actually be? How much buffer space do you actually want to use for your serial port? etc. When you plan your memory usage ahead of time, there are far fewer failure modes to deal with in your finished system.

    Hopefully that answered your question [noparse]:)[/noparse]

    --Micah
  • David A.David A. Posts: 2
    edited 2008-08-20 23:30
    Thanks very much, this makes some sort of sense. It seems strange that Spin can't create new objects, but I suppose that's simply something to be worked out. Thanks for all of your help!,

    David A
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2008-08-21 02:53
    Can new objects be declared during a Spin program's execution? Sure! But you have to work at it, and the notation is a little different.

    First, however, we have to define what we mean by an "object". An object is really nothing more than a collection of data and the methods that operate upon it. In Spin, the object-particular data are the VARs declared in the object (actually, "object class") itself, and its methods are those of its routines declared as PUBlic. Each statically-declared instance of an object is assigned a separate set of VARiables, the methods and DAT area being allocated only once.

    So, if we want to create new objects dynamically, all we really have to do is come up with a way to allocate the object's variables dynamically with a new method, and recycle them back to the pool with a destroy method. (Automatic garbage collection is pretty much out of the question here.) The simplest, but not very efficient, way to do this would be for each Spin object to manage its own repository of memory in DAT space, sufficient to accommodate as many dynamic objects as might be instantiated at runtime. Better, of course, would be a universal dynamic memory allocator, which could service several different object classes at once.

    The idea is that, when an object's new method is called, it allocates the necessary memory and returns a pointer (i.e. the address of the memory block's first element), initializing elements in that block as necessary. One thing a new method cannot do in Spin, however, is "bless" the pointer into the object class so that the notation pointer.method(parameter1, ..., parameterN) can be used. Further method calls will have to be of the form classname.method(pointer, parameter1, ... , parameterN).

    In the object itself, the first parameter of any PUBlic method will then be the dynamic object pointer, normally referred to as self, viz:

    CON
    
      X = 0
      Y = 2
    
    PUB new : self
    
      self := allocate(1) 'Allocate one long, returning the address of the DAT block allocated.
      long(self) := 0  'Initialize X and Y (words) to zero.
      'return self (automatic, since self is the return variable).
    
    PUB destroy(self)
    
      deallocate(self) 'Return memory block to pool.
    
    PUB length(self) 'Compute cartesion distance.
    
      return ^^(word[noparse][[/noparse]self + X] * word[noparse][[/noparse]self + X] + word[noparse][[/noparse]self + Y] * word[noparse][[/noparse]self + Y])
    
    
    


    The use of named CONstant displacements helps to make the code more readable. You have to be careful, though, to mind the alignment of word and long displacements. You also have to be careful not to call methods with an object reference that ponts to an object in a different class. Just as a pointer is not "blessed" into an object class, it is also not "condemned" to fail when used with a foreign class method. Finally, object classes defined in this manner should have no VAR section. All variables global to the class need to be defined in a DAT block.

    -Phil

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Still some PropSTICK Kit bare PCBs left!

    Post Edited (Phil Pilgrim (PhiPi)) : 8/21/2008 3:01:11 AM GMT
Sign In or Register to comment.