2025-11-27 01:34:37 +07:00

5.6 KiB
Raw Blame History

Plugins lesson 3.

Умеет формировать API для подключения плагинов.

Умеет создавать и собирать плагины в соответствии с заданным API.
(C)

Умеет подключать и отключать плагины в рантайме. (загрузка библиотеки и ее выгрузка)
На примере торговых ботов для биржи создадим систему с возможностью подключения плагинов. Научимся формировать API для плагинов, собирать и подключать их в рантайме, а также управлять ими — включать и отключать по мере необходимости.

Урок - практический. Студент после него сможет писать аналогичные решения, что мы и проверим в проекте.

Торговый бот - контекст, в котором показываются примеры (не production bot)

Придумать API

Start

В прошлых уроках мы узнали, что используя dlopen и dlsym можно динамически подгружать функции из отдельных файлов динамических библиотек. Это бывает полезно для уменьшения размера программы на диске и в оперативной памяти, а также выноса зависимостей в рациональные. В этом уроке мы изучим взаимодействие с динамическими кодом более высокоуровневым способом: системой плагинов.

Система плагинов

Система плагинов представляет собой подгружаемый функционал, взаимодействие с которым возможно через заранее определенный интерфейс. Как концепцию плагины можно представить так: определение интерфейса это прописывание трейта, а реализация трейта - уже сами плагины, но не вшитые в саму программу, а находящиеся в отдельном файле. Давайте попробуем сделать систему плагинов для собственного торгового бота. Пускай интерфейсом будет одна функция trade, которая по переданому срезу u32 цен будет принимать решение: купить, продать или ничего не делать:

pub enum TradeAction {
    Sell(u32),
    Buy(u32),
    None,
}

pub struct PluginInterface {
    pub trade: extern "Rust" fn(prices: &[u32]) -> TradeAction,
}

Основная программа, после подгрузки плагина сможет по такому интерфейсу сможет воспользоваться плагином. Теперь, библиотеку с таким интерфейсом нужно как-нибудь подгружать. Это можно сделать используя dlopen и dlsym, но в данном примере для удобства воспользуемся библиотекой libloading.

pub struct Plugin {
    plugin: Library,
}

impl Plugin {
    pub fn new(filename: &str) -> Result<Self, libloading::Error> {
        Ok(Plugin {
            plugin: unsafe { libloading::Library::new(filename) }?,
        })
    }
    pub fn interface(&self) -> Result<PluginInterface<'_>, libloading::Error> {
        Ok(PluginInterface {
            trade: unsafe { self.plugin.get("trade") }?,
        })
    }
}

Тут придется немного изменить интерфейс плагина, так как libloading добавляет лайфтаймы к функциям:

pub struct PluginInterface<'a> {
    pub trade: Symbol<'a, extern "Rust" fn(prices: &[u32]) -> TradeAction>,
}