#GitHub - noaccessl/glua-RobustClass: A f...

1 messages · Page 1 of 1 (latest)

echo field
#

why not an object based approach aka class(classType, classTypeParent) instead of robustclass( 'B : A' )

broken bluff
#

More for variety reasons and it kind of reflects of what's going on

echo field
#

iunno i did it the other way in my impl

winter barn
#

Somehow I'm finding your example code to be difficult to follow

#

It's early in the morning, admittedly

#

I think using easy-to-forget names like A and B makes it harder to follow than if you called them something like Dog : Pet, Cat : Pet, Pet : Animal, Animal

wary gorge
#

500 lines class lib 0_o

fast maple
#

Multiple inheritance đź’€

winter barn
#

Well well well

#

If it isn't just the thing I need to port some C++ over to Lua

#

@broken bluff Please consider including Lua Language Server annotations so that the documentation can be read directly in-editor rather than as a separate document

broken bluff
winter barn
#

This is a C++ like class system which will be helpful in recreating some C++ classes in Lua

echo field
#

usage example

local class = require "./class"

---@alias ECS.Entity Object<ECS.Entity.Template>
---@alias ECS.Entity.Proto Object<ECS.Entity.Template>
---@class ECS.Entity.Template
---@field components table<ECS.Component.Proto, ECS.Component>
---@field realm ECS.Realm
local entityProto = { }

function entityProto:init()
    self.components = { }
    return self
end

-- Associate this entity with a realm
---@param realm ECS.Realm
function entityProto:associate(realm)
    assert(not self.realm, "This entity is already associated with a realm")
    self.realm = realm
end

---@param ... ECS.Component
---@protected
function entityProto:update(...)
    self.realm.queryManager:update(self, ...)
end

-- Always try to add as much components per call as you can. This reduces the overhead required for persistent query rematching 
---@param ... ECS.Component
function entityProto:add(...)
    for i = 1, select("#", ...) do
        ---@type ECS.Component
        local comp = select(i, ...)
        assert(not self.components[comp.class], "Trying to add existing component")
        self.components[comp.class] = comp
    end

    -- if we are not assigned to one yet, dont bother, this will run on assignation
    if self.realm then
        self:update(...)
    end
end

---@param component ECS.Component
function entityProto:remove(component)
    self.components[component.class] = nil
    self:update(component)
end

return class.make(entityProto, "ECS.Entity")
winter barn
#

@broken bluff One issue I'm running into is that there is no support for namespaces. If I have a class called "Rect", I can't easily protect it from name collisions with any other addon which might use the same name

#

I can't name the class "namespace.Rect" because constructors must share their name with their class

#

So I'm forced to do namespace_Rect which isn't very nice to use

#

If I could name the class namespace.Rect and have it still understand that Rect is the name of the constructor, that would be nice

#

I'm also not clear on if it's easily possible to override a function from an inherited class and call the overridden base class function from within the override

broken bluff
broken bluff
winter barn
#

I've run into an issue that isn't currently able to be easily solved

#

I'm porting some C++ code and they have a base class that automatically registers itself with a manager class in its constructor

#

They're using a template class in the C++ to provide an ID that the constructor will use during registration

#

This is a snippet of that code where CHUNKID_GAME_OBJECT_DEF_VEHICLE is the ID that is provided to the template class

SimplePersistFactoryClass<VehicleGameObjDef, CHUNKID_GAME_OBJECT_DEF_VEHICLE> _VehicleGameObjDefPersistFactory;
#

I'm trying to achieve that same behavior by passing the ID into the constructor but because the base class's constructor (where it registers itself using the ID) is called before the constructor of the class (where it can set the ID) the class registers itself with a nil ID

#

There are a few ways to solve this, of course, but a relatively easy one would be to add a PostConstructor function that gets called automatically after the constructor has finished.
This would allow me to store the ID in the constructor, then register the instance in the PostConstructor

#

This may be outside the scope of what this library is trying to do so I'll understand if this isn't something you want to add but it's one of the few places I've found where RobustClass doesn't solve most of my C++ class problems

winter barn
#

@broken bluff I'm running into a problem that might be from RobustClass. When a child class inherits from two parent classes, do you expect that the fields/functions of both parent classes will be accessible through self within the child's constructor?

broken bluff
#

Lua allows for that

winter barn
#

I've been working to understand it for a couple of days

#

It's to do with inheritance

winter barn
#

I can reproduce the error reliably in my project but that is too large so I am trying to create a smaller repro but it's difficult

#

If it is a bug with RobustClass, it is subtle enough to only happen in very specific circumstances

#

The issue is basically that when I create a new instance of my Renegade_NetworkObject class and call a function on it from the constructor, it produces a nil value error and believes the function doesn't exist

NOTE: This code does not reproduce the error and is provided for explanation

local INSTANCE = robustclass.Register( "Renegade_NetworkObject" )

function INSTANCE:Renegade_NetworkObject()
    self:TestNetworkObject()
end

function INSTANCE:TestNetworkObject()
    print( "TestNetworkObject" )
end
winter barn
#

This is the specific error being produced

[renegade] addons/renegade/lua/renegade/code/wwnet/network-object.lua:90: attempt to call method 'TestNetworkObject' (a nil value)
  1. ConstructorForemost - addons/renegade/lua/renegade/code/wwnet/network-object.lua:90
   2. construct - addons/renegade/lua/renegade/sh_robustclass.lua:316
    3. New - addons/renegade/lua/renegade/sh_robustclass.lua:399
     4. unknown - addons/renegade/lua/renegade/ren_classtest.lua:33
winter barn
#

It only seems to happen the first time that the class script executes

#

If changes are made and it re-executes, the problem disappears

#

I'm unsure why

winter barn
#

The problem appears to be something with inherit called within robustclass.Register

broken bluff
#

Which version you're using? Have you edited anything?

winter barn
#

It seems that your method for multi-parent inheritance didn't work when many layers of parentage are required

#

In my case I needed it to support the following inheritance tree which it was unable to support previously:

PowerUpGameObject
└─ SimpleGameObject
   └─ PhysicalGameObject
      ├─ DamageableGameObject
      │  └─ ScriptableGameObject
      │     └─ BaseGameObject
      │        ├─ Persist
      │        │  └─ PostLoadable
      │        └─ NetworkObject
      └─ CombatPhysicsObserver
         └─ PhysicsObserver
broken bluff
# winter barn

Claude has perverted everything.

And 25.02.17 is quite an old date. The very first public version. I've been updating the repo ever since and fixing some stuff.

winter barn
#

But it did get multi inheritance working

#

When I’m at my PC I can try the latest version from git

winter barn
broken bluff
# winter barn In my case I needed it to support the following inheritance tree which it was un...

The thing with this is that your class hierarchy is bifurcated and the construct() & destruct() won't reach to the lateral subclasses. I haven't counted for this possibility when I was doing the library. I would expect the regular hierarchy to be rather linear and the side subclasses be for functionality, not additional construction. I'll have to sit with this and perhaps come up with something.

winter barn
broken bluff
#

Yeah

winter barn
#

It took it several attempts to understand that was the problem

#

I do like robustclass and would prefer to use human-written code so I would certainly be interested in an update if you made one

#

I may contribute to the documentation if I have some spare time. I think Lua will only benefit from there being a strong class system available

#

I do think that robustclass has one other significant missing feature: statics

#

Of course they can be done easily enough without a library (I do this in my project) but I think they should be a built-in feature to make it an all-in-one package

#

Easier to work with one system than two

#

If you are interested, I can share some of my code which uses robustclass so you can see how it is used by others

broken bluff
broken bluff
winter barn
broken bluff
#

Oh fine

winter barn
broken bluff
winter barn
#

This is the Lua class I wrote to port a C++ class from the Command & Conquer: Renegade source code

#

The CNC.CreateExport() function creates an empty table and stores it in a cache (with its file path as the key) so other scripts can call CNC.Import( path ) to retrieve the table (CNC.Import( path ) runs include( path ) exactly once for each script and then returns the cached table for any subsequent calls)

winter barn
#

Calling CNC.CreateExport() before CNC.Import( path ) ensures that circular dependencies (two classes which import each other) are possible.
If FirstClass imports SecondClass.lua and SecondClass imports FirstClass.lua, the order of events is:

  1. FirstClass calls CNC.CreateExport() and an empty table is provisioned for it.
  2. FirstClass calls CNC.Import( "second-class.lua" )
  3. The import/export library sees there is no cached table for second-class.lua so it runs include( "second-class.lua" )
    1. SecondClass calls CNC.CreateExport() to get its own table provisioned.
    2. SecondClass then calls CNC.Import( "first-class.lua" ) and the import/export library returns the empty table provisioned for FirstClass.
    3. SecondClass continues with its function and field declarations using the imported reference to the FirstClass table where necessary.
  4. The import/export library returns the now populated SecondClass table
  5. FirstClass continues with its function and field declarations using the imported reference to the SecondClass table

In this way both classes have valid references to the table that the other populates its static fields and functions in regardless of which script executes first