Skip to content

Commit

Permalink
Overhaul readDynLib and writeDynLib
Browse files Browse the repository at this point in the history
Instead of the argument "file", there are now two arguments "bname" and
"directory" (see updated documentation in man/utilities.Rd).

However, we still have remainders of the old DLL locations and names in
after writing and reading, see eddelbuettel#13.
  • Loading branch information
jranke committed Nov 25, 2020
1 parent 327f932 commit f8ac225
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 78 deletions.
77 changes: 31 additions & 46 deletions R/utilities.R
Original file line number Diff line number Diff line change
@@ -1,72 +1,57 @@
## ---------------------------------------------------------------------------
# saving and loading CFunc objects (called write and read as it needs to
# be assigned.
# saving and loading CFunc objects

writeDynLib <- function(x, file) {
writeDynLib <- function(x, bname, directory = ".") {

DLL <- getDynLib(x)

if (is.null(DLL))
stop ("'x' DLL not loaded")
DLLname <- DLL[["path"]]

DLLname <- DLL[["path"]]
if (!file.exists(DLLname))
stop ("'x' does not point to an existing DLL")

# correct extension of filename (dll, so)
dname <- dirname(file)
bname <- unlist(strsplit(basename(file), ".", fixed = TRUE))[1]
# get extension of filename (dll, so)
extension <- unlist(strsplit(basename(DLLname), ".", fixed = TRUE))[2]
file <- paste(dname,bname, extension, sep = ".")
newDLLname <- file.path(directory, paste(bname, extension, sep = "."))

try(dyn.unload(file), silent = TRUE)
try(dyn.unload(newDLLname), silent = TRUE)

file.copy(from = DLLname, to = file, overwrite = TRUE)
file.copy(from = DLLname, to = newDLLname, overwrite = TRUE)

# accessory file with compiled code information (DLL name has changed)
fileCF <- paste(dname,"/",bname, ".Cfunc", sep = "")
attributes(x)$DLL <- file
fileCF <- file.path(directory, paste(bname, "CFunc", sep = "."))
attributes(x)$DLL <- newDLLname

# names of functions in compiled code
if (class(x) == "CFunc")
attributes(x)$fname <- DLL[["name"]]
else
attributes(x)$fname <- names(x)

save(file = fileCF, x)

invisible(fileCF)
}

## ---------------------------------------------------------------------------

readDynLib <- function(file) {

# open all the required files
extension <- unlist(strsplit(basename(file), ".", fixed = TRUE))[2]
readDynLib <- function(bname, directory = ".") {

if (is.na(extension)) {
extension <- "CFunc"
file <- paste(file, extension, sep = ".")
}

if (extension != "CFunc")
stop ("'file' should point to a CFunc object, extension '.CFunc'")
# open all the required files
fileCF <- file.path(directory, paste(bname, "CFunc", sep = "."))

if (!file.exists(file))
stop ("'file' does not exist")
if (!file.exists(fileCF))
stop (fileCF, " does not exist")

CF <- get(load(file = file))
CF <- get(load(file = fileCF))
attrs <- attributes(CF)
DLLname <- attrs$DLL

if (!file.exists(DLLname))
stop ("'file' does not point to valid CFunc object: DLL ", DLLname, " does not exist")

# cleanup <- function(env) {
# unlink(DLLname)
# }
# reg.finalizer(environment(), cleanup, onexit = TRUE)


# load routines in DLL

DLL <- dyn.load(DLLname)
Expand All @@ -82,22 +67,22 @@ readDynLib <- function(file) {
code <- CFi@code
body(CFi)[[2]] <- getNativeSymbolInfo(fn[i], DLL)$address
CF[[i]]@.Data <- CFi
}
attributes(CF) <- attrs
return(CF)
}

attributes(CF) <- attrs
return(CF)
}

setGeneric("code", function(x, ...) standardGeneric("code") )
setMethod( "code", signature( x = "character" ),
function( x, linenumbers = TRUE ){
lines <- strsplit(x, "\n")
if (linenumbers)
for (i in 1:length(lines[[1]])) cat(format(i, width = 3),
for (i in 1:length(lines[[1]])) cat(format(i, width = 3),
": ", lines[[1]][i], "\n", sep = "")
else
for (i in 1:length(lines[[1]])) cat(lines[[1]][i], "\n", sep = "")

} )
setMethod( "code", signature( x = "CFunc" ), function( x, linenumbers = TRUE ) code (x@code, linenumbers))
setMethod( "code", signature( x = "CFuncList" ), function(x, linenumbers = TRUE ) code( x[[1L]], linenumbers ) )
Expand All @@ -107,16 +92,16 @@ setMethod( "code", signature( x = "CFuncList" ), function(x, linenumbers = TRUE
setMethod( "print", signature( x = "CFunc" ),
function( x ){
cat("An object of class 'CFunc'\n")
Dat <- x@.Data
print(Dat)
Dat <- x@.Data
print(Dat)
cat("code:\n")
code(x)
} )

setMethod( "print", signature( x = "CFuncList" ), function(x) {
cat("An object of class 'CFuncList'\n")
for (i in 1:length(x)) {
print(names(x)[i])
print(names(x)[i])
print(x[[i]]@.Data )
cat("\n")
}
Expand Down
79 changes: 47 additions & 32 deletions man/utilities.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,38 @@

\description{
\code{writeDynLib} saves the DLL and the CFunc or CFuncList object as
generated by \link{cfunction}; \code{readDynLib} loads it.
The \code{print} and \code{code} methods respectively print the entire
generated by \link{cfunction}; \code{readDynLib} loads it.

The \code{print} and \code{code} methods respectively print the entire
object or the code parts.
}

\usage{
writeDynLib(x, file)
readDynLib(file)
writeDynLib(x, bname, directory = ".")
readDynLib(bname, directory = ".")
}

\section{Methods}{

\itemize{
\item Method \code{print(x, ...)} prints the entire object \code{x}
\item Method \code{print(x, ...)} prints the entire object \code{x}

\describe{

\item{\code{signature(x = "CFunc")}}{Prints the CFunc object
generated by \code{\link{cfunction}}, including the code that generated it. }
\item{\code{signature(x = "CFunc")}}{Prints the CFunc object
generated by \code{\link{cfunction}}, including the code that generated it. }

\item{\code{signature(x = "CFuncList")}}{Print all CFunc objects
generated by \code{\link{cfunction}}, including the code that generated them. }
generated by \code{\link{cfunction}}, including the code that generated them. }

}

\item Method \code{code(x, linenumbers = TRUE, ...)} prints the code only
\item Method \code{code(x, linenumbers = TRUE, ...)} prints the code only

\describe{

\item{\code{signature(x)}}{The \code{CFunc} or \code{CFuncList} object as generated by
\code{\link{cfunction}}. }
\item{\code{signature(x)}}{The \code{CFunc} or \code{CFuncList} object as generated by
\code{\link{cfunction}}. }

\item{\code{linenumbers}}{If \code{TRUE} all code lines will be numbered. }

Expand All @@ -57,37 +57,46 @@ generated by \code{\link{cfunction}}, including the code that generated them. }
\arguments{

\item{x}{A \code{CFunc} or \code{CFuncList} object as created by \code{\link{cfunction}} to be saved.}

\item{file}{base name of the file to write the object to or to read from.

\item{bname}{base name of the file to write the object to or to read from.
Two files will be saved, one for the shared object or DLL (extension \code{so}
or \code{dll}) and one that holds the \code{CFunc} or \code{CFuncList} specification, without
the function address (extension \code{CFunc}).}

\item{directory}{name of the directory to write to.
Two files will be saved, one for the shared object or DLL (extension \code{so}
or \code{DLL}) and one that holds the \code{CFunc} or \code{CFuncList} specification, without
or \code{dll}) and one that holds the \code{CFunc} or \code{CFuncList} specification, without
the function address (extension \code{CFunc}).}

}

\value{

Function \code{readDynLib} returns a \code{CFunc} or \code{CFuncList} object.

Function \code{writeDynLib} returns the name of the \code{.CFunc} file that
was created.
}

\details{

Both the CFunc or CFuncList object and the shared object or DLL are saved,
in two files; the first has extension \code{CFunc}; the second \code{so} or
\code{DLL}, depending on the operating system used.
When reading, both files are loaded, and the compiled function address
\code{dll}, depending on the operating system used.

When reading, both files are loaded, and the compiled function address(es)
added to the object.

}

\note{

\itemize{
\item The code of a \code{CFunc} or \code{CFuncList} object \code{x} can be extracted
(rather than printed), using:
\item The code of a \code{CFunc} or \code{CFuncList} object \code{x} can be extracted
(rather than printed), using:

\code{x@code}.
\item To write the code to a file (here called \code{"fn"}),
\code{x@code}.
\item To write the code to a file (here called \code{"fn"}),
without the new-line character \code{"\n"}:

\code{write (strsplit(x, "\n")[[1]], file = "fn")}
Expand All @@ -96,7 +105,7 @@ without the new-line character \code{"\n"}:
}

\seealso{ \code{
\link{getDynLib}
\link{getDynLib}
}}

\examples{
Expand All @@ -109,18 +118,24 @@ code <- "
do 1 i=1, n(1)
1 x(i) = x(i)**3
"
cubefn <- cfunction(signature(n="integer", x="numeric"), code, convention=".Fortran")
cubefn <- cfunction(signature(n="integer", x="numeric"), code,
convention=".Fortran")
code(cubefn)

cubefn(n, x)$x

\dontrun{
fname <- tempfile()
writeDynLib(cubefn, file = fname)
# load and assign different name to object
cfn <- readDynLib(fname)
print(cfn)
cfn(2, 1:2)
writeDynLib(cubefn, bname = "testname", directory = tempdir())
dir(tempdir())
# load and assign different name to object
cfn <- readDynLib("testname", directory = tempdir())

# We still have remainders of the original locations and names:
getDynLib(cfn)
environment(cfn@.Data)$f
environment(cfn@.Data)$libLFile
# although the DLL attribute points to the new DLL
attributes(cfn)
}

}
Expand Down

0 comments on commit f8ac225

Please sign in to comment.