Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix pragma syntax #15

Merged
merged 9 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ jobs:
strategy:
fail-fast: false
matrix:
branch: [version-1-6, devel]
branch: [version-1-6, version-2-0, devel]
target: [linux] #, macos, windows]
include:
- target: linux
builder: ubuntu-18.04
builder: ubuntu-latest
# - target: macos
# builder: macos-10.15
# - target: windows
Expand Down
5 changes: 5 additions & 0 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,11 @@ This file can now be sourced from the R interpreter (using the
=source= function) or in an R script and then =addNumbers= is usable
and will execute the compiled Nim code!

Note that the autogeneration logic assumes the shared library and the
generated R script will live in the same directory. If you wish to
move one, you might have to adjust the paths that perform the
~dyn.load~ command!

** Trying it out

To try out the functionality of calling R from Nim, you need to meet a
Expand Down
9 changes: 9 additions & 0 deletions changelog.org
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
* v0.1.6
- fix syntax used for ~{.union.}~ pragma in wrapped R types, old
syntax not valid on modern Nim
- fix up path logic for ~dyn.load~ in autogenerated R scripts. It now
uses the path to the R script as the base directory for the shared
library. This means the shared library must be in the same
directory. Feel free to update the path as needed in the
autogenerated R script by hand of course.
- clarify raw vector test case and fix vector tests
* v0.1.5
- handle Nim procedures that return void when using
={.exportR.}=. This is done by rewriting to a =SEXP= return type,
Expand Down
4 changes: 2 additions & 2 deletions src/rnim/Rinternals_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type
expr*: ptr SEXPREC
env*: ptr SEXPREC

INNER_C_UNION_Rinternals_types_69* {.bycopy.} = object {.union.}
INNER_C_UNION_Rinternals_types_69* {.bycopy, union.} = object
primsxp*: primsxp_struct
symsxp*: symsxp_struct
listsxp*: listsxp_struct
Expand All @@ -72,7 +72,7 @@ type
vecsxp*: vecsxp_struct

VECSEXP* = ptr VECTOR_SEXPREC
SEXPREC_ALIGN* {.bycopy.} = object {.union.}
SEXPREC_ALIGN* {.bycopy, union.} = object
s*: VECTOR_SEXPREC
align*: cdouble

Expand Down
25 changes: 20 additions & 5 deletions src/rnim/auto_wrapper.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,22 @@ type
file*: string # file that contains these procedures
procs*: seq[ExportProc]

const dynLoadTmpl = """dyn.load("$#")"""
const getScriptPathTmpl = """
# Get the directory of the currently running script. We need it, because
# if the auto generated R file is sourced from a different directory (and assuming the shared
# library is next to the autogen'd R file) the R interpreter won't find it.
# So we need to fix the path based on the location of the autogen'd script.
scriptDir <- dirname(sys.frame(1)$ofile)
# This is a bit of a hack, see:
# https://stackoverflow.com/a/16046056https://stackoverflow.com/a/16046056
# Otherwise we could depend on the `here` library...
"""
const adjustPathTmpl = """
# Construct the path to another script in the same directory (or a subdirectory)
libPath <- file.path(scriptDir, "$#")
"""
const dynLoadTmpl = """dyn.load($#)"""
const fnTmpl = """

$name <- function($args) {
$body
}
Expand Down Expand Up @@ -61,7 +74,11 @@ proc argsToSeq*(expProc: ExportProc): seq[string] =
proc serialize*(pt: NimRModule) {.compileTime.} =
## writes an R wrapper for the exported Nim procedures based on the given
## procedure table
var res = dynLoadTmpl % ("lib" & pt.file & ".so")
let libName = "lib" & pt.file & ".so"
var res = getScriptPathTmpl # get path of autogen'd R script
res.add adjustPathTmpl % libName # produces a `libPath` local variable
res.add dynLoadTmpl % "libPath" # add the dyn.load call
res.add "\n"
for p in pt.procs:
let args = p.argsToSeq.join(", ")
let body = if p.isVoid:
Expand All @@ -78,8 +95,6 @@ proc serialize*(pt: NimRModule) {.compileTime.} =
proc assignToCtTable*(expProc: ExportProc) =
## Assigns the given procedure to the CT table and serializes an R file
## from the procedures
## TODO: Should we use the full path to always emit next to the Nim file or always
## local?
let (_, filename, _) = expProc.origFn.lineInfoObj.filename.extractFilename.splitFile
var pt = moduleTable.getOrDefault(filename, NimRModule(file: filename))
pt.procs.add(expProc)
Expand Down
11 changes: 6 additions & 5 deletions tests/tCallNimFromR.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
dyn.load("tests/libtNimFromR.so")

source('tNimFromR.R')
# Source the autogenerated R file, which loads the dynamic
# library and wraps them as R functions of the same name.
# IMPORTANT: The path here is based on the location of where the
# R interpreter is run!
source('tests/tNimFromR.R')

check <- function(arg) {
if (!all(arg)){
Expand Down Expand Up @@ -31,6 +33,5 @@ check(y == yOrig + 1.0)

printVec(y)


checkSexp(x)
checkSexpRaw(x)
checkSexpRaw(x)
62 changes: 31 additions & 31 deletions tests/tNimFromR.nim
Original file line number Diff line number Diff line change
Expand Up @@ -56,44 +56,44 @@ proc modifyVec*(v: SEXP) {.exportR.} =

proc checkVector[T](v: NumericVector[T]) =
## checks the given vector to be our expectation
test "Checking vector of type " & $typeof(v):
let exp = @[1, 2, 3, 4, 5].mapIt(it.T)
check v.len == exp.len
for i in 0 ..< v.len:
check v[i] == exp[i]
let exp = @[1, 2, 3, 4, 5].mapIt(it.T)
check v.len == exp.len
for i in 0 ..< v.len:
check v[i] == exp[i]

proc checkSexp*(s: SEXP) {.exportR.} =
suite "NumericVector tests":
proc checkType[T](s: SEXP) =
let nv = initNumericVector[T](s)
checkVector(nv)
checkType[int32](s)
checkType[cint](s)
checkType[int](s)
checkType[int64](s)
checkType[float](s)
checkType[float32](s)
checkType[cdouble](s)
checkType[cfloat](s)
#checkType[uint8](s)
proc checkType[T](s: SEXP) =
let nv = initNumericVector[T](s)
checkVector(nv)
checkType[int32](s)
checkType[cint](s)
checkType[int](s)
checkType[int64](s)
checkType[float](s)
checkType[float32](s)
checkType[cdouble](s)
checkType[cfloat](s)
#checkType[uint8](s)

proc checkVector[T](v: RawVector[T]) =
## checks the given vector to be our expectation
test "Checking vector of type " & $typeof(v):
let exp = @[1, 2, 3, 4, 5].mapIt(it.T)
check v.len == exp.len
for i in 0 ..< v.len:
check v[i] == exp[i]
let exp = @[1, 2, 3, 4, 5].mapIt(it.T)
check v.len == exp.len
for i in 0 ..< v.len:
check v[i] == exp[i]

proc checkSexpRaw*(s: SEXP) {.exportR.} =
suite "RawVector tests":
proc checkType[T](s: SEXP) =
let nv = initRawVector[T](s)
checkVector(nv)
checkType[int32](s)
checkType[cint](s)
checkType[float](s)
checkType[cdouble](s)
proc checkType[T](s: SEXP) =
let nv = initRawVector[T](s)
checkVector(nv)
checkType[int32](s)
checkType[cint](s)
## The following for example do *NOT* work, because the data type and sizes have to match for a
## raw vector!
## A `RawVector` is just casting the data!
#checkType[int](s)
#checkType[float](s)
#checkType[cdouble](s)


#[
Expand Down
Loading