Skip to content

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.

Backdoor for RCfunctions

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))

Backdoor for models

The following example should work for models:

## 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.
Clone this wiki locally