Руководство

Подробное руководство по эффективному использованию плагина, которое поможет вам максимально использовать его возможности и функциональность.

Динамический вызов C/C++ функций на любом языке

Это плагин, который обеспечивает динамический вызов C-функций во время выполнения. Это особенно полезно, когда сигнатуры функций неизвестны во время компиляции, например, при взаимодействии с плагинами, внешними библиотеками или скриптовыми системами. Он основан на мощной библиотеке dyncall и разработан для легкой интеграции в ваш плагин или скриптовую среду.

Это руководство охватывает:

  • Инициализацию виртуальной машины вызовов
  • Настройку вызова функции
  • Передачу аргументов
  • Вызов функции
  • Обработку возвращаемых значений

Пошаговый пример

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

double add(double a, double b) {
    return a + b;
}

1. Получение указателя на функцию

На практике это может быть получено из разделяемой библиотеки или плагина:

double (*add_ptr)(double, double) = &add;
void* func_ptr = (void*) add_ptr;

2. Создание виртуальной машины вызовов

Вам нужно выделить DCCallVM и установить соглашение о вызовах:

auto* vm = dyncall::NewVM(4096);            // 4КБ пространства стека
dyncall::Mode(vm, dyncall::Mode::Default);  // Использовать стандартное C соглашение о вызовах

3. Передача аргументов

Передавайте аргументы в правильном порядке, используя типо-специфичные функции dyncall::Arg*:

dyncall::Reset(vm);          // Сброс состояния аргументов
dyncall::ArgDouble(vm, 2.0); // Первый аргумент
dyncall::ArgDouble(vm, 3.5); // Второй аргумент

4. Вызов функции

Вызовите функцию, используя правильный вариант dyncall::Call*:

double result = dyncall::CallDouble(vm, func_ptr);
printf("Result: %f\n", result); // Result: 5.5

Другие типы аргументов

Вот сводка того, как передавать различные типы аргументов:

Тип CФункция передачиФункция вызова
intdyncall::ArgInt(vm, x)dyncall::CallInt(...)
longdyncall::ArgLong(vm,x)dyncall::CallLong(...)
floatdyncall::ArgFloat(...)dyncall::CallFloat(...)
doubledyncall::ArgDouble(...)dyncall::CallDouble(...)
void*dyncall::ArgPointer(...)dyncall::CallPointer(...)
char*dyncall::ArgString(...)dyncall::CallString(...)

Очистка

Освободите виртуальную машину вызовов после использования:

dyncall::Free(vm);

Расширенное: Вызов из низкоуровневого языка

Предположим, функция такая:

int foo(const char* msg, float x) {
    printf("Called: %s %.2f\n", msg, x);
    return 42;
}

Тогда динамический вызов будет:

void* fptr = (void*) &foo;
auto* vm = dyncall::NewCallVM(4096);
dyncall::Mode(vm, dyncall::Mode::Default);
dyncall::Reset(vm);
dyncall::ArgString(vm, "Hello");
dyncall::ArgFloat(vm, 3.14f);
int result = dyncall::CallInt(vm, fptr);
printf("Returned: %d\n", result);
dyncall::Free(vm);

Расширенное: Вызов из высокоуровневого языка

Предположим, функция такая:

CSWeaponData* GetCSWeaponDataFromKey(int id, const char* msg);

Тогда динамический вызов будет:

main_plugins_dir = Path(__file__).resolve().parent.parent.parent
config_path = str(main_plugins_dir / "s2sdk" / "gamedata" / "s2sdk.games.txt")
game_config = s2.LoadGameConfigFile(config_path)
func_addr = s2.GetGameConfigMemSig(game_config, "GetCSWeaponDataFromKey")
vm = dc.NewVM(4096)
dc.Mode(vm, dc.Mode.Default)
dc.Reset(vm)
dc.ArgInt32(vm, -1)
dc.ArgString(vm, "weapon_ak47")
weapon_data = dc.CallPointer(vm, func_addr)
dc.Free(vm)

Пример взят отсюда

Формат сигнатуры (используется в dyncallback, dynload)

Строки сигнатур следуют этому формату (из dyncall_signature.h):

  • Префикс типа возврата + упорядоченные типы аргументов
  • Пример: "ifp" = возвращает int, принимает float и void*
КодТип
vvoid
iint
llong
ffloat
ddouble
ppointer
sstring

Примечания и безопасность

  • Порядок аргументов имеет значение: они должны точно соответствовать сигнатуре функции.
  • Если функция использует stdcall, fastcall или другие, установите dyncall::Mode() соответственно (например, dyncall::Mode::StdCall).
  • Используйте dyncall::Reset() перед каждым вызовом для повторного использования VM.
  • Поведение не определено, если передан неправильный тип.

Минимальный полный пример

#include <stdio.h>
#include <dyncall.hpp>

double add(double a, double b) {
    return a + b;
}

int main() {
    auto* vm = dyncall::NewCallVM(4096);
    dyncall::Mode(vm, dyncall::Mode::Default);

    dyncall::Reset(vm);
    dyncall::ArgDouble(vm, 5.0);
    dyncall::ArgDouble(vm, 7.0);
    double result = dyncall::CallDouble(vm, (void*) &add);

    printf("Result: %f\n", result);
    dyncall::Free(vm);
    return 0;
}

Смотрите также