New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

define STRICT_R_HEADERS to avoid definition of Free, ... #898

Open
romainfrancois opened this Issue Sep 7, 2018 · 20 comments

Comments

Projects
None yet
4 participants
@romainfrancois
Contributor

romainfrancois commented Sep 7, 2018

When this macro is not defined, R headers do define macros with very generic names, such as Free for S-PLUS back compatibility:

https://github.com/wch/r-source/blob/trunk/src/include/R_ext/RS.h#L70

#ifndef STRICT_R_HEADERS
/* S-PLUS 3.x but not 5.x NULLs the pointer in the following */
#define Calloc(n, t)   (t *) R_chk_calloc( (R_SIZE_T) (n), sizeof(t) )
#define Realloc(p,n,t) (t *) R_chk_realloc( (void *)(p), (R_SIZE_T)((n) * sizeof(t)) )
#define Free(p)        (R_chk_free( (void *)(p) ), (p) = NULL)
#endif

https://github.com/wch/r-source/blob/trunk/src/include/R_ext/RS.h#L49

#ifndef STRICT_R_HEADERS

#define R_PROBLEM_BUFSIZE	4096
/* Parentheses added for FC4 with gcc4 and -D_FORTIFY_SOURCE=2 */
#define PROBLEM			{char R_problem_buf[R_PROBLEM_BUFSIZE];(sprintf)(R_problem_buf,
#define MESSAGE                 {char R_problem_buf[R_PROBLEM_BUFSIZE];(sprintf)(R_problem_buf,
#define ERROR			),error(R_problem_buf);}
#define RECOVER(x)		),error(R_problem_buf);}
#define WARNING(x)		),warning(R_problem_buf);}
#define LOCAL_EVALUATOR		/**/
#define NULL_ENTRY		/**/
#define WARN			WARNING(NULL)

#endif

And https://github.com/wch/r-source/blob/trunk/src/include/R_ext/Constants.h#L35

#ifndef STRICT_R_HEADERS
#define PI             M_PI
#include <float.h>  /* Defines the rest, at least in C99 */
#define SINGLE_EPS     FLT_EPSILON
#define SINGLE_BASE    FLT_RADIX
#define SINGLE_XMIN    FLT_MIN
#define SINGLE_XMAX    FLT_MAX
#define DOUBLE_DIGITS  DBL_MANT_DIG
#define DOUBLE_EPS     DBL_EPSILON
#define DOUBLE_XMAX    DBL_MAX
#define DOUBLE_XMIN    DBL_MIN
#endif

For example the Free macro was making noise in arrow: romainfrancois/arrow@79c5001#diff-65a2f88d0196bb912b5ba669f5792492R20

So, should Rcpp have define STRICT_R_HEADERS somewhere ?

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Sep 7, 2018

Member

I am fairly certain we looked into this at some point early on when we did the header cleanup. And I am also pretty sure that I too had been bitten by it but I don't recall now why we are not doing it. That said, "it's late now" as we have 1400+ reverse depends so change may not be easy.

One way around it is of course to include Rcpp.h last but that is not always possible. Can you (for now) define STRICT_R_HEADERS before you include it for Arrow projects?

Member

eddelbuettel commented Sep 7, 2018

I am fairly certain we looked into this at some point early on when we did the header cleanup. And I am also pretty sure that I too had been bitten by it but I don't recall now why we are not doing it. That said, "it's late now" as we have 1400+ reverse depends so change may not be easy.

One way around it is of course to include Rcpp.h last but that is not always possible. Can you (for now) define STRICT_R_HEADERS before you include it for Arrow projects?

@romainfrancois

This comment has been minimized.

Show comment
Hide comment
@romainfrancois

romainfrancois Sep 7, 2018

Contributor

Of course I can and I am. I don’t need this, i was just sharing the information.

Contributor

romainfrancois commented Sep 7, 2018

Of course I can and I am. I don’t need this, i was just sharing the information.

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Sep 7, 2018

Member

And I started a reverse depends check to see how much breakage this may introduce. I am running a full "baseline" run first so this will take a moment.

Member

eddelbuettel commented Sep 7, 2018

And I started a reverse depends check to see how much breakage this may introduce. I am running a full "baseline" run first so this will take a moment.

@kevinushey

This comment has been minimized.

Show comment
Hide comment
@kevinushey

kevinushey Sep 7, 2018

Contributor

I'd be on-board with setting STRICT_R_HEADERS by default as long as there is not too much fallout from dependent packages.

Contributor

kevinushey commented Sep 7, 2018

I'd be on-board with setting STRICT_R_HEADERS by default as long as there is not too much fallout from dependent packages.

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Sep 7, 2018

Member

Yes. The non-"Namespace" (in quotes, because C ...) protected identifiers are a pain, and the clashes are a nuisance. We should promote better programming and packaging practice where possible.

We'll see what the rev.deps say, but at a minimum we could even try to phase it in.

Member

eddelbuettel commented Sep 7, 2018

Yes. The non-"Namespace" (in quotes, because C ...) protected identifiers are a pain, and the clashes are a nuisance. We should promote better programming and packaging practice where possible.

We'll see what the rev.deps say, but at a minimum we could even try to phase it in.

@romainfrancois

This comment has been minimized.

Show comment
Hide comment
@romainfrancois

romainfrancois Sep 7, 2018

Contributor

Would be less aggressive than undef them, Rcpp could offer an opt-in instead so that packages that really need these macros can still have them.

Contributor

romainfrancois commented Sep 7, 2018

Would be less aggressive than undef them, Rcpp could offer an opt-in instead so that packages that really need these macros can still have them.

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Sep 7, 2018

Member

What I was thinking is that we could possibly #define STRICT_R_HEADERS with an option to opt-out, ie if you have something set, say RCPP_NO_STRICT_HEADERS then we won't.

But let's first see what damage (if any) a definition may cause.

Member

eddelbuettel commented Sep 7, 2018

What I was thinking is that we could possibly #define STRICT_R_HEADERS with an option to opt-out, ie if you have something set, say RCPP_NO_STRICT_HEADERS then we won't.

But let's first see what damage (if any) a definition may cause.

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Sep 8, 2018

Member

Does not look too good. Baseline run against Rcpp 0.12.18 (once I move two new ones to skip):

Test of Rcpp had 1357 successes, 16 failures, and 60 skipped packages. 

where 'the usual' set of packages fails.

But with STRICT_R_HEADERS:

Test of unknown had 1295 successes, 78 failures, and 60 skipped packages.

Details in RcppCore/rcpp-logs@f05b3e4

So for the time being this may be a documentation-only thing.

Edited: Had a copy and paste error reporting the initial run twice.

Member

eddelbuettel commented Sep 8, 2018

Does not look too good. Baseline run against Rcpp 0.12.18 (once I move two new ones to skip):

Test of Rcpp had 1357 successes, 16 failures, and 60 skipped packages. 

where 'the usual' set of packages fails.

But with STRICT_R_HEADERS:

Test of unknown had 1295 successes, 78 failures, and 60 skipped packages.

Details in RcppCore/rcpp-logs@f05b3e4

So for the time being this may be a documentation-only thing.

Edited: Had a copy and paste error reporting the initial run twice.

@kevinushey

This comment has been minimized.

Show comment
Hide comment
@kevinushey

kevinushey Sep 8, 2018

Contributor

Dang, that's more than I expected 😞

Given the number of failures, I don't think we should force the issue -- client packages can (as arrow does) just provide the define themselves, e.g.

#define STRICT_R_HEADERS
#include <Rcpp.h>

(or perhaps even better enforce in PKG_CPPFLAGS from Makevars)

Contributor

kevinushey commented Sep 8, 2018

Dang, that's more than I expected 😞

Given the number of failures, I don't think we should force the issue -- client packages can (as arrow does) just provide the define themselves, e.g.

#define STRICT_R_HEADERS
#include <Rcpp.h>

(or perhaps even better enforce in PKG_CPPFLAGS from Makevars)

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Sep 8, 2018

Member

Yes and yes.

But I am thinking we should still try to get this in. I was thinking of two approaches, the second idea is a little fresher and less mature 😀

First Approach: Contact Maintainers

This is old gum-shoe work. Email all 60+ maintainers. Ask for a change. Give a reasonable window, say one year.

Second Approach: configure !!

Given that we know which packages (or CRAN, among non-skipped ones) fail, we could have configure add / not-add the #define conditionally. For the currently-failing packages we do not set it, but issue a loud warning. We also document "everywhere" that you can opt out with a RCPP_NO_STRICT_HEADERS definition.

What do you think? Is this feasible?

Member

eddelbuettel commented Sep 8, 2018

Yes and yes.

But I am thinking we should still try to get this in. I was thinking of two approaches, the second idea is a little fresher and less mature 😀

First Approach: Contact Maintainers

This is old gum-shoe work. Email all 60+ maintainers. Ask for a change. Give a reasonable window, say one year.

Second Approach: configure !!

Given that we know which packages (or CRAN, among non-skipped ones) fail, we could have configure add / not-add the #define conditionally. For the currently-failing packages we do not set it, but issue a loud warning. We also document "everywhere" that you can opt out with a RCPP_NO_STRICT_HEADERS definition.

What do you think? Is this feasible?

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Sep 9, 2018

Member

Using the SQLite result files from the prrd runs committed to the sibbling rcpp-logs repo:

suppressMessages({
    library(RSQLite)
    library(data.table)
})
    
run1 <- dbConnect(SQLite(), "~/git/rcpp-logs/queues/Rcpp_2018-09-07/queuefile.sqlite")
run2 <- dbConnect(SQLite(), "~/git/rcpp-logs/queues/Rcpp_2018-09-08/queuefile.sqlite")

res1 <- setDT(dbGetQuery(run1, "select * from results"))
res2 <- setDT(dbGetQuery(run2, "select * from results"))

## correct res1 for the two which were later added to skip list
res1[ package %in% c("bgsmtr", "EstMix") , result:=2]

## find the ones in run2 that failed
cands <- res2[ result==1, 1:3 ]
## inner join with run1, extract the ones that worked there --> new failures
fails <- res1[cands[,1:3], 1:3, on="package"][result==0]

gets us

R> fails
          package version result
 1:         basad   0.2.0      0
 2:   bayesImageS   0.5-3      0
 3:           bcp   4.0.3      0
 4:           bfa     0.4      0
 5:      biglasso   1.3-6      0
 6:     bigmemory  4.5.33      0
 7:        bigReg   0.1.2      0
 8:      binnednp   0.1.0      0
 9:     biwavelet 0.20.17      0
10:   blockmodels   1.1.1      0
11:        castor   1.3.4      0
12:        cbinom     1.3      0
13:    circumplex   0.1.2      0
14:        CorReg   1.2.8      0
15:       CoxPlus   1.1.1      0
16:           DGM   1.7.2      0
17:         emIRT   0.0.8      0
18:          EMVS     1.0      0
19:          ETAS   0.4.4      0
20:  facilitation   0.5.2      0
21:   fasteraster   1.1.1      0
22:    fastGHQuad     0.2      0
23:    extraDistr   1.8.9      0
24:          fDMA 2.2.3.1      0
25:   frailtySurv   1.3.5      0
26:        gcKrig   1.1.2      0
27:           GAS   0.2.8      0
28:       geogrid 0.1.0.1      0
29:        glmBfp  0.0-51      0
30:         Gmisc   1.6.2      0
31: HardyWeinberg   1.6.1      0
32:       icenReg   2.0.8      0
33:          lidR   1.6.1      0
34:          lpme   1.1.1      0
35:    MAINT.Data   1.2.3      0
36:        mcmcse   1.3-2      0
37:       medfate   0.2.2      0
38:     meteoland   0.7.5      0
39: minimaxdesign   0.1.3      0
40:       MSGARCH     2.3      0
41:       multdyn     1.6      0
42:      MultiFit   0.1.2      0
43:        mwaved   1.1.5      0
44:   netdiffuseR  1.20.0      0
45:         nngeo   0.2.1      0
46:          npsf   0.4.1      0
47:   packcircles   0.3.2      0
48:         PAFit 1.0.0.6      0
49:     partialCI   1.1.1      0
50:         pense   1.2.0      0
51:       precrec   0.9.1      0
52:        recmap  0.5.37      0
53:         ReIns   1.0.7      0
54:       rmgarch   1.3-0      0
55:     robustlmm   2.2-1      0
56:        rococo   1.1.5      0
57:     rotations     1.5      0
58:       rtdists   0.9-0      0
59:    serrsBayes  0.3-13      0
60:      unmarked  0.12-2      0
61:         updog   1.0.1      0
62:      vegclust   1.7.4      0
          package version result
R> 

or

R> dput(fails[,1:2])
structure(list(package = c("basad", "bayesImageS", "bcp", "bfa", 
"biglasso", "bigmemory", "bigReg", "binnednp", "biwavelet", "blockmodels", 
"castor", "cbinom", "circumplex", "CorReg", "CoxPlus", "DGM", 
"emIRT", "EMVS", "ETAS", "facilitation", "fasteraster", "fastGHQuad", 
"extraDistr", "fDMA", "frailtySurv", "gcKrig", "GAS", "geogrid", 
"glmBfp", "Gmisc", "HardyWeinberg", "icenReg", "lidR", "lpme", 
"MAINT.Data", "mcmcse", "medfate", "meteoland", "minimaxdesign", 
"MSGARCH", "multdyn", "MultiFit", "mwaved", "netdiffuseR", "nngeo", 
"npsf", "packcircles", "PAFit", "partialCI", "pense", "precrec", 
"recmap", "ReIns", "rmgarch", "robustlmm", "rococo", "rotations", 
"rtdists", "serrsBayes", "unmarked", "updog", "vegclust"), version = c("0.2.0", 
"0.5-3", "4.0.3", "0.4", "1.3-6", "4.5.33", "0.1.2", "0.1.0", 
"0.20.17", "1.1.1", "1.3.4", "1.3", "0.1.2", "1.2.8", "1.1.1", 
"1.7.2", "0.0.8", "1.0", "0.4.4", "0.5.2", "1.1.1", "0.2", "1.8.9", 
"2.2.3.1", "1.3.5", "1.1.2", "0.2.8", "0.1.0.1", "0.0-51", "1.6.2", 
"1.6.1", "2.0.8", "1.6.1", "1.1.1", "1.2.3", "1.3-2", "0.2.2", 
"0.7.5", "0.1.3", "2.3", "1.6", "0.1.2", "1.1.5", "1.20.0", "0.2.1", 
"0.4.1", "0.3.2", "1.0.0.6", "1.1.1", "1.2.0", "0.9.1", "0.5.37", 
"1.0.7", "1.3-0", "2.2-1", "1.1.5", "1.5", "0.9-0", "0.3-13", 
"0.12-2", "1.0.1", "1.7.4")), class = c("data.table", "data.frame"
), row.names = c(NA, -62L), .internal.selfref = <pointer: 0x55a6d42ba0b0>)
R> 
Member

eddelbuettel commented Sep 9, 2018

Using the SQLite result files from the prrd runs committed to the sibbling rcpp-logs repo:

suppressMessages({
    library(RSQLite)
    library(data.table)
})
    
run1 <- dbConnect(SQLite(), "~/git/rcpp-logs/queues/Rcpp_2018-09-07/queuefile.sqlite")
run2 <- dbConnect(SQLite(), "~/git/rcpp-logs/queues/Rcpp_2018-09-08/queuefile.sqlite")

res1 <- setDT(dbGetQuery(run1, "select * from results"))
res2 <- setDT(dbGetQuery(run2, "select * from results"))

## correct res1 for the two which were later added to skip list
res1[ package %in% c("bgsmtr", "EstMix") , result:=2]

## find the ones in run2 that failed
cands <- res2[ result==1, 1:3 ]
## inner join with run1, extract the ones that worked there --> new failures
fails <- res1[cands[,1:3], 1:3, on="package"][result==0]

gets us

R> fails
          package version result
 1:         basad   0.2.0      0
 2:   bayesImageS   0.5-3      0
 3:           bcp   4.0.3      0
 4:           bfa     0.4      0
 5:      biglasso   1.3-6      0
 6:     bigmemory  4.5.33      0
 7:        bigReg   0.1.2      0
 8:      binnednp   0.1.0      0
 9:     biwavelet 0.20.17      0
10:   blockmodels   1.1.1      0
11:        castor   1.3.4      0
12:        cbinom     1.3      0
13:    circumplex   0.1.2      0
14:        CorReg   1.2.8      0
15:       CoxPlus   1.1.1      0
16:           DGM   1.7.2      0
17:         emIRT   0.0.8      0
18:          EMVS     1.0      0
19:          ETAS   0.4.4      0
20:  facilitation   0.5.2      0
21:   fasteraster   1.1.1      0
22:    fastGHQuad     0.2      0
23:    extraDistr   1.8.9      0
24:          fDMA 2.2.3.1      0
25:   frailtySurv   1.3.5      0
26:        gcKrig   1.1.2      0
27:           GAS   0.2.8      0
28:       geogrid 0.1.0.1      0
29:        glmBfp  0.0-51      0
30:         Gmisc   1.6.2      0
31: HardyWeinberg   1.6.1      0
32:       icenReg   2.0.8      0
33:          lidR   1.6.1      0
34:          lpme   1.1.1      0
35:    MAINT.Data   1.2.3      0
36:        mcmcse   1.3-2      0
37:       medfate   0.2.2      0
38:     meteoland   0.7.5      0
39: minimaxdesign   0.1.3      0
40:       MSGARCH     2.3      0
41:       multdyn     1.6      0
42:      MultiFit   0.1.2      0
43:        mwaved   1.1.5      0
44:   netdiffuseR  1.20.0      0
45:         nngeo   0.2.1      0
46:          npsf   0.4.1      0
47:   packcircles   0.3.2      0
48:         PAFit 1.0.0.6      0
49:     partialCI   1.1.1      0
50:         pense   1.2.0      0
51:       precrec   0.9.1      0
52:        recmap  0.5.37      0
53:         ReIns   1.0.7      0
54:       rmgarch   1.3-0      0
55:     robustlmm   2.2-1      0
56:        rococo   1.1.5      0
57:     rotations     1.5      0
58:       rtdists   0.9-0      0
59:    serrsBayes  0.3-13      0
60:      unmarked  0.12-2      0
61:         updog   1.0.1      0
62:      vegclust   1.7.4      0
          package version result
R> 

or

R> dput(fails[,1:2])
structure(list(package = c("basad", "bayesImageS", "bcp", "bfa", 
"biglasso", "bigmemory", "bigReg", "binnednp", "biwavelet", "blockmodels", 
"castor", "cbinom", "circumplex", "CorReg", "CoxPlus", "DGM", 
"emIRT", "EMVS", "ETAS", "facilitation", "fasteraster", "fastGHQuad", 
"extraDistr", "fDMA", "frailtySurv", "gcKrig", "GAS", "geogrid", 
"glmBfp", "Gmisc", "HardyWeinberg", "icenReg", "lidR", "lpme", 
"MAINT.Data", "mcmcse", "medfate", "meteoland", "minimaxdesign", 
"MSGARCH", "multdyn", "MultiFit", "mwaved", "netdiffuseR", "nngeo", 
"npsf", "packcircles", "PAFit", "partialCI", "pense", "precrec", 
"recmap", "ReIns", "rmgarch", "robustlmm", "rococo", "rotations", 
"rtdists", "serrsBayes", "unmarked", "updog", "vegclust"), version = c("0.2.0", 
"0.5-3", "4.0.3", "0.4", "1.3-6", "4.5.33", "0.1.2", "0.1.0", 
"0.20.17", "1.1.1", "1.3.4", "1.3", "0.1.2", "1.2.8", "1.1.1", 
"1.7.2", "0.0.8", "1.0", "0.4.4", "0.5.2", "1.1.1", "0.2", "1.8.9", 
"2.2.3.1", "1.3.5", "1.1.2", "0.2.8", "0.1.0.1", "0.0-51", "1.6.2", 
"1.6.1", "2.0.8", "1.6.1", "1.1.1", "1.2.3", "1.3-2", "0.2.2", 
"0.7.5", "0.1.3", "2.3", "1.6", "0.1.2", "1.1.5", "1.20.0", "0.2.1", 
"0.4.1", "0.3.2", "1.0.0.6", "1.1.1", "1.2.0", "0.9.1", "0.5.37", 
"1.0.7", "1.3-0", "2.2-1", "1.1.5", "1.5", "0.9-0", "0.3-13", 
"0.12-2", "1.0.1", "1.7.4")), class = c("data.table", "data.frame"
), row.names = c(NA, -62L), .internal.selfref = <pointer: 0x55a6d42ba0b0>)
R> 
@kevinushey

This comment has been minimized.

Show comment
Hide comment
@kevinushey

kevinushey Sep 10, 2018

Contributor

I'm not a fan of just screening these packages in configure unless we give these packages a way to communicate to Rcpp that they now do indeed compile with that define somehow. Overall that route seems not worth the trouble.

"Contact the maintainers" seems a better way forward. We might even consider a brief message to CRAN (Uwe) just to see if they agree the change would be warranted given the potential for a bit of disruption. (This seems like the sort of thing CRAN might want to enforce globally some day)

Contributor

kevinushey commented Sep 10, 2018

I'm not a fan of just screening these packages in configure unless we give these packages a way to communicate to Rcpp that they now do indeed compile with that define somehow. Overall that route seems not worth the trouble.

"Contact the maintainers" seems a better way forward. We might even consider a brief message to CRAN (Uwe) just to see if they agree the change would be warranted given the potential for a bit of disruption. (This seems like the sort of thing CRAN might want to enforce globally some day)

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Sep 10, 2018

Member

Yes, screening is kinda hackish. Another attempt may be to email all maintainers now, say we plan to do this "in due course" but with suitable warning, say in 12 months, and then revisit in 3, 6, 0, ... months as we have time. Hopefully the list whittles down...

Member

eddelbuettel commented Sep 10, 2018

Yes, screening is kinda hackish. Another attempt may be to email all maintainers now, say we plan to do this "in due course" but with suitable warning, say in 12 months, and then revisit in 3, 6, 0, ... months as we have time. Hopefully the list whittles down...

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Sep 16, 2018

Member

New plan. I will email all 50+ maintainers, explaining that their package failed with strict headers and asking that they update within the year. I will also email rcpp-devel.

And come September 2019, we add the #define. Better?

Code

With fails as above, adding

db <- setDT(tools::CRAN_package_db())

maints <- db[ fails[, .(package, version)], .(Package, Version, Maintainer)  ]

## need one special case: one package without accent in name, two with
maints[Maintainer == "Miquel De Caceres <miquelcaceres@gmail.com>",
       Maintainer := "Miquel De Cáceres <miquelcaceres@gmail.com>" ]

setkey(maints, Maintainer)

maints[ , .(pkgs=paste(Package, collapse=", ")), by=Maintainer]

get us these maintainers:

R> maints[ , .(pkgs=paste(Package, collapse=", ")), by=Maintainer]
                                               Maintainer                         pkgs
 1:               Abdollah Jalilian <jalilian@razi.ac.ir>                         ETAS
 2:              Alexander W Blocker <ablocker@gmail.com>                   fastGHQuad
 3:                Alexios Ghalanos <alexios@4dscape.com>                      rmgarch
 4:                           Andy Bosyi <andy@bosyi.com>                  fasteraster
 5:                          Andy Royle <aroyle@usgs.gov>                     unmarked
 6:              Bryan Stanfill <bstanfill2003@gmail.com>                    rotations
 7:  Chibisi Chima-Okereke <chibisi@active-analytics.com>                       bigReg
 8:           Christian Panse <Christian.Panse@gmail.com>                       recmap
 9:                 Clement THERY <corregeous@correg.org>                       CorReg
10:   Clifford Anderson-Bergman <pistacliffcho@gmail.com>                      icenReg
11:                     Dan Dalthorp <ddalthorp@usgs.gov>                       cbinom
12:              Daniel Barreiro Ures <dbu5293@gmail.com>                     binnednp
13:                  David Gerard <gerard.1787@gmail.com>                        updog
14:         David Kepplinger <david.kepplinger@gmail.com>                        pense
15:                   Dootika Vats <D.Vats@warwick.ac.uk>                       mcmcse
16:                Gemma Moran <gmoran@wharton.upenn.edu>                         EMVS
17:                 George Vega Yon <g.vegayon@gmail.com>                  netdiffuseR
18:                          Haiming Zhou <zhouh@niu.edu>                         lpme
19:          Henrik Singmann <singmann+rtdists@gmail.com>                      rtdists
20:            Isaac Gravestock <isaac.gravestock@uzh.ch>                       glmBfp
21:                            James Lo <lojames@usc.edu>                        emIRT
22:               Jan Graffelman <jan.graffelman@upc.edu>                HardyWeinberg
23:                  Jared Murray <jsmurray@stat.cmu.edu>                          bfa
24:         Jean-Benoist Leger <jbleger@bordeaux.inra.fr>                  blockmodels
25: Jean-Romain Roussel <jean-romain.roussel.1@ulaval.ca>                         lidR
26:                      Jeffrey Girard <me@jmgirard.com>                   circumplex
27:                         Jing Peng <pengjing@live.com>                      CoxPlus
28:                      Jonas Rende <jonas.rende@fau.de>                    partialCI
29:        Justin Rory Wishart <justin.wishart@mq.edu.au>                       mwaved
30:                Keven Bluteau <Keven.Bluteau@unine.ch>                      MSGARCH
31:            Krzysztof Drachal <kdrachal@wne.uw.edu.pl>                         fDMA
32:        Leopoldo Catania <leopoldo.catania@econ.au.dk>                          GAS
33:                      Mali Salles <marinacs@gmail.com>                 facilitation
34:               Manuel Koller <koller.manuel@gmail.com>                    robustlmm
35:                       Matt Moores <mmoores@gmail.com>      bayesImageS, serrsBayes
36:                            Max Gordon <max@gforge.se>                        Gmisc
37:           Michael Bedward <michael.bedward@gmail.com>                  packcircles
38:                Michael Dorman <dorman@post.bgu.ac.il>                        nngeo
39:          Michael J. Kane <bigmemoryauthors@gmail.com>                    bigmemory
40:           Miquel De Cáceres <miquelcaceres@gmail.com> medfate, meteoland, vegclust
41:                                              ORPHANED                      geogrid
42:            Oleg Badunenko <oleg.badunenko@port.ac.uk>                         npsf
43:              Pedro Duarte Silva <psilva@porto.ucp.pt>                   MAINT.Data
44:                   Qingyan Xiang <qxiang@illinois.edu>                        basad
45:                         S. Gorsky <s.gorsky@duke.edu>                     MultiFit
46:                          Simon Mak <smak6@gatech.edu>                minimaxdesign
47:                       Simon Schwab <schw4b@gmail.com>                 DGM, multdyn
48:                Stilianos Louca <louca@zoology.ubc.ca>                       castor
49:               Takaya Saito <takaya.saito@outlook.com>                      precrec
50:            Tarik C. Gouhier <tarik.gouhier@gmail.com>                    biwavelet
51:                  Thong Pham <thongpham@thongpham.net>                        PAFit
52:                Tom Reynkens <tomreynkens@hotmail.com>                        ReIns
53:   Tymoteusz Wolodzko <twolodzko+extraDistr@gmail.com>                   extraDistr
54:          Ulrich Bodenhofer <bodenhofer@bioinf.jku.at>                       rococo
55:                   Vinnie Monaco <contact@vmonaco.com>                  frailtySurv
56:                  Xiaofei Wang <xiaofei.wang@yale.edu>                          bcp
57:                   Yaohui Zeng <yaohui-zeng@uiowa.edu>                     biglasso
58:                       Zifei Han <hanzifei1@gmail.com>                       gcKrig
                                               Maintainer                         pkgs
R> 
Member

eddelbuettel commented Sep 16, 2018

New plan. I will email all 50+ maintainers, explaining that their package failed with strict headers and asking that they update within the year. I will also email rcpp-devel.

And come September 2019, we add the #define. Better?

Code

With fails as above, adding

db <- setDT(tools::CRAN_package_db())

maints <- db[ fails[, .(package, version)], .(Package, Version, Maintainer)  ]

## need one special case: one package without accent in name, two with
maints[Maintainer == "Miquel De Caceres <miquelcaceres@gmail.com>",
       Maintainer := "Miquel De Cáceres <miquelcaceres@gmail.com>" ]

setkey(maints, Maintainer)

maints[ , .(pkgs=paste(Package, collapse=", ")), by=Maintainer]

get us these maintainers:

R> maints[ , .(pkgs=paste(Package, collapse=", ")), by=Maintainer]
                                               Maintainer                         pkgs
 1:               Abdollah Jalilian <jalilian@razi.ac.ir>                         ETAS
 2:              Alexander W Blocker <ablocker@gmail.com>                   fastGHQuad
 3:                Alexios Ghalanos <alexios@4dscape.com>                      rmgarch
 4:                           Andy Bosyi <andy@bosyi.com>                  fasteraster
 5:                          Andy Royle <aroyle@usgs.gov>                     unmarked
 6:              Bryan Stanfill <bstanfill2003@gmail.com>                    rotations
 7:  Chibisi Chima-Okereke <chibisi@active-analytics.com>                       bigReg
 8:           Christian Panse <Christian.Panse@gmail.com>                       recmap
 9:                 Clement THERY <corregeous@correg.org>                       CorReg
10:   Clifford Anderson-Bergman <pistacliffcho@gmail.com>                      icenReg
11:                     Dan Dalthorp <ddalthorp@usgs.gov>                       cbinom
12:              Daniel Barreiro Ures <dbu5293@gmail.com>                     binnednp
13:                  David Gerard <gerard.1787@gmail.com>                        updog
14:         David Kepplinger <david.kepplinger@gmail.com>                        pense
15:                   Dootika Vats <D.Vats@warwick.ac.uk>                       mcmcse
16:                Gemma Moran <gmoran@wharton.upenn.edu>                         EMVS
17:                 George Vega Yon <g.vegayon@gmail.com>                  netdiffuseR
18:                          Haiming Zhou <zhouh@niu.edu>                         lpme
19:          Henrik Singmann <singmann+rtdists@gmail.com>                      rtdists
20:            Isaac Gravestock <isaac.gravestock@uzh.ch>                       glmBfp
21:                            James Lo <lojames@usc.edu>                        emIRT
22:               Jan Graffelman <jan.graffelman@upc.edu>                HardyWeinberg
23:                  Jared Murray <jsmurray@stat.cmu.edu>                          bfa
24:         Jean-Benoist Leger <jbleger@bordeaux.inra.fr>                  blockmodels
25: Jean-Romain Roussel <jean-romain.roussel.1@ulaval.ca>                         lidR
26:                      Jeffrey Girard <me@jmgirard.com>                   circumplex
27:                         Jing Peng <pengjing@live.com>                      CoxPlus
28:                      Jonas Rende <jonas.rende@fau.de>                    partialCI
29:        Justin Rory Wishart <justin.wishart@mq.edu.au>                       mwaved
30:                Keven Bluteau <Keven.Bluteau@unine.ch>                      MSGARCH
31:            Krzysztof Drachal <kdrachal@wne.uw.edu.pl>                         fDMA
32:        Leopoldo Catania <leopoldo.catania@econ.au.dk>                          GAS
33:                      Mali Salles <marinacs@gmail.com>                 facilitation
34:               Manuel Koller <koller.manuel@gmail.com>                    robustlmm
35:                       Matt Moores <mmoores@gmail.com>      bayesImageS, serrsBayes
36:                            Max Gordon <max@gforge.se>                        Gmisc
37:           Michael Bedward <michael.bedward@gmail.com>                  packcircles
38:                Michael Dorman <dorman@post.bgu.ac.il>                        nngeo
39:          Michael J. Kane <bigmemoryauthors@gmail.com>                    bigmemory
40:           Miquel De Cáceres <miquelcaceres@gmail.com> medfate, meteoland, vegclust
41:                                              ORPHANED                      geogrid
42:            Oleg Badunenko <oleg.badunenko@port.ac.uk>                         npsf
43:              Pedro Duarte Silva <psilva@porto.ucp.pt>                   MAINT.Data
44:                   Qingyan Xiang <qxiang@illinois.edu>                        basad
45:                         S. Gorsky <s.gorsky@duke.edu>                     MultiFit
46:                          Simon Mak <smak6@gatech.edu>                minimaxdesign
47:                       Simon Schwab <schw4b@gmail.com>                 DGM, multdyn
48:                Stilianos Louca <louca@zoology.ubc.ca>                       castor
49:               Takaya Saito <takaya.saito@outlook.com>                      precrec
50:            Tarik C. Gouhier <tarik.gouhier@gmail.com>                    biwavelet
51:                  Thong Pham <thongpham@thongpham.net>                        PAFit
52:                Tom Reynkens <tomreynkens@hotmail.com>                        ReIns
53:   Tymoteusz Wolodzko <twolodzko+extraDistr@gmail.com>                   extraDistr
54:          Ulrich Bodenhofer <bodenhofer@bioinf.jku.at>                       rococo
55:                   Vinnie Monaco <contact@vmonaco.com>                  frailtySurv
56:                  Xiaofei Wang <xiaofei.wang@yale.edu>                          bcp
57:                   Yaohui Zeng <yaohui-zeng@uiowa.edu>                     biglasso
58:                       Zifei Han <hanzifei1@gmail.com>                       gcKrig
                                               Maintainer                         pkgs
R> 
@kevinushey

This comment has been minimized.

Show comment
Hide comment
@kevinushey

kevinushey Sep 16, 2018

Contributor

Sounds good to me! Thanks for spearheading the effort.

Contributor

kevinushey commented Sep 16, 2018

Sounds good to me! Thanks for spearheading the effort.

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Sep 16, 2018

Member

I wrote up something as basis for the email to rcpp-devel, CRAN and the maintainers which I just sent to rcpp-core (and CC to @romainfrancois). If you are reasing this here and there, please peruse.

Thanks!

Member

eddelbuettel commented Sep 16, 2018

I wrote up something as basis for the email to rcpp-devel, CRAN and the maintainers which I just sent to rcpp-core (and CC to @romainfrancois). If you are reasing this here and there, please peruse.

Thanks!

@UBod

This comment has been minimized.

Show comment
Hide comment
@UBod

UBod Oct 11, 2018

Sorry guys, maybe I'm stupid, but I replaced DBL_EPSILON in my package (rococo) by DOUBLE_EPS in conjunction with setting -DSTRICT_R_HEADERS in Makevars, but I get a compiler error that DOUBLE_EPS is not defined. Have I misunderstood something?

UBod commented Oct 11, 2018

Sorry guys, maybe I'm stupid, but I replaced DBL_EPSILON in my package (rococo) by DOUBLE_EPS in conjunction with setting -DSTRICT_R_HEADERS in Makevars, but I get a compiler error that DOUBLE_EPS is not defined. Have I misunderstood something?

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Oct 11, 2018

Member

I think it is the other way around: DBL_EPSILON is "good" and should be used, DOUBLE_EPS is sloppy and only here because of ancient S headers.

I just added #define STRICT_R_HEADERS so now it wants DBL_EPSILON and eg per this you want the limits.h (or climit) header.

The following diff makes it build and install for me. (As an aside, you still have a lot of SEXP around. You have not needed that for years. I find actual C++ types cleaner to work with.)

edd@rob:/tmp/rococo$ diff -ru rococo.orig/ rococo |head -40
diff -ru rococo.orig/src/rcor_bm.h rococo/src/rcor_bm.h
--- rococo.orig/src/rcor_bm.h   2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_bm.h        2018-10-11 09:35:25.110306046 -0500
@@ -1,6 +1,7 @@
 #ifndef _RCOR_BM_H
 #define _RCOR_BM_H

+#define STRICT_R_HEADERS
 #include <Rcpp.h>

 RcppExport SEXP rcor_matrix_classical(SEXP vx, SEXP r);
diff -ru rococo.orig/src/rcor.cpp rococo/src/rcor.cpp
--- rococo.orig/src/rcor.cpp    2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor.cpp 2018-10-11 09:35:07.942507260 -0500
@@ -1,3 +1,4 @@
+#define STRICT_R_HEADERS
 #include <Rcpp.h>
 #include "tnorms.h"
 #include "rcor.h"
diff -ru rococo.orig/src/rcor_exacttest.cpp rococo/src/rcor_exacttest.cpp
--- rococo.orig/src/rcor_exacttest.cpp  2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_exacttest.cpp       2018-10-11 09:39:52.815183485 -0500
@@ -1,4 +1,6 @@
+#define STRICT_R_HEADERS
 #include <Rcpp.h>
+#include <limits>
 #include "tnorms.h"
 #include "rcor.h"

@@ -139,7 +141,7 @@
     do
     {
        get_sums(mat_x, mat_y, perm, tnorm_fp, &c, &d);
-       gamma[0] = (fabs(c + d) <= DBL_EPSILON ? 0 : (c - d) / (c + d));
+       gamma[0] = (fabs(c + d) <= std::numeric_limits<double>::epsilon() ? 0 : (c - d) / (c + d));

        if (numGamma) permGamma[i] = gamma[0];

diff -ru rococo.orig/src/rcor_exacttest.h rococo/src/rcor_exacttest.h
--- rococo.orig/src/rcor_exacttest.h    2017-07-04 12:43:02.000000000 -0500

--- rococo.orig/src/rcor_exacttest.h    2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_exacttest.h 2018-10-11 09:35:36.466173026 -0500
@@ -1,6 +1,7 @@
 #ifndef _RCOR_EXACTTEST_H
 #define _RCOR_EXACTTEST_H

+#define STRICT_R_HEADERS
 #include <Rcpp.h>

 using namespace Rcpp;
diff -ru rococo.orig/src/rcor.h rococo/src/rcor.h
--- rococo.orig/src/rcor.h      2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor.h   2018-10-11 09:35:44.330080946 -0500
@@ -1,6 +1,7 @@
 #ifndef _RCOR_H
 #define _RCOR_H

+#define STRICT_R_HEADERS
 #include <Rcpp.h>

 using namespace Rcpp;
diff -ru rococo.orig/src/rcor_permtest.cpp rococo/src/rcor_permtest.cpp
--- rococo.orig/src/rcor_permtest.cpp   2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_permtest.cpp        2018-10-11 09:41:16.726209321 -0500
@@ -1,4 +1,6 @@
+#define STRICT_R_HEADERS
 #include <Rcpp.h>
+#include <limits>
 #include "tnorms.h"
 #include "rcor.h"
 #include "rcor_permtest.h"
@@ -66,7 +68,7 @@
        shuffle_in_place(perm);

        get_sums(mat_x, mat_y, perm, tnorm_fp, &c, &d);
-       gamma[0] = (fabs(c + d) <= DBL_EPSILON) ? 0 : (c - d) / (c + d);
+       gamma[0] = (fabs(c + d) <= std::numeric_limits<double>::epsilon()) ? 0 : (c - d) / (c + d);

        if (numGamma) permGamma[i] = gamma[0];

diff -ru rococo.orig/src/rcor_permtest.h rococo/src/rcor_permtest.h
--- rococo.orig/src/rcor_permtest.cpp   2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_permtest.cpp        2018-10-11 09:41:16.726209321 -0500
@@ -1,4 +1,6 @@
+#define STRICT_R_HEADERS
 #include <Rcpp.h>
+#include <limits>
 #include "tnorms.h"
 #include "rcor.h"
 #include "rcor_permtest.h"
@@ -66,7 +68,7 @@
        shuffle_in_place(perm);

        get_sums(mat_x, mat_y, perm, tnorm_fp, &c, &d);
-       gamma[0] = (fabs(c + d) <= DBL_EPSILON) ? 0 : (c - d) / (c + d);
+       gamma[0] = (fabs(c + d) <= std::numeric_limits<double>::epsilon()) ? 0 : (c - d) / (c + d);

        if (numGamma) permGamma[i] = gamma[0];

diff -ru rococo.orig/src/rcor_permtest.h rococo/src/rcor_permtest.h
--- rococo.orig/src/rcor_permtest.h     2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_permtest.h  2018-10-11 09:35:54.513961738 -0500
@@ -1,6 +1,7 @@
 #ifndef _RCOR_PERMTEST_H
 #define _RCOR_PERMTEST_H

+#define STRICT_R_HEADERS
 #include <Rcpp.h>

 using namespace Rcpp;
edd@rob:/tmp/rococo$
Member

eddelbuettel commented Oct 11, 2018

I think it is the other way around: DBL_EPSILON is "good" and should be used, DOUBLE_EPS is sloppy and only here because of ancient S headers.

I just added #define STRICT_R_HEADERS so now it wants DBL_EPSILON and eg per this you want the limits.h (or climit) header.

The following diff makes it build and install for me. (As an aside, you still have a lot of SEXP around. You have not needed that for years. I find actual C++ types cleaner to work with.)

edd@rob:/tmp/rococo$ diff -ru rococo.orig/ rococo |head -40
diff -ru rococo.orig/src/rcor_bm.h rococo/src/rcor_bm.h
--- rococo.orig/src/rcor_bm.h   2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_bm.h        2018-10-11 09:35:25.110306046 -0500
@@ -1,6 +1,7 @@
 #ifndef _RCOR_BM_H
 #define _RCOR_BM_H

+#define STRICT_R_HEADERS
 #include <Rcpp.h>

 RcppExport SEXP rcor_matrix_classical(SEXP vx, SEXP r);
diff -ru rococo.orig/src/rcor.cpp rococo/src/rcor.cpp
--- rococo.orig/src/rcor.cpp    2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor.cpp 2018-10-11 09:35:07.942507260 -0500
@@ -1,3 +1,4 @@
+#define STRICT_R_HEADERS
 #include <Rcpp.h>
 #include "tnorms.h"
 #include "rcor.h"
diff -ru rococo.orig/src/rcor_exacttest.cpp rococo/src/rcor_exacttest.cpp
--- rococo.orig/src/rcor_exacttest.cpp  2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_exacttest.cpp       2018-10-11 09:39:52.815183485 -0500
@@ -1,4 +1,6 @@
+#define STRICT_R_HEADERS
 #include <Rcpp.h>
+#include <limits>
 #include "tnorms.h"
 #include "rcor.h"

@@ -139,7 +141,7 @@
     do
     {
        get_sums(mat_x, mat_y, perm, tnorm_fp, &c, &d);
-       gamma[0] = (fabs(c + d) <= DBL_EPSILON ? 0 : (c - d) / (c + d));
+       gamma[0] = (fabs(c + d) <= std::numeric_limits<double>::epsilon() ? 0 : (c - d) / (c + d));

        if (numGamma) permGamma[i] = gamma[0];

diff -ru rococo.orig/src/rcor_exacttest.h rococo/src/rcor_exacttest.h
--- rococo.orig/src/rcor_exacttest.h    2017-07-04 12:43:02.000000000 -0500

--- rococo.orig/src/rcor_exacttest.h    2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_exacttest.h 2018-10-11 09:35:36.466173026 -0500
@@ -1,6 +1,7 @@
 #ifndef _RCOR_EXACTTEST_H
 #define _RCOR_EXACTTEST_H

+#define STRICT_R_HEADERS
 #include <Rcpp.h>

 using namespace Rcpp;
diff -ru rococo.orig/src/rcor.h rococo/src/rcor.h
--- rococo.orig/src/rcor.h      2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor.h   2018-10-11 09:35:44.330080946 -0500
@@ -1,6 +1,7 @@
 #ifndef _RCOR_H
 #define _RCOR_H

+#define STRICT_R_HEADERS
 #include <Rcpp.h>

 using namespace Rcpp;
diff -ru rococo.orig/src/rcor_permtest.cpp rococo/src/rcor_permtest.cpp
--- rococo.orig/src/rcor_permtest.cpp   2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_permtest.cpp        2018-10-11 09:41:16.726209321 -0500
@@ -1,4 +1,6 @@
+#define STRICT_R_HEADERS
 #include <Rcpp.h>
+#include <limits>
 #include "tnorms.h"
 #include "rcor.h"
 #include "rcor_permtest.h"
@@ -66,7 +68,7 @@
        shuffle_in_place(perm);

        get_sums(mat_x, mat_y, perm, tnorm_fp, &c, &d);
-       gamma[0] = (fabs(c + d) <= DBL_EPSILON) ? 0 : (c - d) / (c + d);
+       gamma[0] = (fabs(c + d) <= std::numeric_limits<double>::epsilon()) ? 0 : (c - d) / (c + d);

        if (numGamma) permGamma[i] = gamma[0];

diff -ru rococo.orig/src/rcor_permtest.h rococo/src/rcor_permtest.h
--- rococo.orig/src/rcor_permtest.cpp   2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_permtest.cpp        2018-10-11 09:41:16.726209321 -0500
@@ -1,4 +1,6 @@
+#define STRICT_R_HEADERS
 #include <Rcpp.h>
+#include <limits>
 #include "tnorms.h"
 #include "rcor.h"
 #include "rcor_permtest.h"
@@ -66,7 +68,7 @@
        shuffle_in_place(perm);

        get_sums(mat_x, mat_y, perm, tnorm_fp, &c, &d);
-       gamma[0] = (fabs(c + d) <= DBL_EPSILON) ? 0 : (c - d) / (c + d);
+       gamma[0] = (fabs(c + d) <= std::numeric_limits<double>::epsilon()) ? 0 : (c - d) / (c + d);

        if (numGamma) permGamma[i] = gamma[0];

diff -ru rococo.orig/src/rcor_permtest.h rococo/src/rcor_permtest.h
--- rococo.orig/src/rcor_permtest.h     2017-07-04 12:43:02.000000000 -0500
+++ rococo/src/rcor_permtest.h  2018-10-11 09:35:54.513961738 -0500
@@ -1,6 +1,7 @@
 #ifndef _RCOR_PERMTEST_H
 #define _RCOR_PERMTEST_H

+#define STRICT_R_HEADERS
 #include <Rcpp.h>

 using namespace Rcpp;
edd@rob:/tmp/rococo$
@UBod

This comment has been minimized.

Show comment
Hide comment
@UBod

UBod Oct 11, 2018

Thanks a lot, Dirk, that solved the issue! I will upload a new version to CRAN in due course.

Regarding your remark about the use of SEXP: yes, I am aware of this. I am actually using C++ types in my newer work, but I currently do not have the time to update my older packages as long as they still work well.

UBod commented Oct 11, 2018

Thanks a lot, Dirk, that solved the issue! I will upload a new version to CRAN in due course.

Regarding your remark about the use of SEXP: yes, I am aware of this. I am actually using C++ types in my newer work, but I currently do not have the time to update my older packages as long as they still work well.

@eddelbuettel

This comment has been minimized.

Show comment
Hide comment
@eddelbuettel

eddelbuettel Oct 11, 2018

Member

Great! And sorry for the false lead earlier. I too thought the define was present was seemingly not. But the numeric_limits<double>::epsilon() is definitely cooler and the way to go. And no worries about old(er) code.

Member

eddelbuettel commented Oct 11, 2018

Great! And sorry for the false lead earlier. I too thought the define was present was seemingly not. But the numeric_limits<double>::epsilon() is definitely cooler and the way to go. And no worries about old(er) code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment