initial commit
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user