---
title: "Linking External Libraries"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Linking External Libraries}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
tcc_compile <- Rtinycc::tcc_compile
tcc_link <- Rtinycc::tcc_link
is_windows <- .Platform$OS.type == "windows"
find_system_math_library <- function() {
  if (is_windows) {
    return(NULL)
  }

  sysname <- as.character(unname(Sys.info()[["sysname"]]))
  paths <- unique(Rtinycc:::tcc_library_search_paths(sysname))
  # On Linux prefer the unversioned `libm.so`, which on glibc systems is a
  # GNU ld script that pulls in libm.so.6 plus libmvec.so.1; TinyCC handles
  # those scripts. Fall back to the SONAME-versioned file only if the dev
  # symlink is absent (some minimal containers).
  patterns <- switch(
    sysname,
    Linux = c("^libm\\.so$", "^libm\\.so(\\.[0-9]+)+$"),
    Darwin = c("^libSystem\\.B\\.dylib$", "^libm\\.dylib$"),
    character(0)
  )

  for (pattern in patterns) {
    for (path in paths) {
      if (!dir.exists(path)) {
        next
      }
      candidates <- list.files(
        path,
        pattern = pattern,
        full.names = TRUE
      )
      if (length(candidates) > 0L) {
        return(candidates[[1]])
      }
    }
  }

  NULL
}
math_lib_path <- find_system_math_library()
math_link_ok <- !is.null(math_lib_path) && tryCatch(
  {
    Rtinycc::tcc_link(
      math_lib_path,
      symbols = list(sqrt = list(args = list("f64"), returns = "f64"))
    )
    TRUE
  },
  error = function(e) FALSE
)
```

`tcc_link()` is the external-library counterpart to `tcc_compile()`.
Instead of compiling only the C source you provide, it generates wrappers for
symbols that already exist in a shared library and links those wrappers with
TinyCC.

The important contract is:

- you describe the C signatures you want to call from R
- `Rtinycc` generates the conversion layer
- TinyCC links that conversion layer against the target shared library

## What `tcc_link()` Is For

Use `tcc_link()` when the implementation already exists elsewhere and you want
an R-facing wrapper with the same calling style as `tcc_compile()`.

Typical uses are:

- binding a few functions from a system library
- wrapping part of a third-party C API
- compiling a small helper function next to external symbols

## A Minimal Linking Example

On Unix-like systems, the math library is a simple example because common
functions such as `sqrt()` and `cos()` are usually resolved from the system
math runtime. For robustness, this vignette resolves an actual runtime library
file instead of relying on a short name. On Linux that is typically a versioned
`libm.so.*` file, while on macOS the resolver prefers `libSystem.B.dylib`.

```{r, eval = !is_windows && math_link_ok}
math <- tcc_link(
  math_lib_path,
  symbols = list(
    sqrt = list(args = list("f64"), returns = "f64"),
    cos = list(args = list("f64"), returns = "f64")
  )
)

math$sqrt(25)
math$cos(0)
```

On Windows, the same family of CRT functions is typically provided through the
UCRT. In `Rtinycc`, that runtime detail is handled by the bundled TinyCC setup
and `configure.win`; user-facing code should generally think in terms of “CRT
functions are available” rather than depending on a specific historical import
library name.

## Adding Helper Code Beside External Symbols

You can include a small amount of helper code in the same TinyCC unit while
still linking against an external library:

```{r, eval = !is_windows && math_link_ok}
math_helpers <- tcc_link(
  math_lib_path,
  symbols = list(
    sqrt = list(args = list("f64"), returns = "f64"),
    hypot2_checked = list(args = list("f64", "f64"), returns = "f64")
  ),
  user_code = "
    #include <math.h>

    double hypot2_checked(double x, double y) {
      if (x < 0 || y < 0) {
        return NAN;
      }
      return sqrt(x * x + y * y);
    }
  "
)

math_helpers$hypot2_checked(3, 4)
is.nan(math_helpers$hypot2_checked(-1, 4))
```

This is often enough for:

- argument checks
- small adapter functions
- convenience wrappers around an existing library API

## Linking by Name or by Path

`tcc_link()` accepts either:

- a short library name such as `"m"` or `"sqlite3"`
- a full path to a shared library file

When given a full path, `Rtinycc` adds the containing directory and asks TinyCC
to link that exact file name. This matters for versioned runtime libraries such
as `libm.so.6`, which are not interchangeable with a generic development
symlink such as `libm.so` on every Linux distribution. When given a short name,
TinyCC resolves it through its configured library search paths.

This means the R-side binding declaration stays the same even when deployment
changes from “use the system copy” to “use this exact shared object”.

## Relationship to `tcc_compile()`

Use `tcc_compile()` when your primary artifact is new C code shipped from R.
Use `tcc_link()` when the primary implementation already lives in a shared
library and you want `Rtinycc` to generate the wrapper layer around it.
