1

This is something I want to do in C++ using the Lua C API.

I'm trying to figure out a good way to make userdata derive from a base userdata object.

I want to be able to do this:

local item = me:GetCurrentItem()
print(item:GetPos())

instead of:

local item = me:GetCurrentItem()
print(item:GetBaseObject():GetPos())

In these examples, me:GetCurrentItem() returns userdata with some functions, but it lacks the base functions which item:GetBaseObject() returns.

I'm binding Lua in the Crysis Wars SDK for learning purposes. The SDK provides an interface to the base entity which is a struct. The IItem struct (me:GetCurrentItem()) is the same. Since these are structs I can't cast them to the base struct or call its base functions. I have to use the IEntity *GetEntity() function.

I've tried chaning the self pointer in __index but it causes the local variable "item" to become "entity" which is kinda obvious, but I want it to revert back after calling the GetPos function, which seems illogical somehow.

Does anyone have a good solution to this issue?

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
CapsAdmin
  • 95
  • 2
  • 7

1 Answers1

2

The obvious solution would be defining an item:GetPos() function that does the redirect.

function Item:GetPos()
  return self:GetBaseObject():GetPos()
end

Where Item is the item's metatable.

This is as efficient as making changes on the metatable, and less problematic.

EDIT: I can help you a bit with the repetitiveness too.

You can implement the following two functions:

function delegate(klass, methodName, memberName)
  klass[methodName] = function(self, ...)
    local member = self[memberName]
    if type(member) == 'function' then member = self[memberName](self) end
    return member[methodName](member, ...)
  end
end

And then use it like this:

delegate(Item, 'GetPos', 'GetBaseObject')

That single line whould do the same as the Item:GetPos 3-line definition above.

If you need to repeat this a lot, you can optimize it further with this other function:

function delegateMany(klass, methodNames, memberName)
  for _,methodName in ipairs(methodNames) do
    delegateMethod(klass, methodName, memberName)
  end
end

Which should allow you to do:

deletageMany(Item, {'GetPost', 'SetPos', 'GetColor', 'SetColor'}, 'GetBaseObject')

I haven't tested any of these functions, so beware of bugs. They should work with both "gotten properties" (self:GetSomething() as well as simple accesses self.something). The code assumes that you would always use ':' to invoke the delegated methods, so self is added when needed.

kikito
  • 51,734
  • 32
  • 149
  • 189
  • I thought of doing this if there would be no good way. It would be a very repetitive task to add the same functions again in all the classes since the base entity has a lot of functions I'm going to use. – CapsAdmin Jul 07 '11 at 12:40
  • Maybe I just need to realize this _is_ the proper way. – CapsAdmin Jul 07 '11 at 12:45
  • @CapsAdmin Repetitive code is never a good thing. I've added some code that should allow you to do what you want in a cleaner way. Let me know if you have any issues with it. – kikito Jul 07 '11 at 17:28
  • I wanted to do this in C++, but doing it in Lua doesn't seem like a bad idea after all. – CapsAdmin Jul 07 '11 at 20:27
  • I'd recommend starting with the Lua implementation, and if later on it's identified as a bottleneck, implement it in C++. Chances are that it won't be necessary though. Also, thanks for accepting my answer! – kikito Jul 07 '11 at 20:49