криптография 2004-2006 года - это старый и сложный код?
Старый, но несложный.
все прекрасно собралось
Добавьте оптимизации:
% g++ -v |& grep "gcc version"
gcc version 14.2.1 20241221 (Gentoo 14.2.1_p20241221 p7)
% g++ -Wall -Wextra -O3 -DDERS_CPU=cpu -c key.cpp
[...]
In file included from uint.hpp:22,
from key.hpp:19,
from key.cpp:11:
In destructor 'sh_ptr<T>::~sh_ptr() [with T = UInt]',
inlined from 'void Key::computeRemainders(ShUInt, int, std::vector<sh_ptr<UInt> >&)' at key.cpp:57:1:
sh_ptr.hpp:93:30: warning: '*(sh_ptr<UInt>::Rep*)<unknown>.sh_ptr<UInt>::Rep::refs' may be used uninitialized [-Wmaybe-uninitialized]
93 | ~sh_ptr() { if (--rep->refs==0) delete rep; }
| ~~~~~^~~~
и отлично работает!
Не могу проверить, потому что хз с чем это запускать вообще.
Но зато ошибки видны просто невооружённым глазом. Например,
else return ShUInt(); // NULL
вызывает сначала конструктор sh_ptr<UInt> с нулевым указателем ptr, а потом его удаляет через в итоге fixed_alloc::free(ptr), который…
Ой-вей, поздравляю с записью по нулевому указателю.
ЗЫ
ЗЫ — чувак написал 1400 строк кода, из которых треть — переизобретение своего наколенного boost::shared_ptr и прочего подобного, в которых сходу находятся ошибки, и пытается этим опровергнуть наличие проблем с миграцией на новые компиляторы у тех, кого строк кода не 1400, а 140000000.
Это, конечно, смешно.
может дело немножно в руках?
Это про любую технологию сказать можно. Дело всегда в руках, технологии всегда шикарные. Не можешь писать производительный код на JS? Да у тебя просто руки кривые.
Детская травма от необходимости явно писать типы нетривиальных match-конструкций в определении функций, когда ну вот же оно тут написано прямо в типе функции ну что ты петух тупой что ли блин.
За счёт того, что в агде паттерн-матчинг сделан ну вот прям в определении функций, там это всё куда лучше выглядит.
Что, конечно, не отменяет куда более лучшей автоматизации в коке, поэтому имеем то, что имеем.
Спасибо, но я все еще не понимаю, а где хранится FallbackState для каждого энкодера в системе?
Он хранится для каждого созданного «экземпляра» fallback-энкодера в замыкании соответствующей функции. Каждый раз, когда я пишу
encoder ← swFallbackEncoder vp9hw vp9sw
то у меня «запускается» тело swFallbackEncoder, которое создаёт (newIORef) мутабельную переменную (isFallback), где лежит FallbackState (CanUseHW по умолчанию), и возвращает лямбду, которая на каждый фрейм проверяет эту захваченную переменную.
На плюсах это выглядело бы примерно как
auto swFallbackEncoder(auto hw, auto sw)
{
auto shouldFallback = std::make_shared<bool>(false);
return [=](VideoFrame frame)
{
if (*shouldFallback)
return sw(frame);
auto res = hw(frame);
if (!res)
{
res = sw(frame)
*shouldFallback = true;
}
return res;
};
}
Вот эта mkEncoder она могла вернуть fallback врапер, а могла просто энкодер. Как вы потом ее результат вызваете?
Как функцию :]
runWithConfig config = do
maybeEnc ← mkEncoder (encoderName config)
case maybeEnc of
Nothing → putStrLn "uh oh"
Just enc → enc frame -- вызываю!
Можно написать тестовый код вроде
vp9swEncoder :: IO (Maybe Encoder)
vp9swEncoder = pure $ pure $ \frame → putStrLn "doing sw..." >> pure (Right $ show frame)
vp9hwEncoder :: IO (Maybe Encoder)
vp9hwEncoder = pure $ pure $ \frame → putStrLn "trying hw…" >> pure (Left "unable to encode")
и потом
main :: IO ()
main = do
-- лень проверять just/nothing, я знаю, что там just
Just enc1 ← mkEncoder "vp9"
Just enc2 ← mkEncoder "vp9"
enc1 10 >>= print
enc1 11 >>= print
enc2 20 >>= print
enc2 21 >>= print
выведет
trying hw…
doing sw...
Right "10"
doing sw...
Right "11"
trying hw…
doing sw...
Right "20"
doing sw...
Right "21"
обратите внимание — trying hw для каждого отдельного энкодера печатается только раз.
Далее создаем отдельный класс, который будет заниматься только одной задачей - формированием информации для вывода на дисплей.
Зачем для этого класс? Какой у него стейт? Могу ли я один инстанс класса DisplayFormatter дёргать из разных тредов? Могу ли я разные инстансы дёргать из разных тредов? Эквивалентен ли DisplayFormatter, отформатировавший сто юзеров, свежесозданному?
А можно просто сделать чистую функцию formatUser : User → String, и там этих вопросов нет. И передавать эти функции как аргументы другим функциям (если очень хочется звучать умно, можно это тоже называть DI).
Блин, извините, я идиот, зачем-то флаги держу, когда можно без флагов, и просто в стейте хранить саму функцию, которую надо вызывать. Так гораздо чище, и в кои-то веки пригодился Control.Monad.mfix:
swFallbackEncoder :: Encoder → Encoder → IO Encoder
swFallbackEncoder hwEncoder swEncoder = do
runner ← mfix $ \runner → newIORef $ \frame → hwEncoder frame >>= \case
Right bs → pure $ Right bs
Left err → writeIORef runner swEncoder >> swEncoder frame
pure $ \frame → readIORef runner >>= ($ frame)
В коде ключевые слова class, instance... Ну допустим это другое.
Это действительно другое.
class — это обобщение ООПных интерфейсов или плюсовых концептов. Концепт «итератор» был бы классом. «Моноид» на самом деле является классом. «Любая монада, поддерживающая возможность хранить стейт типа Foo», является классом.
Инстанс — это объявление, что данный тип соответствует данному интерфейсу/концепту, и реализация требуемых интерфейсом/концептов методов/типов.
Часть — код редактора (который определяет тайпкласс Tool,и непоказанная часть по использованию, потому что грузить экзистенциалами читателя не стоит). Часть — код клиентов (определяющих конкретные тулы, это каждая пара data/instance).
Соседние ораторы показали много разных сложных примеров, а я покажу рабоче-колхозно:
type EncodeResult = Either Error BitStream
type Encoder = VideoFrame → IO EncodeResult
-- создаёт энкодер, загружая libvpx и потенциально фейлясь
vp9swEncoder :: IO (Maybe Encoder)
vp9swEncoder = undefined
-- ну дрова уж точно могут зафейлиться при инициализации
vp9hwEncoder :: IO (Maybe Encoder)
vp9hwEncoder = undefined
data FallbackState = CanUseHW | ShouldFallback
swFallbackEncoder :: Encoder → Encoder → IO Encoder
swFallbackEncoder hwEncoder swEncoder = do
isFallback ← newIORef CanUseHW
pure $ \frame → readIORef isFallback >>= \case
ShouldFallback → swEncoder frame
CanUseHW → hwEncoder frame >>= \case
Right bs → pure $ Right bs
Left _ → writeIORef isFallback ShouldFallback >> swEncoder frame
Дальше это можно обмазать типами, чтобы swFallbackEncoder можно было создать только из пары SW и HW-энкодеров, но это предлагается читателю в качестве упражнения.
И в ООП вы или создаете VP8SWEncoder или H265HWEncoder или SoftwareFallbackEncoder(VP9SWEncoder, VP9HWEncoder). А потом просто вызвыаете Encode() у инстанса не думая о том, какой зоопарк кодеков у вас на самом деле.
А в чём вопрос? Просто возвращаете функцию.
mkEncoder :: String → IO (Maybe Encoder)
mkEncoder "vp9" = do
vp9swResult ← vp9swEncoder
vp9hwResult ← vp9hwEncoder
case (vp9hwResult, vp9swResult) of
(Just vp9hw, Just vp9sw) → pure <$> swFallbackEncoder vp9hw vp9sw
(_, _) → pure $ msum [vp9hwResult, vp9swResult]
mkEncoder "vp9:force-sw" = vp9swEncoder
mkEncoder "vp8" = vp8swEncoder
mkEncoder "h265" = h265hwEncoder
mkEncoder _ = pure Nothing
Если бы меня кто-то спрашивал, я бы сказал, что преимущества ООП раскрываются в предметных областях, которые хорошо матчатся на объектную модель (жира, например, как программный продукт: таска, проект, исполнитель, то-сё).
Звучит снова как ФП.
Я читал Domain Modeling Made Functional, и читал более классическую книгу Эванса. Если в первой всё было просто и понятно, хоть сейчас устраивайся в кровавый тырпрайз, то после второй я, по-моему, стал понимать даже меньше.
class Tool t m where
processPick :: t → Ctx → Point → m ()
handleSelection :: t → [Object] → m ()
data BackdoorCCP = BackdoorCCP
{ address :: String
, port :: Int
}
instance Tool BackdoorSendToCCPServers IO where
processPick bd _ p = sendToCCP bd $ "User clicked at " <> p
handleSelection = ...
data FilterClicks base = FilterClicks
{ filterPred :: Ctx → Point → Bool
, baseTool :: base
}
instance Tool t m => Tool (FilterClicks t) m where
processPick FilterClicks{..} ctx p
| filterPred ctx p = processPick baseTool ctx p
| otherwise = pure ()
handleSelection = ...
data TestSpy = TestSpy
instance Tool TestSpy (WriterT [(Point)] m) where
processPick _ _ p = tell [p]
handleSelection = ...
Функциональное программирование более гибкое, соглашусь. Податливо, как пластилин. Очень удобно для научных задачек и сольного программирования. НО ЗАВОДЫ ИЗ ПЛАСТИЛИНА НА СТРОЯТ.
Ага. Именно поэтому верифицированное ядро SeL4 в первую очередь сделано на хаскеле (с полуэкстракцией в C). Или поэтому смарт-контракты с требованиями доверия им делаются на хаскеле и верифицируются на коке/изабелле/етц.
…которые в него вступили по причинам традиций, социального давления и так далее, не понимая, что лично им в нём будет хуже, чем описывается в дискурсе.
На самом деле тут надо бы ещё как-то демонстрировать своё отношение к этим книгам, потому что прочитанный и стоящий на полочке Поппер или Фукуяма ничего не говорят о том, насколько вы с ними согласны.
Старый, но несложный.
Добавьте оптимизации:
Не могу проверить, потому что хз с чем это запускать вообще.
Но зато ошибки видны просто невооружённым глазом. Например,
вызывает сначала конструктор
sh_ptr<UInt>с нулевым указателемptr, а потом его удаляет через в итогеfixed_alloc::free(ptr), который…, который…
Ой-вей, поздравляю с записью по нулевому указателю.
ЗЫ — чувак написал 1400 строк кода, из которых треть — переизобретение своего наколенного
boost::shared_ptrи прочего подобного, в которых сходу находятся ошибки, и пытается этим опровергнуть наличие проблем с миграцией на новые компиляторы у тех, кого строк кода не 1400, а 140000000.Это, конечно, смешно.
Это про любую технологию сказать можно. Дело всегда в руках, технологии всегда шикарные. Не можешь писать производительный код на JS? Да у тебя просто руки кривые.
Детская травма от необходимости явно писать типы нетривиальных
match-конструкций в определении функций, когда ну вот же оно тут написано прямо в типе функции ну что ты петух тупой что ли блин.За счёт того, что в агде паттерн-матчинг сделан ну вот прям в определении функций, там это всё куда лучше выглядит.
Что, конечно, не отменяет куда более лучшей автоматизации в коке, поэтому имеем то, что имеем.
Он хранится для каждого созданного «экземпляра» fallback-энкодера в замыкании соответствующей функции. Каждый раз, когда я пишу
то у меня «запускается» тело
swFallbackEncoder, которое создаёт (newIORef) мутабельную переменную (isFallback), где лежитFallbackState(CanUseHWпо умолчанию), и возвращает лямбду, которая на каждый фрейм проверяет эту захваченную переменную.На плюсах это выглядело бы примерно как
Как функцию :]
Можно написать тестовый код вроде
и потом
выведет
обратите внимание — trying hw для каждого отдельного энкодера печатается только раз.
Зачем для этого класс? Какой у него стейт? Могу ли я один инстанс класса
DisplayFormatterдёргать из разных тредов? Могу ли я разные инстансы дёргать из разных тредов? Эквивалентен лиDisplayFormatter, отформатировавший сто юзеров, свежесозданному?А можно просто сделать чистую функцию
formatUser : User → String, и там этих вопросов нет. И передавать эти функции как аргументы другим функциям (если очень хочется звучать умно, можно это тоже называть DI).Я другой оратор и у меня другой тривиальный пример, я его ниже написал.
Блин, извините, я идиот, зачем-то флаги держу, когда можно без флагов, и просто в стейте хранить саму функцию, которую надо вызывать. Так гораздо чище, и в кои-то веки пригодился
Control.Monad.mfix:Это действительно другое.
class— это обобщение ООПных интерфейсов или плюсовых концептов. Концепт «итератор» был бы классом. «Моноид» на самом деле является классом. «Любая монада, поддерживающая возможность хранить стейт типаFoo», является классом.Инстанс — это объявление, что данный тип соответствует данному интерфейсу/концепту, и реализация требуемых интерфейсом/концептов методов/типов.
Часть — код редактора (который определяет тайпкласс
Tool,и непоказанная часть по использованию, потому что грузить экзистенциалами читателя не стоит). Часть — код клиентов (определяющих конкретные тулы, это каждая параdata/instance).Соседние ораторы показали много разных сложных примеров, а я покажу рабоче-колхозно:
Дальше это можно обмазать типами, чтобы
swFallbackEncoderможно было создать только из пары SW и HW-энкодеров, но это предлагается читателю в качестве упражнения.А в чём вопрос? Просто возвращаете функцию.
Звучит снова как ФП.
Я читал Domain Modeling Made Functional, и читал более классическую книгу Эванса. Если в первой всё было просто и понятно, хоть сейчас устраивайся в кровавый тырпрайз, то после второй я, по-моему, стал понимать даже меньше.
Как один из кучи вариантов.
isabelle/hol
Мне не зашло, но люди пользуются (а меня и от кока чё-т тошнит).
Да один фиг, даже обычное сожительство тоже переоценено.
На удалёнке от 10 до 30 тысяч долларов на руки, нафиг эти ваши офисы?
Ага. Именно поэтому верифицированное ядро SeL4 в первую очередь сделано на хаскеле (с полуэкстракцией в C). Или поэтому смарт-контракты с требованиями доверия им делаются на хаскеле и верифицируются на коке/изабелле/етц.
Почему не подходит? Вполне применяю его широко, от написания на нём компиляторов до перекладывающих жсоны опердней.
…которые в него вступили по причинам традиций, социального давления и так далее, не понимая, что лично им в нём будет хуже, чем описывается в дискурсе.
Или, более кратко, «брак переоценен».
На самом деле тут надо бы ещё как-то демонстрировать своё отношение к этим книгам, потому что прочитанный и стоящий на полочке Поппер или Фукуяма ничего не говорят о том, насколько вы с ними согласны.
Хоть одна девушка бы мне хоть раз такую претензию высказала бы, эх.
Можно, законы физики не запрещают. Но этически некрасиво.