Экспорт функций

Руководство по экспорту функций из вашего плагина на Rust для использования другими языковыми модулями в Plugify.

В экосистеме Plugify плагины на Rust могут экспортировать функции, делая их доступными для других плагинов. Это руководство объясняет, как экспортировать функции в Rust, и содержит примеры, которые помогут вам бесшовно интегрировать ваши плагины.

Базовое сопоставление типов

В следующей таблице показано, как типы предоставляются в API Rust:

Тип C++Тип RustПсевдоним PlugifyПоддержка ref?
void()void
boolboolbool
chari8char8
char16_tu16char16
int8_ti8int8
int16_ti16int16
int32_ti32int32
int64_ti64int64
uint8_tu8uint8
uint16_tu16uint16
uint32_tu32uint32
uint64_tu64uint64
uintptr_tusizeptr64
uintptr_tusizeptr32
floatf32float
doublef64double
void**const c_voidfunction
plg::stringStrstring
plg::anyAnyany
plg::vector<bool>Arr<bool>bool[]
plg::vector<char>Arr<i8>char8[]
plg::vector<char16_t>Arr<u16>char16[]
plg::vector<int8_t>Arr<i8>int8[]
plg::vector<int16_t>Arr<i16>int16[]
plg::vector<int32_t>Arr<i32>int32[]
plg::vector<int64_t>Arr<i64>int64[]
plg::vector<uint8_t>Arr<u8>uint8[]
plg::vector<uint16_t>Arr<u16>uint16[]
plg::vector<uint32_t>Arr<u32>uint32[]
plg::vector<uint64_t>Arr<u64>uint64[]
plg::vector<uintptr_t>Arr<usize>ptr64[]
plg::vector<uintptr_t>Arr<usize>ptr32[]
plg::vector<float>Arr<f32>float[]
plg::vector<double>Arr<f64>double[]
plg::vector<plg::string>Arr<Str>string[]
plg::vector<plg::any>Arr<Any>any[]
plg::vector<plg::vec2>Arr<Vec2>vec2[]
plg::vector<plg::vec3>Arr<Vec3>vec3[]
plg::vector<plg::vec4>Arr<Vec4>vec4[]
plg::vector<plg::mat4x4>Arr<Mat4x4>mat4x4[]
plg::vec2Vec2vec2
plg::vec3Vec3vec3
plg::vec4Vec4vec4
plg::mat4x4Mat4x4mat4x4

Экспорт функций в Rust

Чтобы экспортировать функцию в плагине на Rust, необходимо убедиться, что она видима для других плагинов. Это делается с помощью атрибута #[unsafe(no_mangle)], предоставляемого крейтом Plugify.

Ключевые моменты

  • No-mangle: макрос автоматически применяет #[no_mangle], чтобы предотвратить искажение имён.
  • Extern "C": функции экспортируются с C-компоновкой для межъязыковой совместимости.
  • Типы параметров и возвращаемых значений: используйте нативные типы Plugify для бесшовной интеграции.

Базовый пример

Вот простой пример экспорта функции в плагине на Rust:

Определение функции

src/lib.rs
use plugify::*;

#[unsafe(no_mangle)]
pub extern "C" fn add_numbers(a: i32, b: i32) -> i32 {
    a + b
}

Атрибут #[unsafe(no_mangle)] автоматически обеспечивает:

  • Экспорт функции с C-компоновкой
  • Предотвращение искажения имён
  • Видимость функции для других плагинов

Пример манифеста плагина

Все экспортируемые функции должны быть описаны в файле манифеста плагина в разделе methods. Вот пример манифеста для плагина, экспортирующего функцию add_numbers:

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "language": "rust",
  "methods": [
    {
      "name": "AddNumbers",
      "funcName": "add_numbers",
      "paramTypes": [
        {
          "type": "int32",
          "name": "a"
        },
        {
          "type": "int32",
          "name": "b"
        }
      ],
      "retType": {
        "type": "int32"
      }
    }
  ]
}

Продвинутый пример: экспорт сложных функций

Вот пример экспорта функции со сложными типами параметров и возвращаемого значения:

Определение функции

src/lib.rs
use plugify::*;

#[unsafe(no_mangle)]
pub extern "C" fn process_data(data: &Arr<f64>, prefix: &Str) -> Arr<Str> {
    let collection: Vec<String> = data.iter()
        .map(|value| format!("{}{}", prefix, value))
        .collect();
    Arr::from(collection)
}

Манифест плагина

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "language": "rust",
  "methods": [
    {
      "name": "ProcessData",
      "funcName": "process_data",
      "paramTypes": [
        {
          "type": "double[]",
          "name": "data"
        },
        {
          "type": "string",
          "name": "prefix"
        }
      ],
      "retType": {
        "type": "string[]"
      }
    }
  ]
}

Экспорт функций со ссылками

Rust позволяет экспортировать функции, принимающие изменяемые ссылки:

Определение функции

src/lib.rs
use plugify::*;

#[unsafe(no_mangle)]
pub extern "C" fn increment_value(value: &mut i32) {
    *value += 1;
}

Манифест плагина

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "language": "rust",
  "methods": [
    {
      "name": "IncrementValue",
      "funcName": "increment_value",
      "paramTypes": [
        {
          "type": "int32",
          "name": "value",
          "ref": true
        }
      ],
      "retType": {
        "type": "void"
      }
    }
  ]
}

Обработка обратных вызовов

Plugify позволяет экспортировать функции, принимающие обратные вызовы в качестве параметров:

Определение функции

src/lib.rs
use plugify::*;

type CallbackFunction = extern "C" fn(i32, &Str) -> Str;

#[unsafe(no_mangle)]
pub extern "C" fn execute_with_callback(value: i32, input: &Str, callback: CallbackFunction) {
    let result = callback(value, input);
    println!("Callback result: {}", result);
}

Манифест плагина

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "language": "rust",
  "methods": [
    {
      "name": "ExecuteWithCallback",
      "funcName": "execute_with_callback",
      "paramTypes": [
        {
          "type": "int32",
          "name": "value"
        },
        {
          "type": "string",
          "name": "input"
        },
        {
          "type": "function",
          "name": "callback",
          "prototype": {
            "name": "ExampleCallback",
            "funcName": "ExampleCallback",
            "paramTypes": [
              {
                "type": "int32",
                "name": "value"
              },
              {
                "type": "string",
                "name": "input"
              }
            ],
            "retType": {
              "type": "string"
            }
          }
        }
      ],
      "retType": {
        "type": "void"
      }
    }
  ]
}

Работа с перечислениями

Перечисления Rust можно экспортировать в Plugify:

Определение перечисления

src/lib.rs
use plugify::*;

#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub enum LogLevel {
    Debug = 0,
    Info = 1,
    Warning = 2,
    Error = 3,
}
vector_enum_traits!(LogLevel, u8);

#[unsafe(no_mangle)]
pub extern "C" fn log_message(level: LogLevel, message: &Str) {
    match level {
        LogLevel::Debug => println!("[DEBUG] {}", message),
        LogLevel::Info => println!("[INFO] {}", message),
        LogLevel::Warning => println!("[WARNING] {}", message),
        LogLevel::Error => println!("[ERROR] {}", message),
    }
}

Манифест плагина с перечислением

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "language": "rust",
  "methods": [
    {
      "name": "LogMessage",
      "funcName": "log_message",
      "paramTypes": [
        {
          "type": "uint8",
          "name": "level",
          "enum": {
            "name": "LogLevel",
            "values": [
              { "name": "Debug", "value": 0 },
              { "name": "Info", "value": 1 },
              { "name": "Warning", "value": 2 },
              { "name": "Error", "value": 3 }
            ]
          }
        },
        {
          "type": "string",
          "name": "message"
        }
      ],
      "retType": {
        "type": "void"
      }
    }
  ]
}

Рекомендации

  1. Используйте unsafe вместе с extern "C": всегда применяйте макрос #[unsafe(no_mangle)] для экспорта функций.
  2. Следуйте соглашениям о типах: используйте нативные типы Plugify для параметров и возвращаемых значений.
  3. Документируйте свои функции: используйте doc-комментарии Rust (///) для документирования экспортируемых функций.
  4. Поддерживайте манифест в актуальном состоянии: убедитесь, что манифест точно отражает ваши экспортируемые функции.
  5. Используйте repr для перечислений: всегда указывайте #[repr(...)] для перечислений, используемых в экспортируемых функциях.
  6. Тщательно тестируйте: проверяйте, что экспортируемые функции работают так, как ожидается, при вызове из других плагинов.
  7. Корректно обрабатывайте ошибки: рассмотрите использование типов Result<T, E> и соответствующее преобразование ошибок.

Обработка ошибок

При экспорте функций, которые могут завершиться неудачей, рассмотрите следующий подход:

src/lib.rs
use plugify::*;

#[unsafe(no_mangle)]
pub extern "C" fn divide_numbers(a: f64, b: f64) -> Var {
    if b == 0.0 {
        Var::from(Any::String("Division by zero"))
    } else {
        Var::from(Any::Float(a / b))
    }
}

Заключение

Экспорт функций в плагинах на Rust выполняется просто, если следовать соглашениям и рекомендациям Plugify. Используя атрибут #[unsafe(no_mangle)], придерживаясь соглашений о типах и поддерживая актуальность файлов манифеста, вы сможете создавать надёжные и совместимые плагины. Типобезопасность и система владения Rust дают дополнительные гарантии, помогающие предотвратить распространённые ошибки при разработке плагинов.