Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Singleton Oop Question
Started by jake72 Mar 24 2013 05:53 AM

* * * * * 1 votes
23 replies to this topic

Best Answer dmccuskey , 24 March 2013 - 11:20 AM

this is the way i do it inside of my OO modules:
 

 
-- at beginning of module
local autostore_singleton = nil
 
-- at end of module
if autostore_singleton == nil then
  -- do any setup necessary for your object/table
    autostore_singleton = AutoStore:new()
    autostore_singleton:init()
    autostore_singleton:load()
end
return autostore_singleton

 
this works because the code inside of a module is run only once on the first require() even though there might be several of them inside of an app. the module loader simply saves the module return value and uses that for any subsequent module require().
 
so, what @thomas6 suggests will work for simple tables because of this fact. however, if you are using OO with an object constructor (e.g., new() ), then you will have to do something similar to what i suggest.
 
cheers,
dmc
 
ps, the example was taken from my library module dmc_autostore if you want to check out the entire code file: https://developer.coronalabs.com/code/dmc-lib-auto-store .

[TOPIC CONTROLS]
This topic has been archived. This means that you cannot reply to this topic.
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

jake72

[GLOBAL: userInfoPane.html]
jake72
  • Enthusiast

  • 68 posts
  • Corona SDK

Hello, I was wondering if there really is a way to use the Singleton method using tables. I'm not sure if it's possible, because Lua doesn't have static variables. And if you were to do a require("MySingletonFile") wouldn't it always return a new instance? Or do tables have the same instance no matter what requires them? Hope that was clear enough..



[TOPIC: post.html]
#2

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 982 posts
  • Corona SDK

In most cases the simplest thing is just creating a global variable requiring your module - don't worry about using a global once in a while, as long as it's not used in intensive for-loops (in which cases you can easily substitute it with a local var temporarily).

 

So basically just say:

myGlobal = require("mySingletonFile")

 

I know it's a simple answer, but that's the way I do it. Let me know if you something more sophisticated for some reason.



[TOPIC: post.html]
#3

jake72

[GLOBAL: userInfoPane.html]
jake72
  • Enthusiast

  • 68 posts
  • Corona SDK

In most cases the simplest thing is just creating a global variable requiring your module - don't worry about using a global once in a while, as long as it's not used in intensive for-loops (in which cases you can easily substitute it with a local var temporarily).

 

So basically just say:

myGlobal = require("mySingletonFile")

 

I know it's a simple answer, but that's the way I do it. Let me know if you something more sophisticated for some reason.

 

Okay, well I did some testing, and this is what I came up with. I made a little program to test this.

 

module.lua

 
local m = {}
 
m.t = "HELLO WORLD";
 
local i;
if not i then
    i = setmetatable({}, m);
end
return i
 
main.lua

local i = require("module")
 
print(i);
 
local h = require("module")
 
print(h);

 

And this is the output.

 

 

table: 0x7fc99a4088b0
table: 0x7fc99a4088b0
 
So it appears they are the same table. Am I missing something to as how tables work?


[TOPIC: post.html]
#4

dmccuskey

[GLOBAL: userInfoPane.html]
dmccuskey
  • Contributor

  • 156 posts
  • Enterprise

  Best Answer

this is the way i do it inside of my OO modules:
 

 
-- at beginning of module
local autostore_singleton = nil
 
-- at end of module
if autostore_singleton == nil then
  -- do any setup necessary for your object/table
    autostore_singleton = AutoStore:new()
    autostore_singleton:init()
    autostore_singleton:load()
end
return autostore_singleton

 
this works because the code inside of a module is run only once on the first require() even though there might be several of them inside of an app. the module loader simply saves the module return value and uses that for any subsequent module require().
 
so, what @thomas6 suggests will work for simple tables because of this fact. however, if you are using OO with an object constructor (e.g., new() ), then you will have to do something similar to what i suggest.
 
cheers,
dmc
 
ps, the example was taken from my library module dmc_autostore if you want to check out the entire code file: https://developer.coronalabs.com/code/dmc-lib-auto-store .



[TOPIC: post.html]
#5

dmccuskey

[GLOBAL: userInfoPane.html]
dmccuskey
  • Contributor

  • 156 posts
  • Enterprise

jake, your second example is similar to the one i suggested and it works. so why do you think that you're missing something ?

cheers,
dmc

[TOPIC: post.html]
#6

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 982 posts
  • Corona SDK

hi Jake,

 

I'm only following this with half my attention, but for a singleton you shouldn't need to use metatables at all, in my opinion - although I'm very curious to hear what dmccuskey thinks of this --> dmc, you're my OOP mentor as far a Lua goes! Massive respect ;-)



[TOPIC: post.html]
#7

jake72

[GLOBAL: userInfoPane.html]
jake72
  • Enthusiast

  • 68 posts
  • Corona SDK

this is the way i do it inside of my OO modules:
 

 
-- at beginning of module
local autostore_singleton = nil
 
 
-- at end of module
if autostore_singleton == nil then
  -- do any setup necessary for your object/table
    autostore_singleton = AutoStore:new()
    autostore_singleton:init()
    autostore_singleton:load()
end
return autostore_singleton

 
this works because the code inside of a module is run only once on the first require() even though there might be several of them inside of an app. the module loader simply saves the module return value and uses that for any subsequent module require().

 

so, what @thomas6 suggests will work for simple tables because of this fact. however, if you are using OO with an object constructor (e.g., new() ), then you will have to do something similar to what i suggest.

 

cheers,

dmc

 

ps, the example was taken from my library module dmc_autostore if you want to check out the entire code file: https://developer.coronalabs.com/code/dmc-lib-auto-store .

 

Ah, okay. I was trying to kinda follow how I did it in Java.. But looks like I don't even need to have a getter in my class for the instance of another if I'm not wanting multiple instances.. I'm actually using and really liking your OOP library. Really helpful. :D

 

jake, your second example is similar to the one i suggested and it works. so why do you think that you're missing something ?

cheers,
dmc

 

I guessed I was just confused as to how modules work in Lua. When you require them does it re-execute the code every time, or not. But if the return value is just saved in the require, then do you even need the

if not i then i = setmetatable({}, m) end
because that is not run every time..



[TOPIC: post.html]
#8

jake72

[GLOBAL: userInfoPane.html]
jake72
  • Enthusiast

  • 68 posts
  • Corona SDK

hi Jake,

 

I'm only following this with half my attention, but for a singleton you shouldn't need to use metatables at all, in my opinion - although I'm very curious to hear what dmccuskey thinks of this --> dmc, you're my OOP mentor as far a Lua goes! Massive respect ;-)

 

From the way I've seen it. When you do i = m it just references m when you call i. So setting a meta table copies a table. Tell me if I'm wrong, but thats what someone told me awhile ago..



[TOPIC: post.html]
#9

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 982 posts
  • Corona SDK

Hey Jake,

 

Setting the metatable of one object to another object, is like saying: "if you don't find a certain function or property in this objects metatable, then go look for it in the other objects metatable". From there on it becomes simple: if you create 20 'empty' objects, and assign all of them the metatable of a 'prototype' object, then these 20 objects will actually practically become instances of this one base object or class. OOP in Lua in a nutshell.

 

But the thing is, if you only want to use a singleton class, you just need this base object with its own functions and properties, and you don't need setting metatables, since this would only make sense if you want a second or third object to behave like your singleton - in which case we're not talking about singletons anymore.

 

Man, it was confusing to me at first, but I'm starting to love Lua's OOP more and more each day!



[TOPIC: post.html]
#10

jake72

[GLOBAL: userInfoPane.html]
jake72
  • Enthusiast

  • 68 posts
  • Corona SDK

Hey Jake,

 

Setting the metatable of one object to another object, is like saying: "if you don't find a certain function or property in this objects metatable, then go look for it in the other objects metatable". From there on it becomes simple: if you create 20 'empty' objects, and assign all of them the metatable of a 'prototype' object, then these 20 objects will actually practically become instances of this one base object or class. OOP in Lua in a nutshell.

 

But the thing is, if you only want to use a singleton class, you just need this base object with its own functions and properties, and you don't need setting metatables, since this would only make sense if you want a second or third object to behave like your singleton - in which case we're not talking about singletons anymore.

 

Man, it was confusing to me at first, but I'm starting to love Lua's OOP more and more each day!

Ah okay, but am I right that just saying table 1 = table 2 just references table 2. So this works..

 

 
local m = { "hey" }
local j = m
 
print(m[1])
 
j[1]="hey2"
 
print(m[1])
 
output:

hey
hey2


[TOPIC: post.html]
#11

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 982 posts
  • Corona SDK

Yep, variables get copied over into the new variable with = , but tables just get referenced. Confusing at first if you want an independent copy, but really practical because this allows you to make a local version of a global table (in this case, your singleton) for use in code-intensive blocks.



[TOPIC: post.html]
#12

dmccuskey

[GLOBAL: userInfoPane.html]
dmccuskey
  • Contributor

  • 156 posts
  • Enterprise

- although I'm very curious to hear what dmccuskey thinks of this --> dmc, you're my OOP mentor as far a Lua goes! Massive respect ;-)

LOL. :D thanks, Thomas !

[TOPIC: post.html]
#13

dmccuskey

[GLOBAL: userInfoPane.html]
dmccuskey
  • Contributor

  • 156 posts
  • Enterprise

i think what it boils down to is what type of object Jake wants to create.

the type thomas is suggesting is a completely valid object -- a single table with properties and methods assigned to it. in a sense though it is *already* a singleton because the intent is *not* to create other instances of it using new(). ( thomas, correct me if i'm wrong ). so it's a perfect solution.

because Jake started to use the setmetable() method, i thought that perhaps it was the beginning of a more complex object because (like Thomas said) that's the reason why you would use setmetable().

what i'm suggesting is for those situations when a singleton is required, but the module *does* allow other instances to be created. this is the situation that the Singleton Pattern solves. for example, you might use a storage/database module which allows multiple database files, but you want to make sure that your app only uses one.

now, technically because you have the code for these modules, you *could* go and make modifications directly to them, but it's cleaner and easier just to wrap that module in one of your own and use some singleton-esque code to constrain the object creation.

[TOPIC: post.html]
#14

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 982 posts
  • Corona SDK

You're very welcome! By the way, I'm a designer by profession, 20 years of Photoshop, Illustrator and 3D experience, so if I can return the favor somehow, let me know!



[TOPIC: post.html]
#15

jake72

[GLOBAL: userInfoPane.html]
jake72
  • Enthusiast

  • 68 posts
  • Corona SDK

i think what it boils down to is what type of object Jake wants to create.

the type thomas is suggesting is a completely valid object -- a single table with properties and methods assigned to it. in a sense though it is *already* a singleton because the intent is *not* to create other instances of it using new(). ( thomas, correct me if i'm wrong ). so it's a perfect solution.

because Jake started to use the setmetable() method, i thought that perhaps it was the beginning of a more complex object because (like Thomas said) that's the reason why you would use setmetable().

what i'm suggesting is for those situations when a singleton is required, but the module *does* allow other instances to be created. this is the situation that the Singleton Pattern solves. for example, you might use a storage/database module which allows multiple database files, but you want to make sure that your app only uses one.

now, technically because you have the code for these modules, you *could* go and make modifications directly to them, but it's cleaner and easier just to wrap that module in one of your own and use some singleton-esque code to constrain the object creation.

 

Yeah, and I'm coming from a Java perspective on the whole thing, which is like the situation you describe. I'm using your dmc_objects library for my OOP, and while it would be nice to have it be able to have other instances be created from it in the future, its not entirely needed. But I'm still wondering why I need to have the if nil then make a new instance, else just return the current instance, is Lua just saves the return anyways and doesn't execute that code again.



[TOPIC: post.html]
#16

dmccuskey

[GLOBAL: userInfoPane.html]
dmccuskey
  • Contributor

  • 156 posts
  • Enterprise

... But I'm still wondering why I need to have the if nil then make a new instance, else just return the current instance, is Lua just saves the return anyways and doesn't execute that code again.

hmmm. i guess you're right -- you could get rid of the if/then part and leave the initialization code by itself and return afterwards. if no initialization is necessary then you could even get rid of the variable and return from the new().

[TOPIC: post.html]
#17

jake72

[GLOBAL: userInfoPane.html]
jake72
  • Enthusiast

  • 68 posts
  • Corona SDK

hmmm. i guess you're right -- you could get rid of the if/then part and leave the initialization code by itself and return afterwards. if no initialization is necessary then you could even get rid of the variable and return from the new().

Which variable and return?



[TOPIC: post.html]
#18

dmccuskey

[GLOBAL: userInfoPane.html]
dmccuskey
  • Contributor

  • 156 posts
  • Enterprise

You're very welcome! By the way, I'm a designer by profession, 20 years of Photoshop, Illustrator and 3D experience, so if I can return the favor somehow, let me know!

wow, i'd say a senior designer such as yourself with good programming skills is a deadly combination. :) you can truly create anything. props to you !

my only design skills come from my copy of OmniGraffle. that app is indispensable because it makes it seem like i have more design talent than i really do. LOL

i will definitely keep you in mind. :)

[TOPIC: post.html]
#19

dmccuskey

[GLOBAL: userInfoPane.html]
dmccuskey
  • Contributor

  • 156 posts
  • Enterprise

Which variable and return?

sorry about that. this is what i was considering:

-- if you need to initialize:local singleton = MyClass:new()singleton:init()singleton:load()return singleton-- if no initialization is required:return MyClass:new()


[TOPIC: post.html]
#20

jake72

[GLOBAL: userInfoPane.html]
jake72
  • Enthusiast

  • 68 posts
  • Corona SDK

sorry about that. this is what i was considering:

-- if you need to initialize:
local singleton = MyClass:new()
singleton:init()
singleton:load()
return singleton
-- if no initialization is required:
return MyClass:new()

 

Ahh, that makes more sense. :) This is why I was asking, I was going to have in my main.lua a class that held the main game engine. The main class would be singleton but the game engine class wouldn't be. But from the looks of this I could just make the GameEngine class singleton and be done with it.. lol



[TOPIC: post.html]
#21

jake72

[GLOBAL: userInfoPane.html]
jake72
  • Enthusiast

  • 68 posts
  • Corona SDK

Wait, does garbage collection ever clear the modules Lua holds in require?



[TOPIC: post.html]
#22

dmccuskey

[GLOBAL: userInfoPane.html]
dmccuskey
  • Contributor

  • 156 posts
  • Enterprise

i don't believe so - i've never heard of nor run into that situation. i think you have to explicitly unload the module for it to be released to the GC, otherwise modules are unloaded when the Lua interpreter is shut down.

i found a discussion regarding the topic:

http://lua.2524044.n2.nabble.com/Unloading-shared-libraries-loaded-by-require-td5991252.html

[TOPIC: post.html]
#23

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 982 posts
  • Corona SDK

dmccuskey, yep, it's a pretty good situation to be in - especially since I used to be quite active in semi-pro music production and have a nice home studio set up. Typically, I code my game on my laptop, and then when I decide I need an ancient Japanese temple graphic or 8-bit soundtrack, I just model or produce that stuff in the studio or on my 3D-workstation, dropbox it to the laptop and continue coding, in the course of one evening. Tres cool, but what I'm lacking most of all is time!

 

I'm currently working on a 2D-platformer with a complete leveldesigner built-in, and having lots of fun, but it is a big project to tackle as a solo hobbyist, so taking longer than I wish it would, but I'll shoot you a preview in the next coming weeks!



[TOPIC: post.html]
#24

jake72

[GLOBAL: userInfoPane.html]
jake72
  • Enthusiast

  • 68 posts
  • Corona SDK

i don't believe so - i've never heard of nor run into that situation. i think you have to explicitly unload the module for it to be released to the GC, otherwise modules are unloaded when the Lua interpreter is shut down.

i found a discussion regarding the topic:

http://lua.2524044.n2.nabble.com/Unloading-shared-libraries-loaded-by-require-td5991252.html

 

ah, okay. I should be good then. Thanks for all the help everyone!! :D




[topic_controls]
 
[/topic_controls]