#include #include #include #include void simple_sprintf(char *out, const char *fmt, ...) { va_list args; char tmp[32]; int outlen = 0; for (va_start(args, fmt); *fmt != '\0'; ++fmt) { switch (*fmt) { case 'd': { int i = va_arg(args, int); sprintf(tmp, "%d", i); strcpy(out + outlen, tmp); outlen += strlen(tmp); break; } case 'c': { int c = va_arg(args, int); out[outlen++] = (char)c; break; } case 'f': { float d = va_arg(args, float); sprintf(tmp, "%f", d); strcpy(out + outlen, tmp); outlen += strlen(tmp); break; } default: puts(p"unknown formatter!"); assert(false); goto END; } } END: out[outlen] = '\0'; va_end(args); } // Basic Variadic Macro Patterns #define PRINT(fmt, ...) printf(fmt "\n", __VA_ARGS__) #define ASSERT(cond, fmt, ...) \ if (!(cond)) \ { \ printf(p"FAIL at %s:%d - " fmt "\n", __FILE__, __LINE__, __VA_ARGS__); \ assert(cond); \ } #define TEST_CODE(name, code, expected, ...) \ do \ { \ printf(p"[%s] ", name); \ assert( \ strcmp(name, p"add_test") == 0 \ || strcmp(name, p"mul_test") == 0 \ ); \ int result = (code); \ printf(p"result=%d, expected=%d " __VA_ARGS__ "\n", result, expected); \ assert (result == expected); \ } while (0) int add(const int a, const int b) { return a + b; } int multiply(const int a, const int b) { return a * b; } #define CALCULATE_SUM(sum, ...) \ do { \ int vals[] = { __VA_ARGS__ }; \ for (int i = 0; i < (int)(sizeof(vals) / sizeof(vals[0])); i++) \ sum += vals[i]; \ } while (0) // Brace-in-macro-argument tests (issue: error 3068) // Support outer macro receiving a braced struct initialiser. Tests that { } tokens are accepted // inside macro arguments (preprocessor fix) struct BraceRecord { const char *name; int value; }; #define INNER_RECORD(label, val) {(label), (val)} #define OUTER_VARIADIC(...) \ static struct BraceRecord brace_records[] = {__VA_ARGS__} // Support variadic passthrough to a function #define INVOKE(function, ...) \ do \ { \ function(__VA_ARGS__); \ } \ while (0) int main(void) { // Test 1: Basic variadic usage with different argument counts PRINT(p"test 1: %d + %d = %d", 5, 3, add(5, 3)); PRINT(p"test %d: %d * %d = %d", 2, 4, 7, multiply(4, 7)); int sum = 0; CALCULATE_SUM(sum, 1, 2, 3, 4, 5); PRINT(p"sum is %d", sum); assert (sum == 15); // Test 2: Assertion with custom messages ASSERT(add(2, 2) == 4, p"addition check: 2+2=%d", add(2, 2)); ASSERT(multiply(3, 3) == 9, p"multiplication: 3*3=%d", multiply(3, 3)); // Test 3: Code generation testing TEST_CODE(p"add_test", add(10, 20), 30, p"(10+20)"); TEST_CODE(p"mul_test", multiply(5, 6), 30, p"(5*6)"); // Test 4: Brace-in-macro-argument (issue: error 3068) // Verify that { } tokens inside macro arguments are accepted by the preprocessor OUTER_VARIADIC( INNER_RECORD( p"a", 1 ) ); assert(strcmp(brace_records[0].name, p"a") == 0); assert(brace_records[0].value == 1); // Test 5: Support variadic passthrough to a function char buf[64]; INVOKE(sprintf, buf, p"%s %d", brace_records[0].name, brace_records[0].value); printf(p"buf: %s\n", buf); assert(strcmp(buf, p"a 1") == 0); // Test 6: Support variadic usage in a function char simple_buf[64]; simple_sprintf(simple_buf, "dcff", 3, p'a', 3.14f, 42.5f); printf(p"simple_buf: %s\n", simple_buf); assert(strcmp(simple_buf, "3A3.14000042.500000") == 0); // Test 7: Escaped quotes inside macro arguments // Verify that \" inside a string arg and \' inside a char arg do not confuse // the macro argument scanner (they must not toggle the quote state) char eq_buf[64]; INVOKE(sprintf, eq_buf, "%s", p"hello\"world"); printf(p"eq_buf: %s\n", eq_buf); assert(strcmp(eq_buf, p"hello\"world") == 0); char sq_buf[64]; INVOKE(sprintf, sq_buf, "%c", p'\''); printf(p"sq_buf: %s\n", sq_buf); assert(sq_buf[0] == '\'' && sq_buf[1] == '\0'); char mixed_buf[64]; INVOKE(sprintf, mixed_buf, p"This \"%s\" '%c'orld values.", p"\"hello'\"", p'w'); printf(p"mixed_buf: %s\n", mixed_buf); assert(strcmp(mixed_buf, p"This \"\"hello'\"\" 'w'orld values.") == 0); return 0; }