2

I have integrated Lua 5.3 in my C++ code and I have added several math classes that should be interchangeable between the two environments.

For example, I have a vec2 Lua metatable with C functionality to link it to my C++ class for vec2d.

Now, my vec2 metatable has a __mul operator so that I can write Lua code like:

local vector = vec2.create(1, 1)
local scaledVector = vector * 5
print(tostring(scaledVector)) -- outputs 5, 5

But sometimes, I just want to write it the other way around, I want this to work too:

local vector = vec.create(1, 1)
local scaledVector = 5 * vector -- error: Class metatable function __index called on something else than userdata
print(tostring(scaledVector)) -- I want 5, 5

I understand why it doesn't work.

Is this at all possible in Lua? And if so... how? (And I am looking for a C/C++ solution, not some kind of construction written in Lua)

4
  • You probably know best, but I tagged it as C and C++ because I use classes and use the C interface for Lua to bind functionality. I am looking for a solution that solves this problem from C/C++, not a solution that solves this from Lua. Commented Dec 5, 2016 at 18:13
  • could You please show Your metatable and code behind __mul and __tostring? Commented Dec 5, 2016 at 18:17
  • C and C++ should've been tagged. Commented Dec 5, 2016 at 20:57
  • Thank you @warspyking, I returned the tags. Commented Dec 5, 2016 at 21:43

1 Answer 1

2

If the left operand of a multiplication does not have the __mul metamethod set, Lua will check the right operand to see if it has a __mul metamethod. So 5 * vec should work just as well as vec * 5:

function scale_vector(vec, alpha)
  local out = {}
  for i=1,#vec do
    out[i] = vec[i] * alpha
  end
  return out
end

function print_vector(vec)
  for i=1, #vec do
    if i > 1 then
      io.stdout:write("\t")
    end
    io.stdout:write(vec[i])
  end
  io.stdout:write("\n")
end


mt = {
  __mul = function(a, b)
    if type(b) == "number" then
      print("Case 1")
      return scale_vector(a, b)
    elseif type(a) == "number" then
      print("Case 2")
      return scale_vector(b, a)
    else
      error("Cannot scale by non-number factor")
    end
  end
}

vec = {1,2,3}
setmetatable(vec, mt)

print_vector( vec * 5 )
print_vector( 5 * vec )

Running this script results in

Case 1
5       10      15
Case 2
5       10      15

The part where you probably went wrong is that inside the __mul metamethod you need to do some tests to determine if your vector is the first parameter or the second parameter. My guess is that your code is currently assuming that the first parameter to the __mul metamethod is always the vector object.

Sign up to request clarification or add additional context in comments.

7 Comments

It seems I need to do some rewriting on my C++ wrapper around the Lua API. Thanks!
Am I right to assume that when I use __mul between two userdata objects A and B, that it's ok to implement A*B in A and B*A in B and returning error otherwise will make Lua look at the other before showing that error?
It will not look for a second metamethod if the first one it finds results in an error.
So this means I will have to implement all possible combinations in both? A will need a check for A*B and B*A and B will need the same? This will become a lot of combinations...
Yes, in a way. The way I would do it is to create a general "multiply two things" function and use this function as the __mul metamethod for both A and B. Inside this "multiply two things" function you would need to have a bunch of tests to determine which concrete multiplication function to dispatch to (vector * matrix, matrix * number, etc, etc), but at least all the combinations are in a single place.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.