Skip to content

Commit 8d951ab

Browse files
authored
automatically include other nClass and nFunction units in nCompile calls (#94)
* nCompile automated inclusion (optionally) of needed units * fix nFunctions method gather_neeeded_nFunctions * clean up and finish gathering of needed units * add test-nCompile_auto_include and fixed issues along the way * fix new test-nCompile_auto_include
1 parent 13a9e5e commit 8d951ab

14 files changed

Lines changed: 873 additions & 262 deletions

nCompiler/R/NC.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ nClass <- function(classname,
9090
# All inheritance provided by compileInfo$inherit should include any
9191
# accessor specifier, typically "public", e.g. "public some_class".
9292
# Similarly, template arguments (include CRTP) should be in the text explicitly.
93+
# needed_units: list of needed nClasses and nFunctions to include, by name or object
9394
#
9495
# constructor(s) and destructor:
9596
#

nCompiler/R/NC_Compile.R

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ nCompile_nClass <- function(NC,
4646
control
4747
)
4848
is_predefined <- !isFALSE(NCinternals(NC)$predefined)
49+
gather_needed_units <- isTRUE(controlFull$always_include_units)
50+
needed_units <- list(needed_nClasses = list(),
51+
needed_nFunctions = list())
52+
allow_write_predefined <- FALSE
4953
if(is_predefined) {
5054
predefined_dir <- NCinternals(NC)$predefined
5155
# predefined can be character, quoted expression, or function.
@@ -62,14 +66,18 @@ nCompile_nClass <- function(NC,
6266
stop("There is a predefined nClass whose predefined field is not (and does not evaluate to) character. ",
6367
"It should give the directory path of the predefined nClass. ",
6468
"The classname argument to nClass gives the base for filenames in that directory.")
65-
regular_filename <- NCinternals(NC)$cpp_classname
69+
regular_filename <- NCinternals(NC)$cpp_classname
70+
if(gather_needed_units)
71+
needed_units <- nCompile_process_manual_needed_units(NCinternals(NC),
72+
NC$parent_env, isNC = TRUE)
73+
allow_write_predefined <- !isTRUE(compileInfo$auto_included)
6674
}
6775
if(is_predefined && isFALSE(controlFull$generate_predefined)) {
6876
RcppPacket <- loadRcppPacket(predefined_dir, regular_filename)
6977
cppDef <- cppRcppPacket$new(RcppPacket = RcppPacket)
7078
cppDef$externalCppDefs <- c(cppDef$externalCppDefs,
7179
get_R_interface_cppDef()) #might not be needed, but doesn't hurt to add and we don't have the details on whether it is needed from the loaded RcppPacket.
72-
} else {
80+
} else {
7381
if(is.null(compileInfo)) compileInfo <- NCinternals(NC)$compileInfo
7482
## Make a new compiler object
7583
NC_Compiler <- NC_CompilerClass$new(NC,
@@ -80,7 +88,7 @@ nCompile_nClass <- function(NC,
8088
interfaceCalls = !is_predefined) ## We don't retain NC in NC_Compiler in order to simplify many environments pointing to each other.
8189
## Get the cppDef
8290
cppDef <- NC_Compiler$cppDef
83-
if(is_predefined) {
91+
if(is_predefined && allow_write_predefined) {
8492
predefined_gen_dir <- NCinternals(NC)$compileInfo$predefined_output_dir
8593
if(is.null(predefined_gen_dir))
8694
predefined_gen_dir <- predefined_dir
@@ -89,6 +97,10 @@ nCompile_nClass <- function(NC,
8997
# Now add interface calls if necessary for this live compilation, having
9098
# kept them out of the written packet code.
9199
cppDef$buildGenericInterface(interfaceCalls=TRUE, interface=FALSE)
100+
# To do: check that there aren't any detected needed units that are not in the compileInfo$needed_units
101+
# because for a predefined, needed units must be provided manually by compileInfo.
102+
} else {
103+
if(gather_needed_units) needed_units <- NC_Compiler$gather_needed_units()
92104
}
93105

94106
##
@@ -101,8 +113,10 @@ nCompile_nClass <- function(NC,
101113
return(NC_Compiler)
102114
}
103115

104-
if(stopAfterCppDef) return(cppDef)
105-
116+
if(stopAfterCppDef) {
117+
if(gather_needed_units) return(list(cppDef = cppDef, needed_units = needed_units))
118+
else return(cppDef)
119+
}
106120
# We might deprecate from here onward.
107121
# Then nCompile_nClass would only be called via nCompile
108122
filebase <- controlFull$filename

nCompiler/R/NC_CompilerClass.R

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,107 @@ NC_CompilerClass <- R6::R6Class(
115115
NCgenerator)
116116
setupMethodSymbolTables()
117117
}
118+
},
119+
gather_needed_units = function() {
120+
# This gathers from member variables and methods.
121+
# It DOES NOT include an inherit nClass, because we could only access
122+
# the inheritNCinternals, but we need the generator object.
123+
# Hence this is collected from nCompile_nClass.
124+
# list() |> unlist() returns NULL so we have to catch that and give list() instead.
125+
# list() |> unique() retruns list(), what we want.
126+
needed_nClasses1 <- nCompile_gather_needed_nClasses(cppDef, self$symbolTable)
127+
needed_nClasses2 <- lapply(NFcompilers,
128+
\(x) x$gather_needed_nClasses()) |>
129+
unlist(recursive = FALSE) |> unique()
130+
needed_nFunctions <- lapply(NFcompilers,
131+
\(x) x$gather_needed_nFunctions()) |>
132+
unlist(recursive = FALSE) |> unique()
133+
compileInfo_needed_units <- nCompile_process_manual_needed_units(
134+
NCinternals(self$NCgenerator),
135+
self$NCgenerator$parent_env, isNC = TRUE)
136+
list(
137+
needed_nClasses = unique(c(needed_nClasses1, needed_nClasses2 %||% list(),
138+
compileInfo_needed_units$needed_nClasses)),
139+
needed_nFunctions = unique(c(needed_nFunctions %||% list(),
140+
compileInfo_needed_units$needed_nFunctions))
141+
)
118142
}
119143
)
120144
)
145+
146+
nCompile_process_manual_needed_units <- function(internals,
147+
where = internals$where, # NFinternals case
148+
isNC = FALSE) {
149+
# This function collects two forms of "manual" needed units (nClasses and nFunctions):
150+
# those provided via compileInfo$needed_units and also (in the case of nClass)
151+
# an inherited nClass.
152+
#
153+
# A little awkwardness on the input arguments:
154+
# It would be nice to pass either just the internals (NCinternals(NC) or NFinternals(NF))
155+
# OR just the NC or NF object.
156+
# But neither case is consistent between nClass and nFunction.
157+
# We would need the nClass generator to get the where (parent_env), and the NCinternals doesn't have that.
158+
# Conversely, we could pass the objects, but the NF_CompilerClass (a calling point) does not have the NF object.
159+
# Therefore, we make this harder to read and pass both internals and where and indicate which case we're in with isNC.
160+
# The defaults are for the case of NF, where internals is NFinternals(NF).
161+
name <- if(isNC) internals$classname else internals$uniqueName
162+
163+
needed_units <- internals$compileInfo$needed_units
164+
results_nClasses <- list()
165+
results_nFunctions <- list()
166+
for(i in seq_along(needed_units)) {
167+
if(is.character(needed_units[[i]])) {
168+
obj <- nGet(needed_units[[i]], where)
169+
if(is.null(obj))
170+
stop(paste0("In processing compileInfo$needed_units for ", name, ", could not find object named '",
171+
needed_units[[i]], "' in the environment of the source unit."))
172+
} else {
173+
obj <- needed_units[[i]]
174+
}
175+
if(isNCgenerator(obj)) {
176+
results_nClasses[[length(results_nClasses) + 1]] <- obj
177+
} else if(isNF(obj)) {
178+
results_nFunctions[[length(results_nFunctions) + 1]] <- obj
179+
} else {
180+
stop(paste0("In processing compileInfo$needed_units for ", name, ", object '",
181+
needed_units[[i]], "' is neither an nClass generator nor an nFunction."))
182+
}
183+
}
184+
185+
if(isNC) {
186+
# Get inherited nClass as a needed unit
187+
if(!is.null(internals$inheritQ)) {
188+
inherit_obj <- eval(internals$inheritQ, envir = internals$env) # see connect_inherit
189+
if(!isNCgenerator(inherit_obj))
190+
stop("An inherit argument that was provided to nClass does not evaluate to an nClass generator.")
191+
results_nClasses[[length(results_nClasses) + 1]] <- inherit_obj
192+
}
193+
}
194+
195+
list(needed_nClasses = results_nClasses,
196+
needed_nFunctions = results_nFunctions)
197+
}
198+
199+
nCompile_gather_needed_nClasses <- function(cppDef,
200+
symTab,
201+
NF_Compiler = NULL) {
202+
# Collect nClass generators needed by this symbol table
203+
new_needed <- list()
204+
for(i in seq_along(symTab$symbols)) {
205+
if(inherits(symTab$symbols[[i]], "symbolNC")) {
206+
new_needed[[length(new_needed) + 1]] <-
207+
symTab$symbols[[i]]$NCgenerator
208+
}
209+
}
210+
# For an nFunction, collection nClass generators identified
211+
# from processing the code.
212+
if(!is.null(NF_Compiler)) {
213+
auxEnv_needed_nClasses <- NF_Compiler$auxEnv$needed_nClasses
214+
if(length(auxEnv_needed_nClasses)) {
215+
bool_NCgen <- lapply(auxEnv_needed_nClasses, isNCgenerator) |> unlist()
216+
new_needed <- c(new_needed,
217+
auxEnv_needed_nClasses[bool_NCgen])
218+
}
219+
}
220+
unique(new_needed)
221+
}

nCompiler/R/NC_InternalsClass.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ NC_InternalsClass <- R6::R6Class(
2727
predefined = FALSE, # directory for reading and (default) writing predefined nClass saved RcppPacket. Writing location can be over-ridden by compileInfo$predefined_output_dir
2828
inheritNCinternals = NULL,
2929
env = NULL,
30-
inheritQ = NULL,
30+
inheritQ = NULL, # quoted inherit expression, to defer access to the inherited nClass generator itself.
3131
process_inherit_done = FALSE,
3232
virtualMethodNames_self = character(), # will be used when checking inherited method validity, only for locally implemented methods
3333
virtualMethodNames = character(),
@@ -101,7 +101,7 @@ NC_InternalsClass <- R6::R6Class(
101101
if(!is.null(self$inheritQ)) {
102102
inherit_obj <- eval(self$inheritQ, envir = self$env) #inheritQ can be an expression but it must always return the same generator object
103103
if(!isNCgenerator(inherit_obj))
104-
stop("An inherit argument that was provided to nClass is not nClass generator.")
104+
stop("An inherit argument that was provided to nClass does not evaluate to an nClass generator.")
105105
self$inheritNCinternals <- NCinternals(inherit_obj)
106106
message("add check that base class has interface 'none'")
107107
if(!self$inherit_base_provided) {

nCompiler/R/NF_Compile.R

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ nCompile_nFunction <- function(NF,
5858
if(is.null(compileInfo)) compileInfo <- NFinternals(NF)$compileInfo
5959

6060
is_predefined <- !isFALSE(NFinternals(NF)$predefined)
61+
gather_needed_units <- isTRUE(controlFull$always_include_units)
62+
needed_units <- list(needed_nClasses = list(),
63+
needed_nFunctions = list())
64+
allow_write_predefined <- FALSE
6165
if(is_predefined) {
6266
predefined_dir <- NFinternals(NF)$predefined
6367
# predefined can be character, quoted expression, or function.
@@ -75,6 +79,9 @@ nCompile_nFunction <- function(NF,
7579
"It should give the directory path of the predefined nFunction. ",
7680
"The name argument to nFunction gives the base for filenames in that directory.")
7781
regular_filename <- NFinternals(NF)$cpp_code_name
82+
if(gather_needed_units)
83+
needed_units <- nCompile_process_manual_needed_units(NFinternals(NF))
84+
allow_write_predefined <- !isTRUE(compileInfo$auto_included)
7885
}
7986
if(is_predefined && isFALSE(controlFull$generate_predefined)) {
8087
RcppPacket <- loadRcppPacket(predefined_dir, regular_filename)
@@ -92,12 +99,14 @@ nCompile_nFunction <- function(NF,
9299
NF_Compiler$stageCompleted))
93100
return(NF_Compiler)
94101
}
95-
if(is_predefined) {
102+
if(is_predefined && allow_write_predefined) {
96103
predefined_gen_dir <- NFinternals(NF)$compileInfo$predefined_output_dir
97104
if(is.null(predefined_gen_dir))
98105
predefined_gen_dir <- predefined_dir
99106
RcppPacket <- cppDefs_2_RcppPacket(NF_Compiler$cppDef)
100107
saveRcppPacket(RcppPacket, predefined_dir, regular_filename)
108+
} else {
109+
if(gather_needed_units) needed_units <- NF_Compiler$gather_needed_units()
101110
}
102111
stageName <- 'makeRcppPacket'
103112
if (logging) logBeforeStage(stageName)
@@ -106,8 +115,10 @@ nCompile_nFunction <- function(NF,
106115

107116
cppDef <- NF_Compiler$cppDef
108117
}
109-
if(stopAfterCppDef) return(cppDef)
110-
118+
if(stopAfterCppDef) {
119+
if(gather_needed_units) return(list(needed_units = needed_units, cppDef = cppDef))
120+
else return(cppDef)
121+
}
111122
# We might deprecate from here down and make all usages start from nCompile.
112123

113124
stop("Entering deprecated portion of nCompile_nFunction. Check what is going on.")

nCompiler/R/NF_CompilerClass.R

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,32 @@ NF_CompilerClass <- R6::R6Class(
139139
doKeywords,
140140
.nCompilerProject,
141141
initialTypeInferenceOnly)
142+
},
143+
gather_needed_units = function() {
144+
compileInfo_needed_units <- nCompile_process_manual_needed_units(self$NFinternals)
145+
list(
146+
needed_nClasses = c(self$gather_needed_nClasses(),
147+
compileInfo_needed_units$needed_nClasses),
148+
needed_nFunctions = c(self$gather_needed_nFunctions(),
149+
compileInfo_needed_units$needed_nFunctions)
150+
)
151+
},
152+
gather_needed_nClasses = function() {
153+
nCompile_gather_needed_nClasses(cppDef, self$symbolTable, self)
154+
},
155+
gather_needed_nFunctions = function() {
156+
nCompile_gather_needed_nFunctions(cppDef, self)
142157
}
143158
)
144159
)
145160

161+
nCompile_gather_needed_nFunctions <- function(cppDef,
162+
NF_Compiler) {
163+
lapply(NF_Compiler$auxEnv$needed_nFunctions,
164+
function(x)
165+
nGet(x[[1]], where = x[[2]])) |> unique()
166+
}
167+
146168
processNFstages <- function(NFcompiler,
147169
control = list(),
148170
sourceObj = NULL,

nCompiler/R/cppDefs_nFunction.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ cpp_include_needed_nClasses <- function(cppDef,
199199
}
200200
}
201201
new_Hincludes <- unique(new_Hincludes)
202-
cppDef$Hincludes <- c(cppDef$Hincludes, new_Hincludes)
202+
cppDef$Hincludes <- unique(c(cppDef$Hincludes, new_Hincludes))
203203
invisible(NULL)
204204
}
205205

0 commit comments

Comments
 (0)