-
Notifications
You must be signed in to change notification settings - Fork 23
Developer backdoor to manually replace generated C
Fritz Obermeyer edited this page Jun 13, 2017
·
7 revisions
For development purposes, it is useful to be able to repeatedly modify generated C++ during compileNimble
workflow. The flag nimbleOptions('pauseAfterWritingFiles')
allows one simple way to do this. The compiler will pause and allow any modification. After discussion we wanted a slightly more controllable system.
The following example should work for RCfunctions
(nimbleFunctions without setup code):
library(nimble)
## Go through a regular compilation process
nf1 <- nimbleFunction(
name = 'nf1', ## will become second part of filename
run = function(x = double(1)) {
print("Hello from original nf1")
return(sum(x))
returnType(double(0))
})
## projectName argument will become first part of filename
cnf1 <- compileNimble(nf1, dirName = '.', projectName = 'myProject')
cnf1(rnorm(3))
## Now we have myProject_nf1.[h, cpp, .o] and myProject_nf1_<time-date-stamp>.[so, log]
## Now we wish to modify C++
## An RCfunction is not one-to-one with its compiled version (the way an instance of a nimbleFunction class is)
## We can (1) make a new nimbleFunction with same prototype if that is a useful way to manage work
## or (2) re-use an old one.
## Method (1): Make a new (identical in interface) nimbleFunction
nf1 <- nimbleFunction(
name = 'nf1', ## must match above to be used in C++ names
run = function(x = double(1)) {
returnType(double(0))
})
## set this to use files called myNimbleFunctionWork.[h, cpp]
nimble:::specialHandling(nf1) <- list(filename = 'myNimbleFunctionWork') ## setter
nimble:::specialHandling(nf1) ## getter
## Modify previous files as you wish.
## I will copy and replace the text instead of hand-editing.
## # Customize to say "Hello from modified nf1"
## (i) Modify the message printed
system('sed s/original/modified/g myProject_nf1.cpp > myNimbleFunctionWork.cpp')
## (ii) Modify the #include line
system('sed -i.bak s/myProject_nf1/myNimbleFunctionWork/g myNimbleFunctionWork.cpp')
## (iii) copy the .h
system('cp myProject_nf1.h myNimbleFunctionWork.h')
nimbleOptions(enableSpecialHandling = TRUE)
cnf1modified <- compileNimble(nf1, dirName = '.')
cnf1modified(rnorm(3))
## Method (2)
## re-use nf1 (this will be the second one created above, but it could be the first one)
## set this to use files called myNimbleFunctionWork2.[h, cpp]
nimble:::specialHandling(nf1) <- list(filename = 'myNimbleFunctionWork2') ## setter
nimble:::specialHandling(nf1) ## getter
## Customize to say "Hello from twice-modified nf1"
system('sed s/modified/twice-modified/g myNimbleFunctionWork.cpp > myNimbleFunctionWork2.cpp')
## (ii) Modify the #include line
system('sed -i.bak s/myNimbleFunctionWork/myNimbleFunctionWork2/g myNimbleFunctionWork2.cpp')
## (iii) copy the .h
system('cp myNimbleFunctionWork.h myNimbleFunctionWork2.h')
cnf1twiceModified <- compileNimble(nf1, dirName = '.')
cnf1twiceModified(rnorm(3))
The following example should work for model
s:
## demo of compiling a model via backdoor
library(nimble)
## first generate valid code with our choice of names
m <- nimbleModel(nimbleCode({a ~ dnorm(0,1)}), name = 'myModel')
cm <- compileNimble(m, dirName = '.', projectName = 'myProject')
## next copy or move the files and modify as desired
## I will move them to be sure that the specialHandling mechanism works (does not accidentally use the old files)
system('mv myProject_myModel.cpp custom_myProject_myModel.cpp')
system('mv myProject_myModel.h custom_myProject_myModel.h')
system('mv myProject_myModel_nfCode.cpp custom_myProject_myModel_nfCode.cpp')
system('mv myProject_myModel_nfCode.h custom_myProject_myModel_nfCode.h')
system('rm myProject_myModel*.o')
system('rm myProject_myModel*.so')
## various #includes need to be updated
system('sed -i.bak s/myProject_myModel/custom_myProject_myModel/g custom_myProject_myModel.cpp')
system('sed -i.bak s/myProject_myModel_nfCode/custom_myProject_myModel_nfCode/g custom_myProject_myModel_nfCode.cpp')
## Now reset model and assign special handling
m <- nimbleModel(nimbleCode({a ~ dnorm(0,1)}), name = 'myModel')
nimble:::specialHandling(m) <- list(filename = "custom_myProject_myModel", nfFileName = "custom_myProject_myModel_nfCode")
nimbleOptions(enableSpecialHandling = TRUE)
cm <- compileNimble(m, dirName = '.', projectName = 'myProject')
## As above, this will result in an error, but only after calling the C++ compiler. This allows checking of whether the C++ code could be compiled.