Saikat DebRoy
2003-Oct-10 18:11 UTC
[Rd] number of arguments in .Call function registration
I initially sent this to the biocore mailing list - but it was suggested that r-devel would also find it interesting. Many of us use a macro like #define CALL_DEF(fname, nargs) { #fname, (DL_FUNC)&fname, nargs} for use in function registration for use with .Call. For example, using the example from R Extension manual, if we want to register a C function myCall with three arguments, we will use R_CallMethodDef callMethods[] = { CALL_DEF(myCall, 3), {NULL, NULL, 0} }; instead of using R_CallMethodDef callMethods[] = { {"myCall", (DL_FUNC)&myCall, 3}, {NULL, NULL, 0} }; However, there is no way for the compiler to make sure that myCall is really a function taking three SEXP's and returning a SEXP. I have been using an alternate definition of the CALL_DEF macro which can do that. I have only tested this on gcc but it may work with other compilers. Here is the relevant snippet from my header file. #if defined __GNUC__ && __GNUC__ >= 2 /* * These macros may work with other compilers. However, they are * just for compile time error checking for wrong .Call and * .External function registration and so, need not be used except * with gcc (which is used by the developers). */ #define TEST_CALL_DEF #endif #ifdef TEST_CALL_DEF #define PREPROC_NULL_ARGS(fname, n) (fname)(PREPROC_SEQ_ARGS(n, R_NilValue)) #define PREPROC_SEQ_ARGS(n, v) PREPROC_SEQ_ ## n (v) #define PREPROC_SEQ_0(v) /* nothing */ #define PREPROC_SEQ_1(v) (v) #define PREPROC_SEQ_2(v) (v), (v) #define PREPROC_SEQ_3(v) (v), (v), (v) #define PREPROC_SEQ_4(v) PREPROC_SEQ_2(v), PREPROC_SEQ_2(v) #define PREPROC_SEQ_5(v) PREPROC_SEQ_3(v), PREPROC_SEQ_2(v) #define PREPROC_SEQ_6(v) PREPROC_SEQ_3(v), PREPROC_SEQ_3(v) #define PREPROC_SEQ_7(v) PREPROC_SEQ_4(v), PREPROC_SEQ_3(v) #define PREPROC_SEQ_8(v) PREPROC_SEQ_4(v), PREPROC_SEQ_4(v) #define PREPROC_SEQ_9(v) PREPROC_SEQ_5(v), PREPROC_SEQ_4(v) #define PREPROC_SEQ_10(v) PREPROC_SEQ_5(v), PREPROC_SEQ_5(v) #define PREPROC_SEQ_11(v) PREPROC_SEQ_6(v), PREPROC_SEQ_5(v) #define PREPROC_SEQ_12(v) PREPROC_SEQ_6(v), PREPROC_SEQ_6(v) #define PREPROC_SEQ_13(v) PREPROC_SEQ_7(v), PREPROC_SEQ_6(v) #define PREPROC_SEQ_14(v) PREPROC_SEQ_7(v), PREPROC_SEQ_7(v) #define PREPROC_SEQ_15(v) PREPROC_SEQ_8(v), PREPROC_SEQ_7(v) #define PREPROC_SEQ_16(v) PREPROC_SEQ_8(v), PREPROC_SEQ_8(v) #define PREPROC_SEQ_17(v) PREPROC_SEQ_9(v), PREPROC_SEQ_8(v) #define PREPROC_SEQ_18(v) PREPROC_SEQ_9(v), PREPROC_SEQ_9(v) #define PREPROC_SEQ_19(v) PREPROC_SEQ_10(v), PREPROC_SEQ_9(v) #define PREPROC_SEQ_20(v) PREPROC_SEQ_10(v), PREPROC_SEQ_10(v) #define PREPROC_SEQ_21(v) PREPROC_SEQ_11(v), PREPROC_SEQ_10(v) #define PREPROC_SEQ_22(v) PREPROC_SEQ_11(v), PREPROC_SEQ_11(v) #define PREPROC_SEQ_23(v) PREPROC_SEQ_12(v), PREPROC_SEQ_11(v) #define PREPROC_SEQ_24(v) PREPROC_SEQ_12(v), PREPROC_SEQ_12(v) #define PREPROC_SEQ_25(v) PREPROC_SEQ_13(v), PREPROC_SEQ_12(v) #define PREPROC_SEQ_26(v) PREPROC_SEQ_13(v), PREPROC_SEQ_13(v) #define PREPROC_SEQ_27(v) PREPROC_SEQ_14(v), PREPROC_SEQ_13(v) #define PREPROC_SEQ_28(v) PREPROC_SEQ_14(v), PREPROC_SEQ_14(v) #define PREPROC_SEQ_29(v) PREPROC_SEQ_15(v), PREPROC_SEQ_14(v) #define PREPROC_SEQ_30(v) PREPROC_SEQ_15(v), PREPROC_SEQ_15(v) #define PREPROC_SEQ_31(v) PREPROC_SEQ_16(v), PREPROC_SEQ_15(v) #define PREPROC_SEQ_32(v) PREPROC_SEQ_16(v), PREPROC_SEQ_16(v) #define PREPROC_SEQ_33(v) PREPROC_SEQ_17(v), PREPROC_SEQ_16(v) #define PREPROC_SEQ_34(v) PREPROC_SEQ_17(v), PREPROC_SEQ_17(v) #define PREPROC_SEQ_35(v) PREPROC_SEQ_18(v), PREPROC_SEQ_17(v) #define PREPROC_SEQ_36(v) PREPROC_SEQ_18(v), PREPROC_SEQ_18(v) #define PREPROC_SEQ_37(v) PREPROC_SEQ_19(v), PREPROC_SEQ_18(v) #define PREPROC_SEQ_38(v) PREPROC_SEQ_19(v), PREPROC_SEQ_19(v) #define PREPROC_SEQ_39(v) PREPROC_SEQ_20(v), PREPROC_SEQ_19(v) #define PREPROC_SEQ_40(v) PREPROC_SEQ_20(v), PREPROC_SEQ_20(v) #define PREPROC_SEQ_41(v) PREPROC_SEQ_21(v), PREPROC_SEQ_20(v) #define PREPROC_SEQ_42(v) PREPROC_SEQ_21(v), PREPROC_SEQ_21(v) #define PREPROC_SEQ_43(v) PREPROC_SEQ_22(v), PREPROC_SEQ_21(v) #define PREPROC_SEQ_44(v) PREPROC_SEQ_22(v), PREPROC_SEQ_22(v) #define PREPROC_SEQ_45(v) PREPROC_SEQ_23(v), PREPROC_SEQ_22(v) #define PREPROC_SEQ_46(v) PREPROC_SEQ_23(v), PREPROC_SEQ_23(v) #define PREPROC_SEQ_47(v) PREPROC_SEQ_24(v), PREPROC_SEQ_23(v) #define PREPROC_SEQ_48(v) PREPROC_SEQ_24(v), PREPROC_SEQ_24(v) #define PREPROC_SEQ_49(v) PREPROC_SEQ_25(v), PREPROC_SEQ_24(v) #define PREPROC_SEQ_50(v) PREPROC_SEQ_25(v), PREPROC_SEQ_25(v) #define PREPROC_SEQ_51(v) PREPROC_SEQ_26(v), PREPROC_SEQ_25(v) #define PREPROC_SEQ_52(v) PREPROC_SEQ_26(v), PREPROC_SEQ_26(v) #define PREPROC_SEQ_53(v) PREPROC_SEQ_27(v), PREPROC_SEQ_26(v) #define PREPROC_SEQ_54(v) PREPROC_SEQ_27(v), PREPROC_SEQ_27(v) #define PREPROC_SEQ_55(v) PREPROC_SEQ_28(v), PREPROC_SEQ_27(v) #define PREPROC_SEQ_56(v) PREPROC_SEQ_28(v), PREPROC_SEQ_28(v) #define PREPROC_SEQ_57(v) PREPROC_SEQ_29(v), PREPROC_SEQ_28(v) #define PREPROC_SEQ_58(v) PREPROC_SEQ_29(v), PREPROC_SEQ_29(v) #define PREPROC_SEQ_59(v) PREPROC_SEQ_30(v), PREPROC_SEQ_29(v) #define PREPROC_SEQ_60(v) PREPROC_SEQ_30(v), PREPROC_SEQ_30(v) #define PREPROC_SEQ_61(v) PREPROC_SEQ_31(v), PREPROC_SEQ_30(v) #define PREPROC_SEQ_62(v) PREPROC_SEQ_31(v), PREPROC_SEQ_31(v) #define PREPROC_SEQ_63(v) PREPROC_SEQ_32(v), PREPROC_SEQ_31(v) #define PREPROC_SEQ_64(v) PREPROC_SEQ_32(v), PREPROC_SEQ_32(v) #define PREPROC_SEQ_65(v) PREPROC_SEQ_33(v), PREPROC_SEQ_32(v) /* * Create the .Call registration definition and do a compile time * check to make sure fname is a function taking nargs SEXP's and * returning a SEXP as value. If there is a type mismatch, gcc will * produce a warning. If the number of arguments do not match, gcc * will produce an error. * * This only works if nargs is an integer >= 0 and <= 65. Currently * these are the only valid nargs values for R .Call functions. * * This depends on sizeof() returning a compile time constant without * evaluating its argument. * */ #define CALL_DEF(fname, nargs) \ { #fname, (DL_FUNC)&(fname), \ nargs+sizeof(TYPEOF(PREPROC_NULL_ARGS(fname, nargs)))- \ sizeof(TYPEOF(R_NilValue)) } #endif #ifndef TEST_CALL_DEF #define CALL_DEF(fname, nargs) { #fname, (DL_FUNC)&fname, nargs} #endif #define EXTERNAL_DEF(fname) CALL_DEF(fname, 1)
Duncan Temple Lang
2003-Oct-10 18:54 UTC
[Rd] number of arguments in .Call function registration
Hi Saikat. Nice. The more help we can get from the compiler, the better. Registration provides the first step, and your macro(s) provide an additional level. So it is highly desirable to have that if people are manually creating the registration information. When we put the registration technique into R, the idea was that people would not create the info. manually. Instead, using something like the Slcc package or the translation table generated from gcc/g++, one can programmatically prepare this registration data. In that case, the error handling is not done when the package code is compiled, but when the package is distributed. I have some code to do this using gcc/g++ and Python. (It is the same basic mechanism that I am working on to automatically rewrite package code to support multiple evaluators.) I have to package it up a little better and I never know when I will get a chance to do that. If somebody wants to add your macros to Rdynload.h or somewhere else, that would be great. I have a very slow connection at present, so doing significant checking on different platforms is prohibitive. D. Saikat DebRoy wrote:> I initially sent this to the biocore mailing list - but it was > suggested that r-devel would also find it interesting. > > Many of us use a macro like > > #define CALL_DEF(fname, nargs) { #fname, (DL_FUNC)&fname, nargs} > > for use in function registration for use with .Call. > > For example, using the example from R Extension manual, > if we want to register a C function myCall with three arguments, we > will use > > R_CallMethodDef callMethods[] = { > CALL_DEF(myCall, 3), > {NULL, NULL, 0} > }; > > instead of using > > R_CallMethodDef callMethods[] = { > {"myCall", (DL_FUNC)&myCall, 3}, > {NULL, NULL, 0} > }; > > However, there is no way for the compiler to make sure that myCall is > really a function taking three SEXP's and returning a SEXP. I have been > using an alternate definition of the CALL_DEF macro which can do that. > I have only tested this on gcc but it may work with other compilers. > Here is the relevant snippet from my header file. > > #if defined __GNUC__ && __GNUC__ >= 2 > /* > * These macros may work with other compilers. However, they are > * just for compile time error checking for wrong .Call and > * .External function registration and so, need not be used except > * with gcc (which is used by the developers). > */ > #define TEST_CALL_DEF > #endif > > #ifdef TEST_CALL_DEF > > #define PREPROC_NULL_ARGS(fname, n) (fname)(PREPROC_SEQ_ARGS(n, > R_NilValue)) > > #define PREPROC_SEQ_ARGS(n, v) PREPROC_SEQ_ ## n (v) > > #define PREPROC_SEQ_0(v) /* nothing */ > #define PREPROC_SEQ_1(v) (v) > #define PREPROC_SEQ_2(v) (v), (v) > #define PREPROC_SEQ_3(v) (v), (v), (v) > #define PREPROC_SEQ_4(v) PREPROC_SEQ_2(v), PREPROC_SEQ_2(v) > #define PREPROC_SEQ_5(v) PREPROC_SEQ_3(v), PREPROC_SEQ_2(v) > #define PREPROC_SEQ_6(v) PREPROC_SEQ_3(v), PREPROC_SEQ_3(v) > #define PREPROC_SEQ_7(v) PREPROC_SEQ_4(v), PREPROC_SEQ_3(v) > #define PREPROC_SEQ_8(v) PREPROC_SEQ_4(v), PREPROC_SEQ_4(v) > #define PREPROC_SEQ_9(v) PREPROC_SEQ_5(v), PREPROC_SEQ_4(v) > #define PREPROC_SEQ_10(v) PREPROC_SEQ_5(v), PREPROC_SEQ_5(v) > #define PREPROC_SEQ_11(v) PREPROC_SEQ_6(v), PREPROC_SEQ_5(v) > #define PREPROC_SEQ_12(v) PREPROC_SEQ_6(v), PREPROC_SEQ_6(v) > #define PREPROC_SEQ_13(v) PREPROC_SEQ_7(v), PREPROC_SEQ_6(v) > #define PREPROC_SEQ_14(v) PREPROC_SEQ_7(v), PREPROC_SEQ_7(v) > #define PREPROC_SEQ_15(v) PREPROC_SEQ_8(v), PREPROC_SEQ_7(v) > #define PREPROC_SEQ_16(v) PREPROC_SEQ_8(v), PREPROC_SEQ_8(v) > #define PREPROC_SEQ_17(v) PREPROC_SEQ_9(v), PREPROC_SEQ_8(v) > #define PREPROC_SEQ_18(v) PREPROC_SEQ_9(v), PREPROC_SEQ_9(v) > #define PREPROC_SEQ_19(v) PREPROC_SEQ_10(v), PREPROC_SEQ_9(v) > #define PREPROC_SEQ_20(v) PREPROC_SEQ_10(v), PREPROC_SEQ_10(v) > #define PREPROC_SEQ_21(v) PREPROC_SEQ_11(v), PREPROC_SEQ_10(v) > #define PREPROC_SEQ_22(v) PREPROC_SEQ_11(v), PREPROC_SEQ_11(v) > #define PREPROC_SEQ_23(v) PREPROC_SEQ_12(v), PREPROC_SEQ_11(v) > #define PREPROC_SEQ_24(v) PREPROC_SEQ_12(v), PREPROC_SEQ_12(v) > #define PREPROC_SEQ_25(v) PREPROC_SEQ_13(v), PREPROC_SEQ_12(v) > #define PREPROC_SEQ_26(v) PREPROC_SEQ_13(v), PREPROC_SEQ_13(v) > #define PREPROC_SEQ_27(v) PREPROC_SEQ_14(v), PREPROC_SEQ_13(v) > #define PREPROC_SEQ_28(v) PREPROC_SEQ_14(v), PREPROC_SEQ_14(v) > #define PREPROC_SEQ_29(v) PREPROC_SEQ_15(v), PREPROC_SEQ_14(v) > #define PREPROC_SEQ_30(v) PREPROC_SEQ_15(v), PREPROC_SEQ_15(v) > #define PREPROC_SEQ_31(v) PREPROC_SEQ_16(v), PREPROC_SEQ_15(v) > #define PREPROC_SEQ_32(v) PREPROC_SEQ_16(v), PREPROC_SEQ_16(v) > #define PREPROC_SEQ_33(v) PREPROC_SEQ_17(v), PREPROC_SEQ_16(v) > #define PREPROC_SEQ_34(v) PREPROC_SEQ_17(v), PREPROC_SEQ_17(v) > #define PREPROC_SEQ_35(v) PREPROC_SEQ_18(v), PREPROC_SEQ_17(v) > #define PREPROC_SEQ_36(v) PREPROC_SEQ_18(v), PREPROC_SEQ_18(v) > #define PREPROC_SEQ_37(v) PREPROC_SEQ_19(v), PREPROC_SEQ_18(v) > #define PREPROC_SEQ_38(v) PREPROC_SEQ_19(v), PREPROC_SEQ_19(v) > #define PREPROC_SEQ_39(v) PREPROC_SEQ_20(v), PREPROC_SEQ_19(v) > #define PREPROC_SEQ_40(v) PREPROC_SEQ_20(v), PREPROC_SEQ_20(v) > #define PREPROC_SEQ_41(v) PREPROC_SEQ_21(v), PREPROC_SEQ_20(v) > #define PREPROC_SEQ_42(v) PREPROC_SEQ_21(v), PREPROC_SEQ_21(v) > #define PREPROC_SEQ_43(v) PREPROC_SEQ_22(v), PREPROC_SEQ_21(v) > #define PREPROC_SEQ_44(v) PREPROC_SEQ_22(v), PREPROC_SEQ_22(v) > #define PREPROC_SEQ_45(v) PREPROC_SEQ_23(v), PREPROC_SEQ_22(v) > #define PREPROC_SEQ_46(v) PREPROC_SEQ_23(v), PREPROC_SEQ_23(v) > #define PREPROC_SEQ_47(v) PREPROC_SEQ_24(v), PREPROC_SEQ_23(v) > #define PREPROC_SEQ_48(v) PREPROC_SEQ_24(v), PREPROC_SEQ_24(v) > #define PREPROC_SEQ_49(v) PREPROC_SEQ_25(v), PREPROC_SEQ_24(v) > #define PREPROC_SEQ_50(v) PREPROC_SEQ_25(v), PREPROC_SEQ_25(v) > #define PREPROC_SEQ_51(v) PREPROC_SEQ_26(v), PREPROC_SEQ_25(v) > #define PREPROC_SEQ_52(v) PREPROC_SEQ_26(v), PREPROC_SEQ_26(v) > #define PREPROC_SEQ_53(v) PREPROC_SEQ_27(v), PREPROC_SEQ_26(v) > #define PREPROC_SEQ_54(v) PREPROC_SEQ_27(v), PREPROC_SEQ_27(v) > #define PREPROC_SEQ_55(v) PREPROC_SEQ_28(v), PREPROC_SEQ_27(v) > #define PREPROC_SEQ_56(v) PREPROC_SEQ_28(v), PREPROC_SEQ_28(v) > #define PREPROC_SEQ_57(v) PREPROC_SEQ_29(v), PREPROC_SEQ_28(v) > #define PREPROC_SEQ_58(v) PREPROC_SEQ_29(v), PREPROC_SEQ_29(v) > #define PREPROC_SEQ_59(v) PREPROC_SEQ_30(v), PREPROC_SEQ_29(v) > #define PREPROC_SEQ_60(v) PREPROC_SEQ_30(v), PREPROC_SEQ_30(v) > #define PREPROC_SEQ_61(v) PREPROC_SEQ_31(v), PREPROC_SEQ_30(v) > #define PREPROC_SEQ_62(v) PREPROC_SEQ_31(v), PREPROC_SEQ_31(v) > #define PREPROC_SEQ_63(v) PREPROC_SEQ_32(v), PREPROC_SEQ_31(v) > #define PREPROC_SEQ_64(v) PREPROC_SEQ_32(v), PREPROC_SEQ_32(v) > #define PREPROC_SEQ_65(v) PREPROC_SEQ_33(v), PREPROC_SEQ_32(v) > > > /* > * Create the .Call registration definition and do a compile time > * check to make sure fname is a function taking nargs SEXP's and > * returning a SEXP as value. If there is a type mismatch, gcc will > * produce a warning. If the number of arguments do not match, gcc > * will produce an error. > * > * This only works if nargs is an integer >= 0 and <= 65. Currently > * these are the only valid nargs values for R .Call functions. > * > * This depends on sizeof() returning a compile time constant without > * evaluating its argument. > * > */ > #define CALL_DEF(fname, nargs) \ > { #fname, (DL_FUNC)&(fname), \ > nargs+sizeof(TYPEOF(PREPROC_NULL_ARGS(fname, nargs)))- \ > sizeof(TYPEOF(R_NilValue)) } > > #endif > > #ifndef TEST_CALL_DEF > #define CALL_DEF(fname, nargs) { #fname, (DL_FUNC)&fname, nargs} > #endif > > #define EXTERNAL_DEF(fname) CALL_DEF(fname, 1) > > ______________________________________________ > R-devel@stat.math.ethz.ch mailing list > https://www.stat.math.ethz.ch/mailman/listinfo/r-devel-- _______________________________________________________________ Duncan Temple Lang duncan@research.bell-labs.com Bell Labs, Lucent Technologies office: (908)582-3217 700 Mountain Avenue, Room 2C-259 fax: (908)582-3340 Murray Hill, NJ 07974-2070 http://cm.bell-labs.com/stat/duncan