Related
I want to forward args of variadic function, I have already find the some topic.
Forward an invocation of a variadic function in C
When I start to practice, I found a problem.
#include <stdio.h>
#include <stdarg.h>
void fun1(const char *msg, ...) // try to forward printf
{
va_list arg_list;
va_start(arg_list, msg);
vprintf(msg, arg_list);
va_end(arg_list);
}
void fun2(const char *msg, ...) // try to forward fun1
{
va_list arg_list;
va_start(arg_list, msg);
fun1(msg, arg_list);
va_end(arg_list);
}
int main()
{
fun1("this is int %d, float %f\n", 1, 2.3);
fun2("this is int %d, float %f\n", 1, 2.3);
return 0;
}
I compile code with gcc main.c and the output shown that
this is int 1, float 2.300000
this is int 6684168, float 2.300000
I can not understand why the fun2 not forward the args of fun1 correctly.
Why the int 1 goes to another number but 2.3 still good.
How can I modify my code to implement the forward?
Thanks for your time.
fun1 needs a list of arguments to match its format, but when call it from fun2 you give it a va_list. To call it that way you need to rewrite it to take a va_list rather than a ...:
void fun1(const char *fmt, va_list args) {
vfprintf(fmt, args);
}
If you're using gcc, you can avoid many problems of this nature by using -Wall and adding a declaration for your functions that take ...:
extern void fun2(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
This tells gcc that fun2 takes printf-style arguments starting from the second argument with the first argument as the format. With this, it will warn you if the arguments passed to fun1 don't match the format string.
As suggtested by StoryTeller - Unslander Monica and Chris Dodd, My finial code looks like follows.
// before modify
// vprintf <-- fun1 <-- fun2
// after modify
// vprintf <-- vfun1 <--- fun1
// |- fun2
#include <stdio.h>
#include <stdarg.h>
void vfun1(const char *msg, va_list arg_list)
{
printf("this is vfun1\n");
vprintf(msg, arg_list);
}
void fun1(const char *msg, ...) // try to forward vprintf, but implemented by call vfun1
{
printf("this is fun1\n");
va_list arg_list;
va_start(arg_list, msg);
vfun1(msg, arg_list);
va_end(arg_list);
}
void fun2(const char *msg, ...) // try to forward fun1, but implemented by call vfun1
{
printf("this is fun2\n");
va_list arg_list;
va_start(arg_list, msg);
vfun1(msg, arg_list);
va_end(arg_list);
}
int main()
{
printf("------------------------------------\n");
fun1("this is int %d, float %f\n", 5, 2.3);
printf("------------------------------------\n");
fun2("this is int %d, float %f\n", 5, 2.3);
return 0;
}
I want to only printf if some condition is true. I know printf is a variadic function but sadly I can't seem to find any thread here explaining I can wrap it.
Basically every in the code where I'd write :
printf(" [text and format] ", ... args ...);
I want to write something like
my_custom_printf(" [text and format] ", ... args ...);
Which then is implemented like this :
int my_custom_printf(const char* text_and_format, ... args ...)
{
if(some_condition)
{
printf(text_and_format, ... args...);
}
}
A first version of the condition would be independent of the args (it would be on some global variable), but it might be in the future that it's a condition on argument that's wanted.
Anyway, right now I just need the syntax for ... args ... in the prototype and the body of my_custom_printf.
I'm using GCC but I don't know which C standard - but we can just try stuff out.
You can use vprintf:
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
static bool canPrint = true;
int myprintf(const char *fmt, ...)
{
va_list ap;
int res = 0;
if (canPrint) {
va_start(ap, fmt);
res = vprintf(fmt, ap);
va_end(ap);
}
return res;
}
int main(void)
{
myprintf("%d %s\n", 1, "Hello");
return 0;
}
I am trying to make a user level thread library, but when I tried to set the context of one of those threads, I received a segmentation fault error. I made a stripped version of all my code and narrowed it down (I think) to one weird thing. If I use malloc() to provide a stack for the thread context, it doesn't work. Weirdly though, if I declare an array of same size on the current function's stack, it works as expected. I thought that, apart for the different areas in memory, both approaches would be completely equivalent. After all, they are both pointers to some area of memory that's free to use.
The important lines are:
char cstack[1024];, and
char *cstack = (char *)malloc(1024);
Both in thread.c, shown below. If I comment the first one and not the second, segmentation fault ensues. If I do the opposite, all is (seems?) fine.
This is the program I used to test it, test.c:
#include <stdlib.h>
#include "thread.h"
int main(int argc, char** argv) {
ccreate(NULL, NULL);
return 0;
}
Here is thread.h:
#ifndef __cthread__
#define __cthread__
int ccreate (void* (*start)(void*), void *arg);
#endif
Here is (most importantly) thread.c:
#include <stdlib.h>
#include <stdio.h>
#include <ucontext.h>
#include "logging.c"
#include "cdata.h"
#include "thread.h"
#undef LOGLEVEL
#define LOGLEVEL 5
ucontext_t m;
int been_here;
static TCB_t *TCB_init(int tid) {
floginfo("initializing TCB %d", tid);
TCB_t *thr = (TCB_t *)malloc(sizeof(TCB_t));
thr->tid = tid;
thr->state = PROCST_APT;
flogdebug("initializing context at address %p", &(thr->context));
if (getcontext(&(thr->context)) == -1) logerror("error initializing context");
thr->context.uc_link = &m;
return thr;
}
static void say_hey(void) {
been_here = 1;
loginfo("hey");
}
int ccreate (void* (*start)(void*), void *arg) {
static int last_tid = -1;
//char cstack[1024]; /* doesn't segfault */
char *cstack = (char *)malloc(1024); /* segfaults */
floginfo("creating thread %d.", last_tid + 1);
TCB_t *thr;
thr = TCB_init(++last_tid);
thr->context.uc_stack.ss_sp = cstack;
thr->context.uc_stack.ss_size = 1024;
been_here = 0;
getcontext(&m);
if (!been_here) {
flogdebug("making context at address %p", &(thr->context));
logdebug("calling: makecontext(&(thr->context), say_hey, 0);");
makecontext(&(thr->context), say_hey, 0);
flogdebug("setting context at address %p", &(thr->context));
logdebug("calling: setcontext(&(thr->context))");
setcontext(&(thr->context));
}
logdebug("came back from setcontext");
logdebug("exiting");
return 0;
}
This is where the TCB_t (thread control block) type is defined, cdata.h:
#ifndef __cdata__
#define __cdata__
#define PROCST_CRE 0
#define PROCST_APT 1
#define PROCST_EXEC 2
#define PROCST_BLOC 3
#define PROCST_TERM 4
typedef struct s_TCB {
int tid;
int state;
ucontext_t context;
} TCB_t;
#endif
And here is (very unimportant, I think) logging.c:
#include <stdio.h>
#include <stdarg.h>
#define LOGLEVEL 6
#define LVL_DEBUG 5
#define LVL_INFO 4
#define LVL_WARNING 3
#define LVL_ERROR 2
#define LVL_CRITICAL 1
void logdebug(const char *msg) {
if (LOGLEVEL >= LVL_DEBUG) {
fprintf(stderr, "DEBUG: %s\n", msg);
}
}
void loginfo(const char *msg) {
if (LOGLEVEL >= LVL_INFO) {
fprintf(stderr, "INFO: %s\n", msg);
}
}
void logwarning(const char *msg) {
if (LOGLEVEL >= LVL_WARNING) {
fprintf(stderr, "WARNING: %s\n", msg);
}
}
void logerror(const char *msg) {
if (LOGLEVEL >= LVL_ERROR) {
fprintf(stderr, "ERROR: %s\n", msg);
}
}
void logcritical(const char *msg) {
if (LOGLEVEL >= LVL_CRITICAL) {
fprintf(stderr, "CRITICAL: %s\n", msg);
}
}
void flogdebug(const char *fmt, ...) {
char buff[1024];
va_list args;
va_start(args, fmt);
vsprintf(buff, fmt, args);
va_end(args);
logdebug((const char *)buff);
}
void floginfo(const char *fmt, ...) {
char buff[1024];
va_list args;
va_start(args, fmt);
vsprintf(buff, fmt, args);
va_end(args);
loginfo((const char *)buff);
}
void flogwarning(const char *fmt, ...) {
char buff[1024];
va_list args;
va_start(args, fmt);
vsprintf(buff, fmt, args);
va_end(args);
logwarning((const char *)buff);
}
void flogerror(const char *fmt, ...) {
char buff[1024];
va_list args;
va_start(args, fmt);
vsprintf(buff, fmt, args);
va_end(args);
logerror((const char *)buff);
}
void flogcritical(const char *fmt, ...) {
char buff[1024];
va_list args;
va_start(args, fmt);
vsprintf(buff, fmt, args);
va_end(args);
logcritical((const char *)buff);
}
Finally, compiling all with gcc -Wall thread.c test.c gives no warning. If char cstack[1024]; is uncommented, I get this:
INFO: creating thread 0.
INFO: initializing TCB 0
DEBUG: initializing context at address 0x16d5018
DEBUG: making context at address 0x16d5018
DEBUG: calling: makecontext(&(thr->context), say_hey, 0);
DEBUG: setting context at address 0x16d5018
DEBUG: calling: setcontext(&(thr->context))
INFO: hey
DEBUG: came back from setcontext
DEBUG: exiting
If char *cstack = (char *)malloc(1024); is uncommented, I get this:
INFO: creating thread 0.
INFO: initializing TCB 0
DEBUG: initializing context at address 0x12f8428
DEBUG: making context at address 0x12f8428
DEBUG: calling: makecontext(&(thr->context), say_hey, 0);
DEBUG: setting context at address 0x12f8428
DEBUG: calling: setcontext(&(thr->context))
Segmentation fault (core dumped)
Any ideas at all are appreciated. I am mostly confused and pondering all I thought I knew.
According to this
stack_t uc_stack Stack used for this context. The value need not be
(and normally is not) the stack pointer. See section 24.9 Using a
Separate Signal Stack.
and this
size_t ss_size
This is the size (in bytes) of the signal stack which
'ss_sp' points to. You should set this to however much space you
allocated for the stack. There are two macros defined in 'signal.h'
that you should use in calculating this size:
SIGSTKSZ
This is the canonical size for a signal stack. It is judged
to be sufficient for normal uses.
MINSIGSTKSZ
This is the amount of signal stack space the operating
system needs just to implement signal delivery. The size of a signal
stack must be greater than this. For most cases, just using
the minimum required size of stack is governed by MINSIGSTKSZ macro.
It happens to work if cstack is on your normal stack because you set up the stack pointer incorrectly and you're getting away with overwriting memory on the real stack rather than overwriting memory you got from malloc.
In which direction does the stack grow on your machine? If it grow like on a normal CPU (pretty much anything other than PA-RISC), where in your memory block should the initial stack pointer point? If you can't answer those two questions with "oh, of course, that's a silly mistake" you should reconsider this project, it only gets harder after here.
According to the man page, you should allocate at leat SIGSTKSZ for the stack size.
On my pc, this constant is equal to 8192. On your computer this constant may be greater than 1024.
Currently, I have the following 2 functions:
void write_to_file(FILE *fp)
{
fprintf(fp, "stuff here: %d", 10);
/* 1000s of similar lines below */
}
and
void write_to_string(char *str)
{
sprintf(str, "stuff here: %d", 10);
/* 1000s of similar lines below */
}
I'd like to poly morph it into a single function.
I'd thought about something like:
void write_somewhere(void *ptr, int to_file)
{
if (to_file) {
typedef fprintf myprintf;
} else {
typedef sprintf myprintf;
}
myprintf(ptr, "stuff here: %d", 10);
}
This doesn't work and looks ugly.
Since the signature of fprintf and sprintf are different and as follows,
int fprintf(FILE *stream, const char *format, …);
int sprintf(char *buffer, const char *format, …);
Is it possible to do something like,
void write_somewhere(void *ptr, void *func)
{
func(ptr, "stuff here: %d", 10);
}
EDIT:
Based on Alter's answer below, this is what I have but it doesn't quite work as expected and prints out garbage value when trying to print out values in write_somewhere() function:
#include <stdio.h>
#include <stdarg.h>
typedef int (*myprintf_t) (void *, const char *, ...);
int myfprintf(void *ptr, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = vfprintf(ptr, format, args);
va_end(args);
return ret;
}
int mysprintf(void *ptr, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = vsprintf(ptr, format, args);
va_end(args);
return ret;
}
void write_somewhere(void *ptr, myprintf_t myprintf, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = myprintf(ptr, format, args);
va_end(args);
return ret;
}
int main(void)
{
char s[100];
int i = 100;
/* This works */
write_somewhere(stdout, myprintf, "Hello world");
/* This prints out garbage */
write_somewhere(stdout, myprintf, "Hello world, I am %d", i);
write_somewhere(s, mysprintf);
return 0;
}
Jen‘s answer is the correct one, but in this case you can redirect ptr to v*printf using a pointer to function:
#include <stdio.h>
#include <stdarg.h>
int myfprintf(void *ptr, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = vfprintf(ptr, format, args);
va_end(args);
return ret;
}
int mysprintf(void *ptr, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = vsprintf(ptr, format, args);
va_end(args);
return ret;
}
void write_somewhere(void *ptr, int (*myprintf)(void *, const char *, ...))
{
myprintf(ptr, "stuff here");
}
int main(void)
{
char s[100];
write_somewhere(stdout, myfprintf);
write_somewhere(s, mysprintf);
return 0;
}
For your last edit:
It seems that you want to pass some extras parameters to write_somewhere, in this case I suggest:
#include <stdio.h>
#include <stdarg.h>
#define TO_FILE 0
#define TO_STRING 1
void write_somewhere(int where, void *ptr, const char *format, ...)
{
#define myprintf(ptr, ...) \
(where == TO_FILE ? vfprintf(ptr, __VA_ARGS__) : vsprintf(ptr, __VA_ARGS__))
va_list args;
va_start(args, format);
myprintf(ptr, format, args);
/* more stuff */
va_end(args);
#undef myprintf
}
int main(void)
{
char s[100];
write_somewhere(TO_FILE, stdout, "%u\n", 10);
write_somewhere(TO_STRING, s, "Hello");
printf("%s\n", s);
return 0;
}
The C language guarantees that all function pointers have the same representation. Your polymorphic functions simply needs to be prototyped as accepting any function pointer, say, a void (*funcptr)(void). Note that a ptr-to-void is not a function pointer (it's an object pointer) and may not be able to hold a function pointer.
Of course you can only call the function if you know which of the several types it is. So you need some way to discriminate, much like printf does by looking at the format. If you call a function with arguments not matching its prototype, the behavior is undefined.
Not an answer to your exact question, but instead of writing write_something() as you have, you could change the structure slightly:
void write_somewhere(void *ptr, int to_file)
{
if (to_file) {
fprintf( (FILE*) ptr, "stuff here");
} else {
sprintf( (char*) ptr, "stuff here");
}
}
However, for a strict answer to your question...
As you've found, the typedef line that you've attempted doesn't work. typedef is a compile time operation, not a runtime operation.
What you could do, though, is to define a type for a function pointer that matches both the fprintf() and sprintf() functions:
typedef int (*someprintf_ptr)(FILE *stream, const char *format, …);
The write_somewhere() would then look like:
void write_somewhere(void *ptr, someprintf_ptr func)
{
func(ptr, "stuff here");
}
/* with calls looking like... */
write_somewhere( (void *)a_file_ptr, (someprintf_ptr)(fprintf));
Your write_something function would have to be something like:
void write_something(void (*function)(), int to_file)
{
....
}
Try making myprintf a function
void write_somewhere(void *ptr, int to_file)
{
myprintf(to_file, ptr, "stuff here");
// do stuff
/* 1000s of similar lines below */
}
void myprintf( int to_file, void *ptr, char *output )
{
if (to_file)
fprintf( ptr, output );
else
sprintf( ptr, output );
}
Let me try:
struct target {
int (*tgtfunction)();
void* ptr;
}
struct target mktarget_file(FILE * fp) {
struct target tgt = { .tgtfuntion = vfprintf, .ptr = fp };
return tgt;
}
struct target mktarget_string(char * str) {
struct target tgt = { .tgtfuntion = vsprintf; .ptr = str };
return tgt;
}
void tgtprintf(struct target * target, char * fmt, ...) {
va_list ap;
va_start(ap, target);
int ret = target.tgtfunction(target.ptr, fmt, ap);
va_end(ap);
return ret;
}
void write_stuff(struct target * target)
{
tgtprintf(target, "stuff here");
/* 1000s of similar lines below */
}
should do what you want: create a struct target for your wanted target, and just call write_stuff to write your stuff there.
Be aware that the sprintf stuff might need to be refined, as each string is written to the same place instead of appended, and there is no check for free space. But the general concept could start like this.
I've been tracing a problem that I've narrowed down to this skeleton:
#include <unistd.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct __log_file_details {
FILE *fp ;
char *name ;
} log_file_details_t;
typedef struct __log_files {
char is_open;
log_file_details_t lf[3];
void (*open_log)(struct __log_files *ctl);
void (*close_log)(struct __log_files *ctl);
} log_files_t ;
int write_log(const int file_nbr, log_files_t *ctl, char *log_this, ...);
void close_log(log_files_t *ctl);
void open_log(log_files_t *ctl);
// Here we go...
int main(int argc, char *argv[]) {
log_files_t log_ctl = {
0,
{ {NULL, NULL}, {NULL, NULL}, {NULL, NULL} },
&open_log,
&close_log
};
write_log(0, &log_ctl, "foo"); // That's it.
return 0;
}
void open_log(log_files_t *ctl) {}
void close_log(log_files_t *ctl) {}
int write_log(const int file_nbr, log_files_t *ctl, char *log_this, ...)
{
int rc;
/* ... */
rc = 0;
}
When I compile this code using gdb -g -o foo foo.c it works on most of my linux systems.
However, I have an ARM device (actually, a Netgear Stora running Linux) on which it fails miserably.
On that device, if I use GDB to step through this code, when write_log executes (line 58), I see:
Breakpoint 1, write(log(file_nbr=-1092220616, ctl=0x0, log_this=0xbee60ac4 "-garbage-") at foo.c:58
(where -1092220616 is a varying value, and -garbage- tends to contain a bunch of control characters.)
I don't know how to determine if this is a runtime problem (one of the libraries?), a problem with one of the standard headers, a gcc problem, or something else. What might I do in order to identify and resolve this problem?
If I remove the va_list definition of write_log, all works well, but of course that's not what I want.)
You need to properly clean up the variable args in your function:
int write_log(const int file_nbr, log_files_t *ctl, char *log_this, ...)
{
int rc;
va_list vl;
va_start(vl,log_this);
/* ... */
va_end(vl);
rc = 0;
}