173 lines
4.7 KiB
C
173 lines
4.7 KiB
C
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
|
|
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;
|
|
}
|