Skip to main content

Overview

The caller API provides a lightweight, zero-overhead abstraction for invoking functions located at arbitrary memory addresses. It offers strongly-typed function pointer wrappers that enable safe invocation of dynamically resolved symbols, JIT-compiled code, or runtime-patched functions.

Types

caller_t<Sig>

A strongly-typed wrapper around a raw function pointer.
template<class Ret, class... Args>
struct caller_t<Ret(Args...)>
{
    using fn_t = Ret (*)(Args...);
    fn_t fn;

    inline constexpr Ret operator()(Args... args) const noexcept;
    [[nodiscard]] constexpr explicit operator bool() const noexcept;
};
fn
Ret (*)(Args...)
Raw function pointer to the target function. No ownership semantics or lifetime guarantees.
operator()
Ret
Invokes the stored function pointer with the provided arguments. Returns the result of the function call.Preconditions:
  • fn != nullptr
  • The address is valid and correctly aligned
  • The signature matches the actual function
Note: Undefined behavior occurs if preconditions are violated.
operator bool
bool
Returns true if the function pointer is non-null, false otherwise.

Functions

caller<Sig>()

Factory function that creates a caller_t<Sig> from any address-like value.
template<class Sig>
inline constexpr auto caller(address_like auto addr) noexcept;
Sig
template parameter
Function signature (e.g., int(int, int) or void(const char*))
addr
address_like
Address of the function. Accepts:
  • Raw pointers (int (*)(int))
  • std::uintptr_t / std::intptr_t
  • stx::va_t (virtual address)
Note: Does not accept rva_t or offset_t.
return
caller_t<Sig>
A callable wrapper around the function pointer at the specified address.

Usage Examples

Calling a Known Function

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

auto c = stx::caller<int(int, int)>(&add);
int result = c(10, 20);  // 30

Using with Virtual Address

stx::va_t address{ reinterpret_cast<stx::uptr>(&add) };
auto c = stx::caller<int(int, int)>(address);
int result = c(3, 4);  // 7

Using with Raw Integer Address

std::uintptr_t raw = reinterpret_cast<std::uintptr_t>(&add);
auto c = stx::caller<int(int, int)>(raw);
int result = c(5, 6);  // 11

Null Safety Check

auto c = stx::caller<int(int, int)>(std::uintptr_t{0});

if (!c)
{
    // function pointer is null - safe to detect
}
else
{
    int result = c(1, 2);
}

Calling an External Symbol

Example scenario: resolved symbol from dynamic loader.
stx::uptr symbol_addr = resolve_symbol("some_function");
auto fn = stx::caller<void(int)>(symbol_addr);

if (fn)
{
    fn(42);
}

Design Characteristics

PropertyDescription
Zero abstraction overheadDirect wrapper around raw function pointer
Strong signature bindingSignature enforced at compile time
Explicit validity checkoperator bool() verifies non-null
constexpr friendlyFully usable in constant evaluation contexts
noexcept call operatorAssumes target function does not violate contract

Safety Considerations

This utility performs no runtime validation beyond null checks. The following conditions lead to undefined behavior:
  • Signature mismatch between Sig and actual function
  • Invalid memory address
  • Calling convention mismatch
  • Incorrect alignment
  • Attempting to use with non-static member functions (not supported)

Intended Use Cases

  • Manual symbol resolution
  • Runtime patching and hooking systems
  • Loader implementations
  • Low-level tooling and debugging utilities
  • JIT integration layers
  • Dynamic library function invocation
  • address_like concept from core.hpp
  • normalize_addr() for address normalization
  • va_t for virtual address representation

Build docs developers (and LLMs) love