lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



I have been thinking more about the issue of separate environments and how that helps with an object-oriented approach to Lua, with C++ object interactions. As Rici has pointed out, when you have a function representing an object method you really want its environment to be something common to all instances of this object type. Otherwise you will be creating functions for every instance, which would not only bloat memory some, but would take time as well.

If you did have an environment common to all instances of an object type, then it could have an __setindex metamethod. Let's say we have a class called MyClass with an __call metamethod to do instantiation. Further, let's say this class has a member variable 'a' and a member function 'f' defined like this:

    Function MyClass:f(v) a = v end

then you could go:

    local instance = MyClass()
    instance:f(5)

and when f(v) runs, the __setindex metamethod would get called with the parameters of the common function environment, the property 'a' and the value 'v'. This __setindex method can then go up to the next function to the stack and find the 'self' reference as the first param. It can then look up 'a' in this reference, set its value to 'v' and you're done. A nice object-oriented mechanism for handling instance local access.

The problem with this is that you don't really KNOW if the first parameter to the function is the 'self' reference. It may have been called using the SELF opcode to setup the stack. But that fact doesn't leave behind any clues. This is because this:

    instance:f(5)

is equivalent to:

    instance.f(instance, 5)

in Lua. That is really too bad. It can lead to very tough to debug errors. I know that, even though I have been writing Lua for several months now I still often use '.' when I intended to use ':'. Often this is easy to catch: the param after 'self' is something that looks nothing like a self pointer, so an error is generated. But what if you are passing an object that looks like a self pointer as the first param? You might have a function like this:

    function MyClass:g(v)
        if v ~= nil then
            self.a = v.a
        else
            self.a = 10
        end
    end

If you call this like this:

    local instance2 = MyClass()
    instance2.a = 5
    instance:g(instance2)

it works fine. After the call instance.a is 5. But if you do this instead:

    instance.g(instance2)

then you change instance2.a to 10, which was not the intent. No error is issued and it might be very difficult to track this down.

It would not be hard to add something to CallInfo to indicate that this method was called with the SELF instruction. That would no longer allow this:

    instance.g(instance, instance2)

in place of this:

    instance:g(instance2)

but it would still be compatible because I'm making the ':' syntax a requirement in my object system. The rest of Lua works as it always has.

Anyway, does this seem like a reasonable approach?

--
chris marrin              ,""$, "As a general rule,don't solve puzzles
chris@marrin.com        b`    $  that open portals to Hell" ,,.
        ,.`           ,b`    ,`                            , 1$'
     ,|`             mP    ,`                              :$$'     ,mm
   ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
  m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
 b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
 ```"```'"    `"`         `""`        ""`                          ,P`