
Elias
28.09.2017
17:00:15
Есть

Philipp
28.09.2017
17:02:55
Я чего-то подвис на __index
tree.__mt = {
__index = function(self, key)
return self.__data[key] or self[key] or tree[key]
end,
__newindex = function(self, key, value)
self.__data[key] = value
end
}
При вызове какого-нибудь метода созданного объекта, переполнение стака

Google

Philipp
28.09.2017
17:03:40
И я в упор не вижу ошибку переопределения __index

Elias
28.09.2017
17:04:01
Потому что tree.__index вызывается, а там tree[key], который не существует, поэтому снова вызывается __index

Philipp
28.09.2017
17:04:30
Так tree[key] существует, вот

Elias
28.09.2017
17:05:30
__index в итоге вызывается когда-нибудь или нет?
Наверное из-за него stack overflow?

Philipp
28.09.2017
17:06:29
В __index
Именно в нем переполнение

Elias
28.09.2017
17:07:00
local tree = {
f = function()
print("HELLO")
end
}
tree.__mt = {
__index = function(self, key)
return self.__data[key] or self[key] or tree[key]
end,
__newindex = function(self, key, value)
self.__data[key] = value
end
}
print(tree:f())
Вот это работает

Philipp
28.09.2017
17:07:02
А, кажись, понял
Не
tree:f работает
А вот если вызывается не напрямую, а через объект
Щас полный код кину
https://pastebin.com/Q82Xtx2z

Google

Philipp
28.09.2017
17:08:06
Во.
local tree_obj = tree:new()
tree_obj:addChild() – stackoverflow
И я кажись понимаю. tree_obj вызывает свою метатабличку, которая ищет в self[key]. Я не уверен, но возможно он снова вызывает self.metatable.__index

Elias
28.09.2017
17:11:26
Просто тут нужно все функции таблицы tree копировать в obj, когда tree:new() вызывается
Иначе у создаваемого объекта не будет addChild ключа и функции

Philipp
28.09.2017
17:12:04
Да, я был прав
Как-то нужно избавиться от цикла в self[key], и оно будет работать

Saphire
28.09.2017
17:14:40
rawset
Или как оно там

Philipp
28.09.2017
17:14:45
Ага, уже тоже про него вспомнил
rawget, наверное
Вот так работает

Snusmumriken
29.09.2017
01:21:45
Иначе у создаваемого объекта не будет addChild ключа и функции
__index - это как раз такая штука, которая придумана "чтобы не копировать".
Если плодятся тысячи и миллионы объектов, копии ссылок на каждый метод у каждого объекта занимают нехило памяти, и требуют время на копирование.
Присобачить одну ссылку на метатаблицу быстрее, чем дублировать сразу много ссылок по списку. И сериализовать объекты исключительно с данными - проще.
Хотя и доступ теоретически чуть быстрее (не вызывается __index).
Поэтому у очень мелких объектов с одним-двумя методами (которые не надо сериализовать) эффективнее копировать методы, а у объектов с кучей методов которые не очень часто вызываются - эффективнее ставить метатаблицу с __index.
Но это уже "низкоуровневые оптимизации", а тебе я советую изучить метатаблицы, отличная штука : )

Elias
29.09.2017
08:12:07
Да, что-то я забыл о том, как нормально реализовывать ООП после долгого использования библиотеки для этого. :D

Sergey
29.09.2017
08:18:45
а какую либу используешь?

Elias
29.09.2017
08:22:41
https://github.com/kikito/middleclass
Если кто-то раньше не встречал профиль этого парня, советую посмотреть на другие репозитории и его сайт. Тонны годноты.


Snusmumriken
29.09.2017
08:35:46
ClassFoo = {}
ClassFoo.__index = ClassFoo
function ClassFoo:new(a, b, c)
local o = {}
o.a, o.b, o.c = a, b, c
return setmetatable(o, self)
end
function ClassFoo:plusTwo()
self.a = self.a + 2
self.b = self.b + 2
self.c = self.c + 2
end
obj = Class:new(10, 20, 30)
obj:plusTwo()
ClassBar = setmetatable({}, {__index = ClassFoo})
...
Ну типа для такой мелочи как ООП - либы не нужны.
Ок. Вот тебе самая короткая реализация ООП с наследованием.
Куча магии которая пашет.
function Class(name, parent)
local smt, gmt = setmetatable, getmetatable
local parent = parent
local mt, cl = {}, {}
mt.__index = type(parent) == 'table' and parent or parent and error('Class inheritance error: table expected got '..type(parent), 2) or nil
mt.__call = function(self, ...) return self.init and self.init(self, ...) or {} end -- average call method
cl.__name = name or ('class id: %8d'):format(math.random(99999999)) -- name of class for getting
cl.__index = cl
cl.Inst = function(t) return smt(t, cl) end
cl.Super = mt.__index
cl.Type = function(self, obj) return type(obj) == 'table' and gmt(obj) == gmt(self) or self.__name end
cl.IsObjectOf = function(self, class) return rawequal(class, gmt(self)) end
return smt(cl, mt)
end

Google

Elias
29.09.2017
08:38:53
Ага
Просто у меня в игре и наследование есть, и статические переменные, что несколько усложняет всё
К тому же хорошо скрывать детали реализации классов и писать в итоге только
local SomeClass = class('SomeClass')

Snusmumriken
29.09.2017
08:40:07
А вот это ты зря, со статическими переменными : )
Попробуй древовидные архитектуры наследования.
А если класс - вспомогательный к чему-то - давать ему ссылку на объект, которому он помогает.

Alexey
29.09.2017
08:41:43
Главная проблема при наследовании с приватными данными/методами
Что если базовый класс имеет параметр _data и наследуемый класс также определит тот же
Это пожно реализовать только через замыкания

Snusmumriken
29.09.2017
08:41:47
Зачем нужна приватность?
Объективные причины, а не "ну типа кто-то узнает как оно на самом деле устроено".
Замыкания (и уж тем более - приватность через них) - в 99% случаев избыточная штукенция, которая только жрёт лишнюю память/требует лишнее процессорное время. Ну, это на моей практике.

Elias
29.09.2017
08:45:58
Статические переменные мне нужны, когда в другой части программы кто-то её хочет прочитать.
Например, Person.MaxAgeAllowed или что-то типа такого
Для приватности использую переменные с подчеркиванием в начале и не использую их потом. :D

Alexey
29.09.2017
08:46:20
Когда количество родителей в цепочке больше 4-5 и каждый из них имеет какие то методы/данные.
Очень неприятно когда вдруг понимаешь что вдруг переопределтил какой-то приватный метод :)

Snusmumriken
29.09.2017
08:46:47

Alexey
29.09.2017
08:48:57
так при наследовании в новом классе тоже нужны закрытые данные
И класс родителя могу писать не я, а взять из сторонней библиотеки

Sergey
29.09.2017
08:55:53
я у него активно inspect использую ))

Snusmumriken
29.09.2017
08:57:16
Так что если наследуетесь больше двух раз - уже что-то не то, и лучше уж базовый класс изменить.

Elias
29.09.2017
09:03:48
inspect - очень полезная штука для дебага, даа :)

Tverd
29.09.2017
09:21:19

Alexey
29.09.2017
09:24:12
Приватные методы/поля которые нельзя изменить/прочитать в наследуемом классе достаточно необходимая вещь
C++ overload vs override
и правила видимости для членов классов

Snusmumriken
29.09.2017
09:25:20
"Достаточно необходимая" - это фигня, я хотел конкретных примеров с доказательством, почему имеенно так надо, а не иначе.
Ну и, желательно, примеров, где НЕОБХОДИМО использовать цепочки из более чем трёх наследований.

Google

Tverd
29.09.2017
09:28:47
как говорил сам Роберто, хотите приватных методов - делайте локальные функции в модуле

Snusmumriken
29.09.2017
09:33:07
Нене, тут имеются ввиду приватные поля, например:
ClassFoo = {}
ClassFoo.__index = ClassFoo
function ClassFoo:new(a, b, c)
local o = {}
local private = {}
o.a, o.b, o.c = a, b, c
function o.getPrivate(key)
return private[key]
end
function o.setPrivate(key, value)
private[key] = value
end
return setmetatable(o, self)
end
И типа табличка private - "скрыта от глаз", вся фигня, и только парой методов его можно достать.
Но я всё ещё не вижу объективной необходимости так делать :<
Складывается впечатление, будто программисты - параноики, которые такие:
"Ха! Он больше не узнает что это за метод даже если попробует пройтись по всем полям таблицы! Какой я молодец, скрыл реализацию! Больше никто-о-о-о-о не узнает как оно работает!".
Ну ёпт, пользователь либы в любом случае найдёт способ всё сломать.
Пущай читает доки, там написано как использовать.


Tverd
29.09.2017
09:40:07
Понятно что можно и так, я говорил про то, что Роберто в книге написал. Типа пользуйтесь вот так.

Snusmumriken
29.09.2017
09:40:14
Угу : )
Благо логично и достаточно просто.

Group Butler [beta]
29.09.2017
15:30:14
Добро пожаловать в чат pro.lua, Alexey! Ознакомься с правилами чата (в описании и прикрепленном сообщении), и присоединяйся к обсуждению.
Добро пожаловать в чат pro.lua, Андрей! Ознакомься с правилами чата (в описании и прикрепленном сообщении), и присоединяйся к обсуждению.

Ixtis
29.09.2017
18:04:58
https://mihacooper.github.io/effil/lua/2017/09/28/effil/

annelin
29.09.2017
18:31:08
а чем это лучше coroutines?

Ixtis
29.09.2017
18:31:34
Самому интересно

Snusmumriken
29.09.2017
18:35:35
Это похоже на настоящие потоки с расшаренными таблицами.

Влад
30.09.2017
20:18:49
а как мне получить данные внутри программы, если я хочу сделать echo "test" | program.lua?

Snusmumriken
30.09.2017
20:21:33
Смотря в какой период исполнения.
Как именно запускаешь?
Что происходит при выполнении команды
'echo "text" | program.lua'?
Потому что ты можешь сделать, например, логирование, которое сбросит абсолютно всё что тебя интересует в файлик.
Или ты можешь влепить debug.debug() в нужную точку, и вывести то что нужно самостоятельно ручками, поставив программу "на паузу" (продолжить исполнение - cont).

Elias
30.09.2017
20:25:00
Что-то типа того, нет?
#/bin/usr/lua
local args = { ... }

Snusmumriken
30.09.2017
20:25:13
Там уже есть табличка arg со всем, что передали при запуске.

Влад
30.09.2017
20:27:07
не
Я хотел получить данные через linux pipe. Вот так работает:
for line in io.lines() do
result = line
end
echo "text" у меня выдает в stdout некие данные, а я хотел их получить в скрипте lua

Snusmumriken
30.09.2017
20:28:27
Для linux pipe подключай posix.

Google

Влад
30.09.2017
20:28:53
да зачем, у меня уже работает через io.lines()

Snusmumriken
30.09.2017
20:31:27
Ок.
Тогда делай print.

annelin
01.10.2017
00:07:33
тогда делай print ⓒ