P
pete m
I would like to support stdio functions for an extremely primitive
type system, as shown in the attached sample program, using dynamic
creation of a va_list.
I've tested it on successfully a couple platforms, but what I don't
know is whether it's actually a legal program.
The function of interest is usnprintf, "union sprintf". (psnprintf is
a dummy wrapper for vsnprintf, useful for debugging.)
The signature is
int usnprintf (char *buffer, size_t size, const char *fmt, const
type_union *arg);
where the last argument is a flag-terminated array of type_union, as
defined below.
================Code begins =========================
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
typedef struct type_union type_union;
typedef enum type_val {
T_END = 0, /* terminator flag for variable-length array */
T_INT,
T_FLOAT,
T_CHAR,
T_STRING,
} type_val;
struct type_union
{
type_val t;
union {
float f;
int i;
char c;
const char *s;
};
};
/* peek into va_list offsets */
int psnprintf(char *buf, size_t s, const char *fmt, va_list va)
{
int i;
int r;
va_list vb = va;
for(i = 0; i < 2; i++) {
double t = va_arg(va, double);
}
r = vsnprintf(buf, s, fmt, vb);
return r;
}
/*
* Treat ac_list, ac_arg as lvalues.
*/
#define va_start_assign(list, var) ((list) = ((char*)&var))
#define va_assign(lvalp, type, rval) \
*((type*)lvalp) = rval; \
va_arg(lvalp, type);
#define VA_FLOAT double
#define VA_CHAR int
/*
* snprintf for typed unions. (Does not validate the parse.)
* Requires flag-terminated array of values.
* TODO: validate with parse_printf_format and eliminate terminator.
*/
int usnprintf (char *buffer, size_t size, const char *fmt, const
type_union *arg)
{
int i;
int ret;
va_list out;
double aligned[100]; /* aligned buffer of ridiculously large size */
va_start_assign(out, aligned[0]);
for(i = 0; arg.t != T_END; i++) {
switch(arg.t) {
case T_INT:
va_assign(out, int, arg.i);
break;
case T_FLOAT:
va_assign(out, VA_FLOAT, arg.f);
break;
case T_CHAR:
va_assign(out, VA_CHAR, arg.c);
break;
case T_STRING:
va_assign(out, const char *, arg.s);
break;
default:
assert(0);
va_end(out);
return -1;
}
}
va_end(out);
va_start_assign(out, aligned[0]);
ret = psnprintf(buffer, size, fmt, out);
va_end(out);
return ret;
}
#define TYPE_FUN(v2u, tv, T, v) \
type_union v2u(T v) { \
type_union r; \
r.v = v; \
r.t = tv; \
return r; \
}
TYPE_FUN(i2u, T_INT, int, i)
TYPE_FUN(c2u, T_CHAR, char, c)
TYPE_FUN(f2u, T_FLOAT, float, f)
TYPE_FUN(s2u, T_STRING, const char *, s)
static const type_union END = { T_END };
int main() {
char buffer[30];
type_union x[] = { f2u(2.5), f2u(3.5), i2u(5), c2u('a'), s2u("xyz
\n"), END };
usnprintf(buffer, 30, "%.1f; %.1f; %d; %c; %s", x);
printf("%s", buffer);
}
type system, as shown in the attached sample program, using dynamic
creation of a va_list.
I've tested it on successfully a couple platforms, but what I don't
know is whether it's actually a legal program.
The function of interest is usnprintf, "union sprintf". (psnprintf is
a dummy wrapper for vsnprintf, useful for debugging.)
The signature is
int usnprintf (char *buffer, size_t size, const char *fmt, const
type_union *arg);
where the last argument is a flag-terminated array of type_union, as
defined below.
================Code begins =========================
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
typedef struct type_union type_union;
typedef enum type_val {
T_END = 0, /* terminator flag for variable-length array */
T_INT,
T_FLOAT,
T_CHAR,
T_STRING,
} type_val;
struct type_union
{
type_val t;
union {
float f;
int i;
char c;
const char *s;
};
};
/* peek into va_list offsets */
int psnprintf(char *buf, size_t s, const char *fmt, va_list va)
{
int i;
int r;
va_list vb = va;
for(i = 0; i < 2; i++) {
double t = va_arg(va, double);
}
r = vsnprintf(buf, s, fmt, vb);
return r;
}
/*
* Treat ac_list, ac_arg as lvalues.
*/
#define va_start_assign(list, var) ((list) = ((char*)&var))
#define va_assign(lvalp, type, rval) \
*((type*)lvalp) = rval; \
va_arg(lvalp, type);
#define VA_FLOAT double
#define VA_CHAR int
/*
* snprintf for typed unions. (Does not validate the parse.)
* Requires flag-terminated array of values.
* TODO: validate with parse_printf_format and eliminate terminator.
*/
int usnprintf (char *buffer, size_t size, const char *fmt, const
type_union *arg)
{
int i;
int ret;
va_list out;
double aligned[100]; /* aligned buffer of ridiculously large size */
va_start_assign(out, aligned[0]);
for(i = 0; arg.t != T_END; i++) {
switch(arg.t) {
case T_INT:
va_assign(out, int, arg.i);
break;
case T_FLOAT:
va_assign(out, VA_FLOAT, arg.f);
break;
case T_CHAR:
va_assign(out, VA_CHAR, arg.c);
break;
case T_STRING:
va_assign(out, const char *, arg.s);
break;
default:
assert(0);
va_end(out);
return -1;
}
}
va_end(out);
va_start_assign(out, aligned[0]);
ret = psnprintf(buffer, size, fmt, out);
va_end(out);
return ret;
}
#define TYPE_FUN(v2u, tv, T, v) \
type_union v2u(T v) { \
type_union r; \
r.v = v; \
r.t = tv; \
return r; \
}
TYPE_FUN(i2u, T_INT, int, i)
TYPE_FUN(c2u, T_CHAR, char, c)
TYPE_FUN(f2u, T_FLOAT, float, f)
TYPE_FUN(s2u, T_STRING, const char *, s)
static const type_union END = { T_END };
int main() {
char buffer[30];
type_union x[] = { f2u(2.5), f2u(3.5), i2u(5), c2u('a'), s2u("xyz
\n"), END };
usnprintf(buffer, 30, "%.1f; %.1f; %d; %c; %s", x);
printf("%s", buffer);
}