y combinator and the C preprocessor - c
As far as I know y combinator is useful if you want to write a recursive function without using recursion explicitly. The C preprocessor does not support recursion. Can we implement the y combinator in C preprocessor in order to support recursion?
Thanks.
In fact this is completely possible, please see my project CSP Git Repo, this is a LISP interpreter COMPLETELY implemented on C macro preprocessor, and of course you can implement Y combinator on it.
Hope you to and some tests/example if you are interested in it.
Here is the most relevant part. (from csp.h)
It successfully implements closure and lambda, which gives support for implementing fixed point combinators.
#define EVAL_e(x) x
#define _be(y) y)
#define ZIP(x) _n() (x,_be
#define _PAIR(x,y) _e( __PAIR(x,y))
#define __PAIR(x,y) _E COND((NULL SAFE_CDR(x)((SAFE_CAR(x) SAFE_CAR(y))))((T)((SAFE_CAR (x) SAFE_CAR (y)) _e(_PAIR2(_e SAFE_CDR(x),_e SAFE_CDR(y))))))
#define _PAIR2(x,y) _E COND((NULL SAFE_CDR(x)((SAFE_CAR(x) SAFE_CAR(y))))((T)((SAFE_CAR (x) SAFE_CAR (y)) _e(_PAIR3(_e SAFE_CDR(x),_e SAFE_CDR(y))))))
#define _PAIR3(x,y) _E COND((NULL SAFE_CDR(x)((SAFE_CAR(x) SAFE_CAR(y))))((T)((SAFE_CAR (x) SAFE_CAR (y)) _e(_PAIR4(_e SAFE_CDR(x),_e SAFE_CDR(y))))))
#define _PAIR4(x,y) _E COND((NULL SAFE_CDR(x)((SAFE_CAR(x) SAFE_CAR(y))))((T)((SAFE_CAR (x) SAFE_CAR (y)) _e(DELAY_INT_54(__PAIR_R) ()(SAFE_CDR(x) SAFE_CDR(y))))))
#define __PAIR_R() _$pair
#define TEST_R() TEST
#define TEST(x) test
#define _PAIR_e(x) x
#define _$pair(x) _PAIR_e(_PAIR ZIP x)
#define PAIR_EVAL(...) PAIR_EVAL2(PAIR_EVAL2(PAIR_EVAL2(__VA_ARGS__)))
#define PAIR_EVAL2(...) PAIR_EVAL3(PAIR_EVAL3(PAIR_EVAL3(__VA_ARGS__)))
#define PAIR_EVAL3(...) PAIR_EVAL4(PAIR_EVAL4(PAIR_EVAL4(__VA_ARGS__)))
#define PAIR_EVAL4(...) PAIR_EVAL_E(PAIR_EVAL_E(PAIR_EVAL_E(__VA_ARGS__)))
#define PAIR_EVAL_E(...) __VA_ARGS__
#define $pair(x) PAIR_EVAL(_$pair(x))
#define $zipped_evlis_R() $zipped_evlis
#define EVLIS_e_R() EVLIS_e
#define EVLIS_e(x) x
#define _EVLIS_ZIP(...) _n() (__VA_ARGS__,_BE
#define _BE(...) __VA_ARGS__)
#define _EVLIS_R() _EVLIS
#define _EVLIS_E(...) __VA_ARGS__
#define _EVLIS_N(...)
#define _EVLIS_B _EVLIS_E (_EVLIS_N,_EVLIS_E(_EVLIS_N,_EVLIS_N))
#define ___EVLIS(a,b,k,...) k($zipped_eval((b),(a)) DELAY_INT_2(_EVLIS_R)()(a))
#define __EVLIS(a,b,...) ___EVLIS(a,b,__VA_ARGS__ _EVLIS_E)
#define _EVLIS_EVAL_E(...) __VA_ARGS__
#define _EVLIS_EVAL_5(...) _EVLIS_EVAL_E(_EVLIS_EVAL_E(_EVLIS_EVAL_E(__VA_ARGS__)))
#define _EVLIS_EVAL_4(...) _EVLIS_EVAL_5(_EVLIS_EVAL_5(_EVLIS_EVAL_5(__VA_ARGS__)))
#define _EVLIS_EVAL_3(...) _EVLIS_EVAL_4(_EVLIS_EVAL_4(_EVLIS_EVAL_4(__VA_ARGS__)))
#define _EVLIS_EVAL_2(...) _EVLIS_EVAL_3(_EVLIS_EVAL_3(_EVLIS_EVAL_3(__VA_ARGS__)))
#define _EVLIS_EVAL(...) _EVLIS_EVAL_2(_EVLIS_EVAL_2(_EVLIS_EVAL_2(__VA_ARGS__)))
#define _EVLIS(x) __EVLIS _EVLIS_ZIP(x)
#define $zipped_evlis(x,y) _EVLIS_EVAL(_EVLIS y x (_EVLIS_B))
#define $zipped_eval_R() $zipped_eval
#define $zipped_eval(e,a) /**sth irrelevant**/
($eq(SAFE_CAR SAFE_CAR e (lambda))\
(DELAY_INT_26(EVAL_e_R)() DELAY_INT_23($zipped_eval_R)()(\
EVAL_e(EVAL_e(EVAL_e(EVAL_e(SAFE_CAR SAFE_CDR SAFE_CDR SAFE_CAR e)))),\
EVAL_e(APPEND DELAY_INT_13($pair_R)()(EVAL_e(EVAL_e(EVAL_e(SAFE_CAR SAFE_CDR SAFE_CAR e)))\
(DELAY_INT_19($zipped_evlis_R)()(EVAL_e(_e EVAL_e(SAFE_CDR e)), a)))a)))\
/**end of eval recursion**/
#define $pair_R() $pair
#define EVAL_e_R() EVAL_e
#define $eval_E(...) __VA_ARGS__
#define $eval_expand5(...) $eval_E($eval_E($eval_E(__VA_ARGS__)))
#define $eval_expand4(...) $eval_expand5($eval_expand5($eval_expand5(__VA_ARGS__)))
#define $eval_expand3(...) $eval_expand4($eval_expand4($eval_expand4(__VA_ARGS__)))
#define $eval_expand2(...) $eval_expand3($eval_expand3($eval_expand3(__VA_ARGS__)))
#define $eval_expand(...) $eval_expand2($eval_expand2($eval_expand2(__VA_ARGS__)))
#define $zeval(x,y) $eval_expand($zipped_eval(x,y))
Y combinator is a higher-order function and it needs a higher-order function support in the language to implement an explicit recursion substitution. Therefore, for particular tasks this can be done in Scheme, SML and other functional languages.
Let's talk about C preprocessor later, but what about C itself? It could be possible to use higher order functions in C as we can pass function references as function arguments and return them to emulate higher-order functions. However, the lack of closures support in the language does not allow to implement Y combinator.
As preprocessor is even more restrictive that C, there is no way to implement Y combinator which is based on lambda calculus concepts. The application of Y combinator can be achieved only in functional programming languages with higher-order functions full support.
C preprocessor is very straightforward as it does only string substitution in a text. I would not try to apply any concepts from lambda calculus and functional programming to it.
Related
How to include filename created by a macro
I have the following define: #define MY_CLASS MyClass I'm trying to make a macro that will use MY_CLASS to expand to: #include "MyClass.h" Something like (according to this answer): #define MY_CLASS MyClass #define FILE_EXT h #define M_CONC(A, B) M_CONC_(A, B) #define M_CONC_(A, B) A##B #define APP_BUILD M_CONC(MY_CLASS, M_CONC(.,FILE_EXT)) #include APP_BUILD That one doesn't work though... I get these 3 errors: Expected "FILENAME" or <FILENAME> Pasting formed '.h', an invalid preprocessing token Pasting formed 'MyClass.', an invalid preprocessing token Is it possible to do it somehow?
As to your Question: If there's a more elegant / quicker (maybe 1 less macro definition?) solution #define STRINGIFY(X) #X #define FILE_H(X) STRINGIFY(X.h) //usage: #define MY_CLASS MyClass #include FILE_H(MY_CLASS) //expands to: #include "MyCLass.h" //or #define ANOTHER_CLASS AnotherClass #include FILE_H(ANOTHER_CLASS) //expands to: #include "AnotherClass.h" Well, if you need concatenation, then you have to add two extra lines: #define CONCAT(A,B) CONCAT_(A,B) #define CONCAT_(A,B) A##B //could be extended like this: //#define CONCAT3(A,B,C) CONCAT(CONCAT(A,B),C) //...
I was referred to this post in the comments (thanks #sj95126) which I originally missed. It didn't work as-is but I was able to deduce the right answer from it rather quickly... This works: #define MY_CLASS MyClass #define EMPTY #define MACRO1(x) #x #define MACRO2(x, y) MACRO1(x##y.h) #define MACRO3(x, y) MACRO2(x, y) #include MACRO3(EMPTY, MY_CLASS) If there's a more elegant / quicker (maybe 1 less macro definition?) solution I would be happy accept a different answer.
Is it possible to process math calculation before stringify a macro?
I look through a lot of examples online about macro stringification but can't find something similar. I currently have the definitions as below. #define PIN_A (0+1) #define PIN_B (0+2) #define PIN_C (0+3) #define str(x) #x #define xstr(x) str(x) #define PIN_DEF(x) { #x, xstr(PIN_ ## x) } The output of PIN_DEF(A) will become { "A", "(0+1)" } However, what I really need is { "A", "1" } Is it even possible? :/
Yes, it's possible. #include <boost/preprocessor/arithmetic.hpp> #define PIN_A BOOST_PP_ADD(0,1) #define PIN_B BOOST_PP_ADD(0,2) #define PIN_C BOOST_PP_ADD(0,3) #define str(x) #x #define xstr(x) str(x) #define PIN_DEF(x) { #x, xstr(PIN_##x) } PIN_DEF(A) Keep in mind boost preprocessor's arithmetic macros saturate at 256. Caveats The preprocessor can also evaluate expressions, but the only means of doing so is to invoke preprocessor conditional directives (such as #if <expression>/#elif <expression>). You can make a useful expression evaluator out of this, with usage limitations, but it doesn't seem to fit this use case. Macro math needs to be adopted to macro usage (and essentially implemented from scratch), so operations must be implemented in terms of macro calls.
Using a single define statement with include guards for function-like macros?
Is there a way to use only one define statement for this header, without changing the function-like macro into a function? my.h file: #ifndef MY_H #define MY_H #define MIN(x, y) ((x) > (y) ? (y) : (x)) #endif For example, I was able to do the following for a constant: pi.h file: #ifndef PI #define PI 3.14159 #endif I also am aware of the warnings in regards to using function-like macros from posts like: https://stackoverflow.com/a/15575690/4803039 I just want to see if there is a more optimal/refactored way. It just seems weird to include an additional #define statement that defines the rest of the header body, when the header body only includes a #define statement itself.
This is what you want: #ifndef MIN #define MIN(x, y) ((x) > (y) ? (y) : (x)) #endif
Your approach would be fine - it's sufficient to guard against doubly defining macro. Adding a definition guard is usually useful if you want to protect an entire file. This serves to both shorten the code (as you don't have to guard each macro independently) and to make sure you have consistent definitions (e.g., if you want to make sure MIN and MAX are defined together). E.g.: #ifndef MY_H #define MY_H #define MIN(x, y) ((x) > (y) ? (y) : (x)) #define MAX(x, y) ((x) < (y) ? (y) : (x)) #define PI 3.14159 #endif If you just have a single macro/constant you want to define, you can guard it by its own definition, like #Danh suggested.
how to judge whether a macro function is defined or not?
Here is a header file, // a.h #ifndef _A_H_ #define _A_H_ #ifndef MACRO_FUNC #define MACRO_FUNC(X, Y) (X * Y + X - Y) #endif #endif The above code is how I judge whether macro function MACRO_FUNC is defined or not. Is that the right way to go?
Yes this is the correct way to do it. Another way to ensure your own implementation is used is to undefine any previous definition first: #ifdef MACRO_FUNC # undef MACRO_FUNC #endif #define MACRO_FUNC(X, Y) (X * Y + X - Y)
Yes, #ifdef or #ifndef are the correct ways to test for a macro being defined. Note that you can also use #undef followed by #define to replace any existing definition. #ifdef MACRO_FUNC #undef MACRO_FUNC #endif #define MACRO_FUNC(X, Y) (X * Y + X - Y) Also, your sample macro would be better expressed as #define MACRO_FUNC((X), (Y)) ((X) * (Y) + (X) - (Y)) Consider what'd happen if you called MACRO_FUNC(somevar - 1, othervar + 1) if the reasons for this aren't clear.
This is the right way what you did
Yes, Since macros are evaluated by the pre-processor you need to use pre-processor directives #ifdef or #ifndef to check whether one exists.
Yes, it is how you check if the macro is already defined or not. Notice that identifiers starting with _[A-Z] are reserved for the implementation. Change _A_H_ with A_H_ for example.
How to write a while loop with the C preprocessor?
I am asking this question from an educational/hacking point of view, (I wouldn't really want to code like this). Is it possible to implement a while loop only using C preprocessor directives. I understand that macros cannot be expanded recursively, so how would this be accomplished?
If you want to implement a while loop, you will need to use recursion in the preprocessor. The easiest way to do recursion is to use a deferred expression. A deferred expression is an expression that requires more scans to fully expand: #define EMPTY() #define DEFER(id) id EMPTY() #define OBSTRUCT(id) id DEFER(EMPTY)() #define EXPAND(...) __VA_ARGS__ #define A() 123 A() // Expands to 123 DEFER(A)() // Expands to A () because it requires one more scan to fully expand EXPAND(DEFER(A)()) // Expands to 123, because the EXPAND macro forces another scan Why is this important? Well when a macro is scanned and expanding, it creates a disabling context. This disabling context will cause a token, that refers to the currently expanding macro, to be painted blue. Thus, once its painted blue, the macro will no longer expand. This is why macros don't expand recursively. However, a disabling context only exists during one scan, so by deferring an expansion we can prevent our macros from becoming painted blue. We will just need to apply more scans to the expression. We can do that using this EVAL macro: #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) #define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__))) #define EVAL5(...) __VA_ARGS__ Next, we define some operators for doing some logic(such as if, etc): #define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__) #define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ #define CHECK_N(x, n, ...) n #define CHECK(...) CHECK_N(__VA_ARGS__, 0,) #define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x)) #define NOT_0 ~, 1, #define COMPL(b) PRIMITIVE_CAT(COMPL_, b) #define COMPL_0 1 #define COMPL_1 0 #define BOOL(x) COMPL(NOT(x)) #define IIF(c) PRIMITIVE_CAT(IIF_, c) #define IIF_0(t, ...) __VA_ARGS__ #define IIF_1(t, ...) t #define IF(c) IIF(BOOL(c)) Now with all these macros we can write a recursive WHILE macro. We use a WHILE_INDIRECT macro to refer back to itself recursively. This prevents the macro from being painted blue, since it will expand on a different scan(and using a different disabling context). The WHILE macro takes a predicate macro, an operator macro, and a state(which is the variadic arguments). It keeps applying this operator macro to the state until the predicate macro returns false(which is 0). #define WHILE(pred, op, ...) \ IF(pred(__VA_ARGS__)) \ ( \ OBSTRUCT(WHILE_INDIRECT) () \ ( \ pred, op, op(__VA_ARGS__) \ ), \ __VA_ARGS__ \ ) #define WHILE_INDIRECT() WHILE For demonstration purposes, we are just going to create a predicate that checks when number of arguments are 1: #define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N #define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1) #define IS_1(x) CHECK(PRIMITIVE_CAT(IS_1_, x)) #define IS_1_1 ~, 1, #define PRED(x, ...) COMPL(IS_1(NARGS(__VA_ARGS__))) Next we create an operator, which we will just concat two tokens. We also create a final operator(called M) that will process the final output: #define OP(x, y, ...) CAT(x, y), __VA_ARGS__ #define M(...) CAT(__VA_ARGS__) Then using the WHILE macro: M(EVAL(WHILE(PRED, OP, x, y, z))) //Expands to xyz Of course, any kind of predicate or operator can be passed to it.
Take a look at the Boost preprocessor library, which allows you to write loops in the preprocessor, and much more.
You use recursive include files. Unfortunately, you can't iterate the loop more than the maximum depth that the preprocessor allows. It turns out that C++ templates are Turing Complete and can be used in similar ways. Check out Generative Programming
I use meta-template programming for this purpose, its fun once you get a hang of it. And very useful at times when used with discretion. Because as mentioned its turing complete, to the point where you can even cause the compiler to get into an infinite loop, or stack-overflow! There is nothing like going to get some coffee just to find your compilation is using up 30+ gigabytes of memory and all the CPU to compile your infinite loop code!
well, not that it's a while loop, but a counter loop, nonetheless the loop is possible in clean CPP (no templates and no C++) #ifdef pad_always #define pad(p,f) p##0 #else #define pad0(p,not_used) p #define pad1(p,not_used) p##0 #define pad(p,f) pad##f(p,) #endif // f - padding flag // p - prefix so far // a,b,c - digits // x - action to invoke #define n0(p,x) #define n1(p,x) x(p##1) #define n2(p,x) n1(p,x) x(p##2) #define n3(p,x) n2(p,x) x(p##3) #define n4(p,x) n3(p,x) x(p##4) #define n5(p,x) n4(p,x) x(p##5) #define n6(p,x) n5(p,x) x(p##6) #define n7(p,x) n6(p,x) x(p##7) #define n8(p,x) n7(p,x) x(p##8) #define n9(p,x) n8(p,x) x(p##9) #define n00(f,p,a,x) n##a(pad(p,f),x) #define n10(f,p,a,x) n00(f,p,9,x) x(p##10) n##a(p##1,x) #define n20(f,p,a,x) n10(f,p,9,x) x(p##20) n##a(p##2,x) #define n30(f,p,a,x) n20(f,p,9,x) x(p##30) n##a(p##3,x) #define n40(f,p,a,x) n30(f,p,9,x) x(p##40) n##a(p##4,x) #define n50(f,p,a,x) n40(f,p,9,x) x(p##50) n##a(p##5,x) #define n60(f,p,a,x) n50(f,p,9,x) x(p##60) n##a(p##6,x) #define n70(f,p,a,x) n60(f,p,9,x) x(p##70) n##a(p##7,x) #define n80(f,p,a,x) n70(f,p,9,x) x(p##80) n##a(p##8,x) #define n90(f,p,a,x) n80(f,p,9,x) x(p##90) n##a(p##9,x) #define n000(f,p,a,b,x) n##a##0(f,pad(p,f),b,x) #define n100(f,p,a,b,x) n000(f,p,9,9,x) x(p##100) n##a##0(1,p##1,b,x) #define n200(f,p,a,b,x) n100(f,p,9,9,x) x(p##200) n##a##0(1,p##2,b,x) #define n300(f,p,a,b,x) n200(f,p,9,9,x) x(p##300) n##a##0(1,p##3,b,x) #define n400(f,p,a,b,x) n300(f,p,9,9,x) x(p##400) n##a##0(1,p##4,b,x) #define n500(f,p,a,b,x) n400(f,p,9,9,x) x(p##500) n##a##0(1,p##5,b,x) #define n600(f,p,a,b,x) n500(f,p,9,9,x) x(p##600) n##a##0(1,p##6,b,x) #define n700(f,p,a,b,x) n600(f,p,9,9,x) x(p##700) n##a##0(1,p##7,b,x) #define n800(f,p,a,b,x) n700(f,p,9,9,x) x(p##800) n##a##0(1,p##8,b,x) #define n900(f,p,a,b,x) n800(f,p,9,9,x) x(p##900) n##a##0(1,p##9,b,x) #define n0000(f,p,a,b,c,x) n##a##00(f,pad(p,f),b,c,x) #define n1000(f,p,a,b,c,x) n0000(f,p,9,9,9,x) x(p##1000) n##a##00(1,p##1,b,c,x) #define n2000(f,p,a,b,c,x) n1000(f,p,9,9,9,x) x(p##2000) n##a##00(1,p##2,b,c,x) #define n3000(f,p,a,b,c,x) n2000(f,p,9,9,9,x) x(p##3000) n##a##00(1,p##3,b,c,x) #define n4000(f,p,a,b,c,x) n3000(f,p,9,9,9,x) x(p##4000) n##a##00(1,p##4,b,c,x) #define n5000(f,p,a,b,c,x) n4000(f,p,9,9,9,x) x(p##5000) n##a##00(1,p##5,b,c,x) #define n6000(f,p,a,b,c,x) n5000(f,p,9,9,9,x) x(p##6000) n##a##00(1,p##6,b,c,x) #define n7000(f,p,a,b,c,x) n6000(f,p,9,9,9,x) x(p##7000) n##a##00(1,p##7,b,c,x) #define n8000(f,p,a,b,c,x) n7000(f,p,9,9,9,x) x(p##8000) n##a##00(1,p##8,b,c,x) #define n9000(f,p,a,b,c,x) n8000(f,p,9,9,9,x) x(p##9000) n##a##00(1,p##9,b,c,x) #define n00000(f,p,a,b,c,d,x) n##a##000(f,pad(p,f),b,c,d,x) #define n10000(f,p,a,b,c,d,x) n00000(f,p,9,9,9,9,x) x(p##10000) n##a##000(1,p##1,b,c,d,x) #define n20000(f,p,a,b,c,d,x) n10000(f,p,9,9,9,9,x) x(p##20000) n##a##000(1,p##2,b,c,d,x) #define n30000(f,p,a,b,c,d,x) n20000(f,p,9,9,9,9,x) x(p##30000) n##a##000(1,p##3,b,c,d,x) #define n40000(f,p,a,b,c,d,x) n30000(f,p,9,9,9,9,x) x(p##40000) n##a##000(1,p##4,b,c,d,x) #define n50000(f,p,a,b,c,d,x) n40000(f,p,9,9,9,9,x) x(p##50000) n##a##000(1,p##5,b,c,d,x) #define n60000(f,p,a,b,c,d,x) n50000(f,p,9,9,9,9,x) x(p##60000) n##a##000(1,p##6,b,c,d,x) #define n70000(f,p,a,b,c,d,x) n60000(f,p,9,9,9,9,x) x(p##70000) n##a##000(1,p##7,b,c,d,x) #define n80000(f,p,a,b,c,d,x) n70000(f,p,9,9,9,9,x) x(p##80000) n##a##000(1,p##8,b,c,d,x) #define n90000(f,p,a,b,c,d,x) n80000(f,p,9,9,9,9,x) x(p##90000) n##a##000(1,p##9,b,c,d,x) #define cycle5(c1,c2,c3,c4,c5,x) n##c1##0000(0,,c2,c3,c4,c5,x) #define cycle4(c1,c2,c3,c4,x) n##c1##000(0,,c2,c3,c4,x) #define cycle3(c1,c2,c3,x) n##c1##00(0,,c2,c3,x) #define cycle2(c1,c2,x) n##c1##0(0,,c2,x) #define cycle1(c1,x) n##c1(,x) #define concat(a,b,c) a##b##c #define ck(arg) a[concat(,arg,-1)]++; #define SIZEOF(x) (sizeof(x) / sizeof((x)[0])) void check5(void) { int i, a[32769]; for (i = 0; i < SIZEOF(a); i++) a[i]=0; cycle5(3,2,7,6,9,ck); for (i = 0; i < SIZEOF(a); i++) if (a[i] != 1) printf("5: [%d] = %d\n", i+1, a[i]); }
Here's an abuse of the rules that would get it done legally. Write your own C preprocessor. Make it interpret some #pragma directives the way you want.
I found this scheme useful when the compiler got cranky and wouldn't unroll certain loops for me #define REPEAT20(x) { x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;} REPEAT20( val = pleaseconverge(val) ); But IMHO, if you need something much more complicated than that, then you should write your own pre-preprocessor. Your pre-preprocessor could for instance generate an appropriate header file for you, and it is easy enough to include this step in a Makefile to have everything compile smoothly by a single command. I've done it.