Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 Контент вільний від AI. Цей пост на 100% написаний людиною, як і все на моєму блозі. Насолоджуйтесь!

29.01.2024

Вбудовування бінарної бібліотеки в Ruby - словничок

Викликати бінарний код з Ruby - це настільки нормально, що стандартна бібліотека робить це щосекунди. На те є стандартний механізм інтеграції, який починається з ruby.h. Це заголовок C. На цей час С є спільною фундацією майже для всіх компільованих мов, тож будь то Go, Crystal, чи навіть асемблер, все одно буде інтегруватись через C.

Щоб зінтегруватись, ми повинні збудувати shared library, або ж як їх знають на Windows, DLL. При цьому ми підʼєднаємо до нашої бібліотеки “адаптер” до самого Ruby; на то є модуль mkmf. Сама бібліотека може бути написана будь-якою мовою, яка вміє створювати shared library.

Далі — у бібліотеці ми оголошуємо клас чи модуль: rb_define_module та наповнюємо його. А в Ruby - просто робимо бібліотеці require, та навіть нічого не треба знати про те, що вона зовнішня. Магія!

Втім, за досвідом, саме код інтеграції, який ховається за цією магією, буде найнеприємнішим. По-перше, він має бути написаний на C (або тонкій абстракції над C - такій, як CGO). По-друге, і це ще не все, бо доведеться конвертувати типи в спеціальні для Ruby та робити інші цікаві речі.

Щоб уникнути такого коду, я знаю два інструменти. SWIG - це генератор коду інтеграції з заголовків C. Тобто достатньо зробити звичайну бібліотеку без інтеграції з Ruby. Або навіть взяти чужу - 14 років тому я так загортав glew - бібліотеку доповнень до OpenGL: SWIG особливо гарний для інтеграції великих чужих бібліотек.

А FFI то вже модуль для Ruby, який здатний зінтегрувати готову динамічну бібліотеку. Робить він це через власний шар C, який ми метапрограмуємо з Ruby. Тому FFI буде повільніше, ніж SWIG чи пряма інтеграція. Хоча це буде помітно тільки на коротких та частих викликах. FFI має сенс брати там, де лізти в вихідний код бібліотеки не можна або не хочеться.