C 전처리기로 while 루프를 작성하는 방법은 무엇입니까?
저는 교육적 / 해킹 관점에서이 질문을하고 있습니다. (이렇게 코딩하고 싶지는 않습니다).
C 전 처리기 지시문 만 사용하여 while 루프를 구현할 수 있습니까? 매크로는 재귀 적으로 확장 할 수 없다는 것을 알고 있는데, 어떻게이 작업을 수행 할 수 있습니까?
전 처리기 등에서 루프를 작성할 수 있는 Boost 전 처리기 라이브러리를 살펴보십시오 .
while 루프를 구현하려면 전 처리기에서 재귀를 사용해야합니다. 재귀를 수행하는 가장 쉬운 방법은 지연된 표현식을 사용하는 것입니다. 지연된 표현식은 완전히 확장하기 위해 더 많은 스캔이 필요한 표현식입니다.
#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
이것이 왜 중요한가요? 매크로가 스캔되고 확장되면 비활성화 컨텍스트가 생성됩니다. 이 비활성화 컨텍스트는 현재 확장중인 매크로를 참조하는 토큰이 파란색으로 표시되도록합니다. 따라서 파란색으로 칠해지면 매크로가 더 이상 확장되지 않습니다. 이것이 매크로가 재귀 적으로 확장되지 않는 이유입니다. 그러나 비활성화 컨텍스트는 한 번의 스캔 동안에 만 존재하므로 확장을 연기하여 매크로가 파란색으로 칠 해지는 것을 방지 할 수 있습니다. 식에 더 많은 스캔을 적용하면됩니다. 다음 EVAL매크로 를 사용하여 수행 할 수 있습니다 .
#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__
다음으로, 로직 (예 : if 등)을 수행하기위한 연산자를 정의합니다.
#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))
이제 이러한 모든 매크로를 사용하여 재귀 WHILE매크로를 작성할 수 있습니다 . 우리는 WHILE_INDIRECT자신을 재귀 적으로 참조 하기 위해 매크로를 사용합니다 . 이렇게하면 매크로가 다른 스캔에서 확장되고 다른 비활성화 컨텍스트를 사용하므로 매크로가 파란색으로 칠해지지 않습니다. WHILE매크로합니다 (가변 인수이다) 술어 매크로 운영자 매크로 및 상태 걸린다. 조건 자 매크로가 false (0)를 반환 할 때까지이 연산자 매크로를 상태에 계속 적용합니다.
#define WHILE(pred, op, ...) \
IF(pred(__VA_ARGS__)) \
( \
OBSTRUCT(WHILE_INDIRECT) () \
( \
pred, op, op(__VA_ARGS__) \
), \
__VA_ARGS__ \
)
#define WHILE_INDIRECT() WHILE
데모를 위해 인수 수가 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__)))
다음으로 두 개의 토큰을 연결하는 연산자를 만듭니다. 또한 M최종 출력을 처리 할 최종 연산자 (라고 함 )를 만듭니다 .
#define OP(x, y, ...) CAT(x, y), __VA_ARGS__
#define M(...) CAT(__VA_ARGS__)
그런 다음 WHILE매크로를 사용합니다 .
M(EVAL(WHILE(PRED, OP, x, y, z))) //Expands to xyz
물론 모든 종류의 술어 또는 연산자를 전달할 수 있습니다.
재귀 포함 파일을 사용합니다. 불행히도 전처리 기가 허용하는 최대 깊이 이상으로 루프를 반복 할 수 없습니다.
C ++ 템플릿은 Turing Complete이며 유사한 방식으로 사용할 수 있습니다. 제너 레이 티브 프로그래밍 확인
여기에 합법적으로 수행 될 규칙의 남용이 있습니다. 자신의 C 전처리기를 작성하십시오. #pragma 지시문을 원하는 방식으로 해석합니다.
저는이 목적을 위해 메타 템플릿 프로그래밍을 사용합니다. 일단 익숙해지면 재미 있습니다. 그리고 신중하게 사용할 때 매우 유용합니다. 튜링이 완료되었다고 언급했듯이 컴파일러가 무한 루프 또는 스택 오버플로에 들어가게 할 수있는 지점까지! 컴파일이 무한 루프 코드를 컴파일하기 위해 최대 30GB 이상의 메모리와 모든 CPU를 사용하고 있다는 것을 알기 위해 커피를 마시 러가는 것만 큼 좋은 것은 없습니다!
글쎄, 그것이 while 루프가 아니라 카운터 루프라는 것은 아니지만 깨끗한 CPP에서 루프가 가능합니다 (템플릿 및 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]);
}
컴파일러가 불안정 해져서 특정 루프를 풀지 않을 때이 체계가 유용하다는 것을 알았습니다.
#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.
Not quite what you asked, but checkout these links to a C program that is also a valid makefile and shell script.
The C, make and shell code build upon each other to create a C program (?) which when executed as a shell script will compile itself through the C compiler using a makefile!
A winner in the 2000 obfuscated C contest.
http://www.ioccc.org/2000/tomx.c
http://www.ioccc.org/2000/tomx.hint
참고URL : https://stackoverflow.com/questions/319328/how-to-write-a-while-loop-with-the-c-preprocessor
'program story' 카테고리의 다른 글
| 상수 대신 열거 형을 사용하는 이유는 무엇입니까? (0) | 2020.11.14 |
|---|---|
| Visual Studio에 단위 테스트에 대한 코드 적용 범위가 있나요? (0) | 2020.11.14 |
| 보호 된 인터페이스 멤버를 가질 수없는 이유는 무엇입니까? (0) | 2020.11.14 |
| VisualStudio 옵션에서 디자인 모드를 비활성화하는 방법 (0) | 2020.11.14 |
| 억제 된 예외 란 무엇입니까? (0) | 2020.11.14 |