2013-02-17 35 views
5

nềnLua, C++ và metatables biến mất

tôi làm việc với Watusimoto vào trò chơi Bitfighter. Chúng tôi sử dụng một biến thể của LuaWrapper để kết nối các đối tượng C++ của chúng ta với các đối tượng Lua trong game. Chúng tôi cũng sử dụng một biến thể của Lua được gọi là lua-vec để tăng tốc hoạt động vector.

Chúng tôi đã cố gắng giải quyết một lỗi trong một khoảng thời gian đã bỏ qua chúng tôi. Các sự cố ngẫu nhiên sẽ xảy ra cho thấy các metatables hỏng. Xem here cho bài đăng của Watusimoto về vấn đề này. Tôi không chắc nó là vì một metatable tham nhũng và đã thấy một số hành vi thực sự kỳ lạ về mà tôi muốn hỏi ở đây.

Vấn đề biểu hiện

Như một ví dụ, chúng ta tạo ra một đối tượng và thêm nó vào một mức độ như thế này:

t = TextItem.new() 
t:setText("hello") 
levelgen:addItem(t) 

Tuy nhiên, các trò chơi sẽ thỉnh thoảng (không phải luôn luôn) sụp đổ. Với một lỗi:

attempt to call missing or unknown method 'addItem' (a nil value) 

Sử dụng một gợi ý được đưa ra trong câu trả lời cho bài Watusimoto đã đề cập ở trên, tôi đã thay đổi dòng cuối cùng như sau:

local ok, res = pcall(function() levelgen:addItem(t) end) 

if not ok then 
    local s = "Invalid levelgen value: "..tostring(levelgen).." "..type(levelgen).."\n" 

    for k, v in pairs(getmetatable(levelgen)) do 
     s = s.."meta "..tostring(k).." "..tostring(v).."\n" 
    end 

    error(res..s) 
end 

này in ra metatable cho levelgen nếu có điều gì khi gọi sai phương thức từ nó.

Tuy nhiên, điều này thật điên rồ, khi không thành công và in ra tính khả thi, metatable là chính xác như thế nào (với cuộc gọi addItem và mọi thứ chính xác). Nếu tôi in các metatable cho levelgen khi tải tập lệnh, và khi nó không sử dụng pcall ở trên, họ là giống hệt nhau, mọi cuộc gọi và con trỏ đến userdata là như nhau và như nó phải được.

Điều đó giống như việc có thể làm cho số ngẫu nhiên cho levelgen tự động biến mất một cách ngẫu nhiên.

Có ai có ý tưởng gì đang xảy ra không?

Cảm ơn bạn

Lưu ý: này không xảy ra chỉ với những đối tượng levelgen. Ví dụ, nó đã xảy ra trên đối tượng TestItem được đề cập ở trên là tốt. Trong thực tế, đó là mã cùng bị treo trên máy tính của tôi tại dòng levelgen:addItem(t) nhưng tai nạn trên máy tính của nhà phát triển khác với dòng t:setText("hello") với thông báo lỗi tương tự missing or unknown method 'setText' (a nil value)

+0

Bạn đã thử chạy mã của mình trong Valgrind chưa? Bạn có thể có một vấn đề tham nhũng bộ nhớ từ C + +. – nneonneo

+0

Xin chào, vâng, chúng tôi đã chạy nó thông qua valgrind nhiều lần ngay bây giờ, và sau khi sửa chữa các lỗi bộ nhớ đã có (trong mã không liên quan) vấn đề vẫn còn tồn tại – raptor

+0

Rất tò mò. Bạn có thể giảm vấn đề xuống một testcase ngắn không? – nneonneo

Trả lời

1

Sau khi hợp nhất với luồng ngược dòng (cam kết 3c54015) LuaWrapper, sự cố này đã biến mất. Nó dường như đã là một lỗi trong LuaWrapper.

Cảm ơn Alex!

2

Như với bất kỳ bí ẩn, bạn sẽ cần phải bóc nó ra khỏi lớp, từng lớp. Tôi khuyên bạn nên đi qua các bước tương tự Lua đang xảy ra và cố gắng để phát hiện nơi con đường lấy phân ra từ sự mong đợi của bạn:

getmetatable(levelgen).__index trở lại? Nếu đó là một bảng, sau đó kiểm tra nội dung của nó cho addItem. Nếu đó là một chức năng, sau đó cố gắng gọi nó với (table, "addItem") và xem những gì nó trả về.

Kiểm tra xem getmetatable trả về tham chiếu đến cùng một đối tượng trước và sau cuộc gọi (hoặc khi không thành công).

Có một số mức độ thụt lùi có thể so sánh được mà cuộc gọi đang diễn ra không? Nếu vậy, hãy cố gắng làm theo cùng một đường dẫn với các cuộc gọi rõ ràng và xem sự khác biệt ở đâu.

Bạn đang sử dụng các phím weak có thể khiến các giá trị biến mất nếu không có tham chiếu khác?

Bạn có thể cung cấp giá trị "mặc định" khi bạn phát hiện ra nó không thành công và tiếp tục xem liệu "tìm" lại phương pháp này sau không? Hoặc khi nó bị hỏng, nó bị hỏng cho mọi cuộc gọi sau đó?

Điều gì xảy ra nếu bạn lưu một giá trị thích hợp cho addItem và "sửa" nó khi bạn phát hiện ra nó bị hỏng?

Điều gì sẽ xảy ra nếu bạn chỉ xử lý lỗi (như bạn đã làm) và gọi nó là 10 lần? Nó sẽ hiển thị kết quả hợp lệ ít nhất một lần (sau khi nó không thành công)? 100 lần? Nếu bạn tiếp tục gọi phương thức tương tự khi nó hoạt động, nó sẽ thất bại? Điều này có thể giúp bạn tìm ra lỗi lặp lại nhiều hơn.

Tôi không quen với LuaWrapper để cung cấp thêm câu hỏi cụ thể, nhưng đây là các bước tôi sẽ thực hiện nếu tôi là bạn.

2

tôi rất nghi ngờ vấn đề là bạn có một lớp học hoặc struct tương tự như sau:

struct Foo 
{ 
    Bar bar; 
    // Other fields follow 
} 

Và rằng bạn đã tiếp xúc với cả hai Foo và Bar để Lua qua LuaWrapper. Các bit quan trọng ở đây là bar là trường đầu tiên trên cấu trúc Foo của bạn. Ngoài ra, bạn có thể có một số lớp thừa hưởng từ một số lớp cơ sở khác và cả lớp dẫn xuất và cơ sở được tiếp xúc với LuaWrapper.

LuaWrapper sử dụng hàm được gọi là Mã định danh để theo dõi duy nhất từng đối tượng (như đối tượng đã cho đã được thêm vào trạng thái Lua) hay chưa. Theo mặc định, nó sử dụng địa chỉ đối tượng làm khóa. Trong trường hợp giống như một trong những đặt ra ở trên có thể là cả hai Foo và Bar có cùng một địa chỉ trong bộ nhớ, và do đó LuaWrapper có thể bị lẫn lộn.

Điều này có thể dẫn đến việc tìm đối tượng sai khi có thể tìm kiếm phương pháp. Rõ ràng, vì nó đang nhìn vào các metatable sai nó sẽ không tìm thấy phương pháp bạn muốn, và do đó, nó sẽ xuất hiện như thể metatable của bạn đã bí ẩn bị mất mục.

Tôi đã kiểm tra thay đổi theo dõi từng dữ liệu của mỗi đối tượng thay vì trong một chồng khổng lồ. Nếu bạn cập nhật bản sao LuaWrapper của bạn lên bản mới nhất từ ​​kho lưu trữ, tôi khá chắc chắn rằng sự cố của bạn sẽ được khắc phục.

+1

Tôi nghĩ rằng có thể chẩn đoán của bạn là chính xác, nhưng vì lý do sai. Có thể là các lớp đang bị nhầm lẫn, nhưng không phải vì thừa kế, mà là vì chúng ta đang di chuyển các đối tượng nhanh chóng và C++ có thể gán lại địa chỉ cho một kiểu đối tượng khác trước khi bộ sưu tập rác của Lua có cơ hội dọn sạch mọi thứ. Phân lớp không phải là vấn đề ở đây. Điều đó nói rằng, những thay đổi gần đây của bạn để LuaWrapper có thể giải quyết vấn đề. – Watusimoto

+0

Wow, cảm ơn vì đã tìm được điều này! Chúng tôi sẽ chuyển qua các thay đổi và cho bạn biết. – raptor

+0

hm, tôi đã không xem xét trường hợp tái sử dụng địa chỉ nhanh hơn Lua GC là gc-ing chúng. Có lẽ có một nhu cầu cho một số loại luaW_forget để hướng dẫn LuaWrapper hoàn toàn quên rằng nó theo dõi một đối tượng nhất định. Một giải pháp thay thế có thể là viết chức năng nhận dạng của riêng bạn đảm bảo một giá trị duy nhất cho từng đối tượng (nếu bạn có bất kỳ cách nào để nhận dạng duy nhất các đối tượng mà bạn có thể tận dụng). – Alex