diff --git a/inst/include/ergm_Rutil.h b/inst/include/ergm_Rutil.h index 9e625c0e..dd0d63e6 100644 --- a/inst/include/ergm_Rutil.h +++ b/inst/include/ergm_Rutil.h @@ -71,4 +71,6 @@ static inline void *R_calloc_helper(size_t n, size_t size){ #define R_CheckUserInterruptEvery(freq, iter) if((iter)%(freq) == 0) R_CheckUserInterrupt(); +void warn_API_version(const char *sn); + #endif diff --git a/inst/include/ergm_stubs.c b/inst/include/ergm_stubs.c index 7abd4a1b..00a22aa4 100644 --- a/inst/include/ergm_stubs.c +++ b/inst/include/ergm_stubs.c @@ -8,6 +8,11 @@ * Copyright 2003-2024 Statnet Commons */ +#include "ergm_constants.h" + +unsigned int built_ERGM_API_MAJOR = ERGM_API_MAJOR; +unsigned int built_ERGM_API_MINOR = ERGM_API_MINOR; + #define STUBFILE #include #include @@ -77,6 +82,16 @@ static void (*fun)(Vertex,Vertex,double,void *,WtNetwork *,double) = NULL; if(fun==NULL) fun = (void (*)(Vertex,Vertex,double,void *,WtNetwork *,double)) R_FindSymbol("WtDyadGenUpdate", "ergm", NULL); fun(tail,head,weight,gen,nwp,edgestate); } +void AddOnDyadGenInit(OnDyadGenInit callback, void *payload){ +static void (*fun)(OnDyadGenInit,void *) = NULL; +if(fun==NULL) fun = (void (*)(OnDyadGenInit,void *)) R_FindSymbol("AddOnDyadGenInit", "ergm", NULL); +fun(callback,payload); +} +void DeleteOnDyadGenInit(OnDyadGenInit callback, void *payload){ +static void (*fun)(OnDyadGenInit,void *) = NULL; +if(fun==NULL) fun = (void (*)(OnDyadGenInit,void *)) R_FindSymbol("DeleteOnDyadGenInit", "ergm", NULL); +fun(callback,payload); +} #define STUBFILE #include diff --git a/src/MHproposal.c b/src/MHproposal.c index e576638d..5b8cb12a 100644 --- a/src/MHproposal.c +++ b/src/MHproposal.c @@ -31,6 +31,10 @@ MHProposal *MHProposalInitialize(SEXP pR, Network *nwp, void **aux_storage){ /* Extract the required string information from the relevant sources */ const char *fname = FIRSTCHAR(getListElement(pR, "name")), *sn = FIRSTCHAR(getListElement(pR, "pkgname")); + + /* Check if the package is compiled against a different version of 'ergm'. */ + warn_API_version(sn); + char *fn = R_Calloc(strlen(fname)+4, char); fn[0]='M'; fn[1]='H'; diff --git a/src/ergm_Rutil.c b/src/ergm_Rutil.c new file mode 100644 index 00000000..c6a68069 --- /dev/null +++ b/src/ergm_Rutil.c @@ -0,0 +1,34 @@ +#include "ergm_Rutil.h" +#include "ergm_constants.h" +#include "ergm_kvec.h" +#include "ergm_khash.h" + +unsigned int built_ERGM_API_MAJOR = ERGM_API_MAJOR; +unsigned int built_ERGM_API_MINOR = ERGM_API_MINOR; + +static kvec_t(khint_t) build_version_warned = kv_blank; + +void warn_API_version(const char *sn){ + + /* Here, we are saving the hash of the library name rather than the + name itself, since keeping track of strings allocated from the + heap is a hassle, and we don't want to assume that sn will always + be around. Chances of collision are negligible. */ + khint_t snhash = kh_str_hash_func(sn); + unsigned int i = 0; + while(i < kv_size(build_version_warned) && kv_A(build_version_warned, i) != snhash) i++; + if(i < kv_size(build_version_warned)) return; + + kv_push(khint_t, build_version_warned, snhash); + + void *bv = R_FindSymbol("built_ERGM_API_MAJOR",sn,NULL); + unsigned int bv_major = bv ? *(unsigned int *)bv : 0; + if(bv_major){ + unsigned int bv_minor = *(unsigned int *)R_FindSymbol("built_ERGM_API_MINOR",sn,NULL); + if(bv_major != ERGM_API_MAJOR || bv_minor != ERGM_API_MINOR) + warningcall_immediate(R_NilValue, "Package '%s' was compiled against 'ergm' with C API version\n %d.%d, but it is being loaded by 'ergm' with C API version %d.%d. Inconsistent\n versions may result in malfunctions ranging from incorrect results to R\n crashing. Please rebuild the package against the current 'ergm' version.", + sn, bv_major, bv_minor, ERGM_API_MAJOR, ERGM_API_MINOR); + }else + warningcall_immediate(R_NilValue, "Package '%s' was compiled against an older version of 'ergm'\n that did not store '%s''s C API version information. Inconsistent\n versions may cause malfunctions ranging from incorrect results to R\n crashing. Please rebuild the package against the current 'ergm' version.", + sn, sn); +} diff --git a/src/model.c b/src/model.c index 05f26cc3..2004dcae 100644 --- a/src/model.c +++ b/src/model.c @@ -162,6 +162,10 @@ Model* ModelInitialize(SEXP mR, SEXP ext_state, Network *nwp, Rboolean noinit_s) the respective functions.*/ const char *fname = FIRSTCHAR(getListElement(thisterm->R, "name")), *sn = FIRSTCHAR(getListElement(thisterm->R, "pkgname")); + + /* Check if the package is compiled against a different version of 'ergm'. */ + warn_API_version(sn); + /* Extract the required string information from the relevant sources */ char *fn = R_Calloc(strlen(fname)+3, char); fn[1]='_'; diff --git a/src/wtMHproposal.c b/src/wtMHproposal.c index 9dcbe5aa..433a6d21 100644 --- a/src/wtMHproposal.c +++ b/src/wtMHproposal.c @@ -30,6 +30,10 @@ WtMHProposal *WtMHProposalInitialize(SEXP pR, WtNetwork *nwp, void **aux_storage /* Extract the required string information from the relevant sources */ const char *fname = FIRSTCHAR(getListElement(pR, "name")), *sn = FIRSTCHAR(getListElement(pR, "pkgname")); + + /* Check if the package is compiled against a different version of 'ergm'. */ + warn_API_version(sn); + char *fn = R_Calloc(strlen(fname)+4, char); fn[0]='M'; fn[1]='H'; diff --git a/src/wtmodel.c b/src/wtmodel.c index 6cbef370..cb6e8626 100644 --- a/src/wtmodel.c +++ b/src/wtmodel.c @@ -162,6 +162,10 @@ WtModel* WtModelInitialize (SEXP mR, SEXP ext_state, WtNetwork *nwp, Rboolean no the respective functions.*/ const char *fname = FIRSTCHAR(getListElement(thisterm->R, "name")), *sn = FIRSTCHAR(getListElement(thisterm->R, "pkgname")); + + /* Check if the package is compiled against a different version of 'ergm'. */ + warn_API_version(sn); + /* Extract the required string information from the relevant sources */ char *fn = R_Calloc(strlen(fname)+3, char); fn[1]='_';