For Programmers: Free Programming Magazines  


Home > Archive > Smalltalk > August 2006 > How would I do this in Smalltalk?









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author How would I do this in Smalltalk?
Jeff M.

2006-08-14, 7:01 pm

Let me describe something that I do on a regular basis in C++ to solve
a real problem. This is really 2 problems in one, but 1 of them is
pretty trivial to solve, and the other is what I'm stumped on.
Hopefully someone can show me a new trick. :-)

I need a list of global objects that is searchable at runtime, but
created at compile or launch-time. For example, consider a game that
when the player hits the ~ key a console pops up and allows them to
type in commands to change the level he's playing on, enter God mode,
etc. These would be done through console commands.

Now, in C++, I just have a class called ConsoleCommand, that the
constructor would add the new global instance to a linked list of
available commands. These would be simplified with a macro, so that I
could just do something like this in code:

CONSOLE_COMMAND(load_level)
{
// load_level code here
}

This macro would create a global variable (something like
__CC_load_level) with the name "load_level", that when executed would
call a function (__CC_func_load_level) with the body defined above.
Simple enough.

The above I can do with Smalltalk pretty easily. I can define a
ConsoleCommand class, with a class variable that is a linked list of
commands, and calling #new would create the new command and add it to
the list. Simple enough.

However, in Smalltalk I don't have source files, so unlike C++, I can't
create a ton of ConsoleCommand objects strewn throughout the codebase
easily. I could have some #init method in another class that created
them all, but that function would turn out to be pretty nasty rather
quickly.

What I think would be more elegant is to have each instance console
command be a singleton subclass of ConsoleCommand. But what I can't
figure out how to do is iterate over all the subclasses of a class or
even make a true singleton objecct in Smalltalk (which I'm sure is
trivial, I just haven't figured it out yet). If the singleton version
of each command could register itself with the parent, then this would
be simple again.

Any pointers appreciated. Thanks!

Jeff M.

Stefan Schmiedl

2006-08-14, 7:01 pm

On Mon, 14 Aug 2006 08:58:11 -0700, Jeff M. wrote:

> I need a list of global objects that is searchable at runtime, but
> created at compile or launch-time.


You could tweak the compiler to automatically do some stuff for you.

> However, in Smalltalk I don't have source files, so unlike C++, I can't
> create a ton of ConsoleCommand objects strewn throughout the codebase
> easily. I could have some #init method in another class that created
> them all, but that function would turn out to be pretty nasty rather
> quickly.


If you're running Dolphin, take a look at the methods called
"publishedAspects". They do something similar to what I understand you
want.


> What I think would be more elegant is to have each instance console
> command be a singleton subclass of ConsoleCommand. But what I can't
> figure out how to do is iterate over all the subclasses of a class or


Object subclasses inspect

HTH
s.
Jeff M.

2006-08-14, 7:01 pm


Stefan Schmiedl wrote:
> On Mon, 14 Aug 2006 08:58:11 -0700, Jeff M. wrote:
>
>
> Object subclasses inspect
>


Ah. That is simple. :-)

Thanks!

Jeff M.

Isaac Gouy

2006-08-14, 7:01 pm


Jeff M. wrote:
> Let me describe something that I do on a regular basis in C++ to solve
> a real problem. This is really 2 problems in one, but 1 of them is
> pretty trivial to solve, and the other is what I'm stumped on.
> Hopefully someone can show me a new trick. :-)
>
> I need a list of global objects that is searchable at runtime, but
> created at compile or launch-time. For example, consider a game that
> when the player hits the ~ key a console pops up and allows them to
> type in commands to change the level he's playing on, enter God mode,
> etc. These would be done through console commands.
>
> Now, in C++, I just have a class called ConsoleCommand, that the
> constructor would add the new global instance to a linked list of
> available commands. These would be simplified with a macro, so that I
> could just do something like this in code:
>
> CONSOLE_COMMAND(load_level)
> {
> // load_level code here
> }
>
> This macro would create a global variable (something like
> __CC_load_level) with the name "load_level", that when executed would
> call a function (__CC_func_load_level) with the body defined above.
> Simple enough.
>
> The above I can do with Smalltalk pretty easily. I can define a
> ConsoleCommand class, with a class variable that is a linked list of
> commands, and calling #new would create the new command and add it to
> the list. Simple enough.
>
> However, in Smalltalk I don't have source files, so unlike C++, I can't
> create a ton of ConsoleCommand objects strewn throughout the codebase
> easily. I could have some #init method in another class that created
> them all, but that function would turn out to be pretty nasty rather
> quickly.
>
> What I think would be more elegant is to have each instance console
> command be a singleton subclass of ConsoleCommand. But what I can't
> figure out how to do is iterate over all the subclasses of a class or
> even make a true singleton objecct in Smalltalk (which I'm sure is
> trivial, I just haven't figured it out yet). If the singleton version
> of each command could register itself with the parent, then this would
> be simple again.
>
> Any pointers appreciated. Thanks!
>
> Jeff M.


There's an entry for Singleton in the Dolphin Help Index - class
instance variables are kind of interesting.

Isaac Gouy

2006-08-15, 4:02 am


Jeff M. wrote:
-snip-
> I need a list of global objects that is searchable at runtime, but
> created at compile or launch-time.

-snip-

It's worth thinking about what compile-time and launch-time mean in
Smalltalk.

When you are working in a development image and you /accept/ a class
definition or a method definition - that's compile-time.

What do we launch at launch-time? Probably we launch an image that has
been stripped and saved; we launch an image that contains whatever
objects were around when it was saved, with the exception of the
development environment.

Somewhere in-between compile-time and launch-time other stuff can
happen!

We can create and initialize objects; we can create and initialize all
the singleton command objects, we can populate the list of command
objects, we can inspect the command objects to check they are what we
want - and then we can save the image.

When we launch that image again, it will contain the same objects it
contained when we saved it - poor mans persistence.

jtzecher

2006-08-15, 8:02 am


Jeff M. wrote:

> What I think would be more elegant is to have each instance console
> command be a singleton subclass of ConsoleCommand. But what I can't
> figure out how to do is iterate over all the subclasses of a class or
> even make a true singleton objecct in Smalltalk (which I'm sure is
> trivial, I just haven't figured it out yet). If the singleton version
> of each command could register itself with the parent, then this would
> be simple again.


Creating a singleton of a subclass (or any class) is pretty simple.
Define a classInstanceVariable called Current on the super class. I
know this is available in VA. If you don't have classInstanceVariables
in your smalltalk dialect, then create a class variable on each
subclass called Current.

Code a method called #current
current
"Return a singleton of this class"

Current isNil ifTrue: [ Current := self new ].
^ Current

Then whenever you want to reference the singleton, just say "MySubClass
current".

For iterating, you can also say

ConsoleCommand allSubclassesDo: operation

where operation is a one argument block.

Paolo Bonzini

2006-08-15, 7:02 pm


> Creating a singleton of a subclass (or any class) is pretty simple.
> Define a classInstanceVariable called Current on the super class.


Class-instance variables are usually named with a lowercase initial.

> know this is available in VA. If you don't have classInstanceVariables
> in your smalltalk dialect, then create a class variable on each
> subclass called Current.


I think class-instance variables are available in all languages -- and
if they're not, it's just a problem in the GUI. Unlike class
variables, which are a kind of global variable, class-instance
variables are nothing magic: they are just an instance variable of the
metaclass (and they have a lowercase initial just like all instance
variables).

You can define one with something like

ConsoleCommand class addInstVarName: #current

or

ConsoleCommand class instanceVariableNames: 'current'

Paolo

jtzecher

2006-08-16, 8:05 am


Paolo Bonzini wrote:
>
> Class-instance variables are usually named with a lowercase initial.


You are correct. Typo.


> I think class-instance variables are available in all languages -- and
> if they're not, it's just a problem in the GUI. Unlike class
> variables, which are a kind of global variable, class-instance
> variables are nothing magic: they are just an instance variable of the
> metaclass (and they have a lowercase initial just like all instance
> variables).


The advantage of class instance variables over regular class instance
variables is that you can define them at the super class and each sub
class can have a different value within it. A class variable defined at
the super class level has the same value for all subclasses beneath it.

Paolo Bonzini

2006-08-16, 7:02 pm

> > I think class-instance variables are available in all languages -- and
>
> The advantage of class instance variables over regular class instance
> variables is that you can define them at the super class and each sub
> class can have a different value within it. A class variable defined at
> the super class level has the same value for all subclasses beneath it.


Agreed. That's why class variables need "magic", while any language
that supports instance variables and metaclasses, will also support
class-instance variables. In fact, things like the class comment or
the method dictionary, that are instance variables of Class or its
superclass Behavior, can be considered examples of class-instance
variables (of a non-existing superclass of Object)

Paolo

Jeff M.

2006-08-21, 7:03 pm

Paolo Bonzini wrote:
>
>
> Agreed. That's why class variables need "magic", while any language
> that supports instance variables and metaclasses, will also support
> class-instance variables. In fact, things like the class comment or
> the method dictionary, that are instance variables of Class or its
> superclass Behavior, can be considered examples of class-instance
> variables (of a non-existing superclass of Object)


This is very interesting to me. I'd like to follow this up with another
"problem" and see how class instance variables could be used to solve
it.

Let's say I'm making a game (since that's what I do), and I wanted to
track everything that was created. Here's a very primitive class
hierarchy:

+ GameObject
+ Human
+ NPC
+ Player
+ Vehicle
+ Item
+ Weapon

Now, if I understand what's been stated above, I could add a class
instance variable named 'allOfMe' to GameObject. And then in the
initialize code, do something like this:

initialize
self class addInstance: self.

Where #addInstance: would add it to the allOfMe collection (linked
list, whatever). The idea being that if I created 2 players, a car, 4
guns, and 1 NPC, my list would look like this (numbers in parens show
how many objects are in that class's collection):

+ GameObject (8)
+ Human (3)
+ NPC (1)
+ Player (2)
+ Vehicle (1)
+ Item (4)
+ Weapon (4)

And, presumably I could make some methods in GameObject to iterate over
all the objects in GameObject (or any of it's subclasses). So my
question now becomes, how can I make methods in GameObject act on the
correct 'allOfMe' instance variable for subclasses. I wouldn't want to
have to duplicate all the methods in GameObject in all the subclasses.

It's possible Smalltalk already does this (I haven't tried my idea out
just yet), and this is a very simple problem. I'm just trying to
understand how the class instance variables work right now, and figure
out how I can use them best.

Thanks!

Jeff M.

Chris Uppal

2006-08-21, 7:03 pm

Jeff M. wrote:

> And, presumably I could make some methods in GameObject to iterate over
> all the objects in GameObject (or any of it's subclasses). So my
> question now becomes, how can I make methods in GameObject act on the
> correct 'allOfMe' instance variable for subclasses. I wouldn't want to
> have to duplicate all the methods in GameObject in all the subclasses.


If you define those methods on the class-side of GameObject, then each subclass
will inherit them. When they execute they, like all methods, will read the
instance variables of the object to which the message is sent (in this case one
of your classes). So if you send #countInstances to Human, then it'l answer
the size of the collection held in that class object's instance variable called
allOfMe. I.e. "it just works(tm)" ;-)

BTW, this may be obvious already, but another way you could set this up would
be for GameObject class's #new method to add the newly created object to the
receiver class's allOfMe collection before answering it. It's something of a
matter of taste whether that's better than expecting each object's #initialize
method to do that.

Another BTW: I realise that this is only an example, but it suggests that you
may not yet have noticed #allInstances and #allSubinstances -- you might find
them interesting. For instance you can send #allInstances to any class and
it'll answer an Array of all the objects which are instances of that class.

-- chris


Jeff M.

2006-08-21, 7:03 pm

Chris Uppal wrote:
> Jeff M. wrote:
>
>
> If you define those methods on the class-side of GameObject, then each subclass
> will inherit them. When they execute they, like all methods, will read the
> instance variables of the object to which the message is sent (in this case one
> of your classes). So if you send #countInstances to Human, then it'l answer
> the size of the collection held in that class object's instance variable called
> allOfMe. I.e. "it just works(tm)" ;-)


Okay. That's what I was hoping.

> BTW, this may be obvious already, but another way you could set this up would
> be for GameObject class's #new method to add the newly created object to the
> receiver class's allOfMe collection before answering it. It's something of a
> matter of taste whether that's better than expecting each object's #initialize
> method to do that.


This was something I didn't realize until just recently. Coming from
C++, #new was quite confusing until I understood that Object was
actually an object and only incidentally happend to have the same name
as the class.

> Another BTW: I realise that this is only an example, but it suggests that you
> may not yet have noticed #allInstances and #allSubinstances -- you might find
> them interesting. For instance you can send #allInstances to any class and
> it'll answer an Array of all the objects which are instances of that class.


I did not know this. Thanks! :-)

Jeff M.

Paolo Bonzini

2006-08-21, 7:03 pm

> Another BTW: I realise that this is only an example, but it suggests that you
> may not yet have noticed #allInstances and #allSubinstances -- you might find
> them interesting. For instance you can send #allInstances to any class and
> it'll answer an Array of all the objects which are instances of that class.


And you (Jeff) may also like to know about #allInstancesDo: which is
more efficient as it does not build a collection. Not all dialects
have #allSubinstancesDo: but it is trivial to implement it with
#allInstancesDo: and #allSubclassesDo:.

allSubinstancesDo: aBlock
self allInstancesDo: aBlock.
self allSubclassesDo: [ :each |
each allInstancesDo: aBlock ]

In most dialects there's also #withAllSubclassesDo: so that you have

allSubinstancesDo: aBlock
self withAllSubclassesDo: [ :each |
each allInstancesDo: aBlock ]

Also, if you want your own implementation, you should look at weak
collections, because otherwise your GameObjects will never be reaped by
the garbage collector. Note that even with the facilities above, it
may make sense if your visits happen very often. In this case
#allInstancesDo: is not very efficient because it loses time discarding
objects of other classes.

Paolo

Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com