Guide

A detailed guide on how to use the plugin effectively, ensuring you maximize its features and functionality for your needs.

Calling C/C++ Functions Dynamically in Any Language

It is a plugin that enables dynamic invocation of C functions at runtime. This is particularly useful when the function signatures are not known at compile-time, such as when interfacing with plugins, foreign binaries, or scripting systems. It’s based on the powerful dyncall backend and is designed to be easily integrated into your plugin or scripting environment.

This guide covers:

  • Initializing a call VM
  • Setting up a function call
  • Passing arguments
  • Invoking the function
  • Handling return values

Step-by-Step Example

Let's say you want to dynamically call this function:

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

1. Get Function Pointer

In practice, this might come from a shared library or plugin:

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

2. Create a Call VM

You need to allocate a DCCallVM and set its calling convention:

auto* vm = dyncall::NewVM(4096);            // 4KB stack space
dyncall::Mode(vm, dyncall::Mode::Default);  // Use default C calling convention

3. Push Arguments

Push arguments in the correct order using type-specific dyncall::Arg* functions:

dyncall::Reset(vm);          // Reset argument state
dyncall::ArgDouble(vm, 2.0); // First argument
dyncall::ArgDouble(vm, 3.5); // Second argument

4. Call Function

Call the function using the correct dyncall::Call* variant:

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

Other Argument Types

Here’s a summary of how to push different argument types:

C TypePush FunctionCall Function
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(...)

Cleanup

Free the call VM after use:

dyncall::Free(vm);

Advanced: Calling from Low-Level Language

Suppose the function is:

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

Then dynamic call would be:

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);

Advanced: Calling from High-Level Language

Suppose the function is:

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

Then dynamic call would be:

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)

Example taken from here

Signature Format (Used in dyncallback, dynload)

Signature strings follow this format (from dyncall_signature.h):

  • Return type prefix + ordered argument types
  • Example: "ifp" = returns int, takes float and void*
CodeType
vvoid
iint
llong
ffloat
ddouble
ppointer
sstring

Notes & Safety

  • Argument order matters: they must match the function signature exactly.
  • If the function uses stdcall, fastcall, or others, set dyncall::Mode() accordingly (e.g., dyncall::Mode::StdCall).
  • Use dyncall::Reset() before each call to reuse the VM.
  • Behavior is undefined if the wrong type is passed.

Minimal Complete Example

#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;
}

See Also