function polymorphisms depending on debug level - c

I would like to change below code:
#ifdef CONSOLE_VERBOSE_DEBUG
printf("this debug error message is printed to the console");
#elseif FILE_VERBOSE_DEBUG
FILE *log = fopen(...);
...
fprintf();
fclose();
#else
((void) 0) // no debugging
into something like
callThePropperDebugFct("message");
and point this one call to functions declared in the properly included header files depending on which DEBUG level is defined
I know it has to do with c polymorphisms and function pointers, but I can not wrap my head around how to do this

Define functions to log your message in some log.c file
Define your callThePropperDebugFct macro to call the right function
log.c
void log_to_console(const char message)
{
printf("%s", message);
}
void log_to_file(const char *name, const char *message)
{
FILE *f = fopen(name, "a");
if (!f) return;
fprintf(f, message);
fclose(f);
}
log.h
void log_to_console(const char message);
void log_to_file(const char *name, const char *message);
#ifdef CONSOLE_VERBOSE_DEBUG
#define callThePropperDebugFct(message) log_to_console(message);
#elseif FILE_VERBOSE_DEBUG
#define callThePropperDebugFct(message) log_to_file(LOG_FILE, message)
#else
#define callThePropperDebugFct(message)
#endif
But you can go further with some variadic macros and variable numbers of arguments functions:
log.c
#include <stdarg.h>
#include <stdio.h>
void log_to_console(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
//vsnprintf(buffer, sizeof buffe, fmt, args);
vprintf(fmt, args);
va_end(args);
}
void log_to_file(const char *name, const char *fmt, ...)
{
FILE *f = fopen(name, "a");
if (!f) return;
va_list args;
va_start(args, fmt);
vfprintf(f, fmt, args);
va_end(args);
fclose(f);
}
log.h
void log_to_console(const char *fmt, ...);
void log_to_file(const char *name, const char *fmt, ...);
#ifdef CONSOLE_VERBOSE_DEBUG
#define callThePropperDebugFct(...) log_to_console("s", __VA_ARGS__)
#elseif FILE_VERBOSE_DEBUG
#define callThePropperDebugFct(...) log_to_file(LOG_FILE, "%s", __VA_ARGS__)
#else
#define callThePropperDebugFct(...)
#endif

#if DEBUG_LEVEL
#ifndef LOG_OUTPUT
#define LOG_OUTPUT stdio
#endif
log_function(DEBUG_LEVEL, LOG_OUTPUT, const char *fmt, ...);
#endif
#ifdef __GNUC__
#define ATTR __attribute__((format (printf, 3, 4)));
#else
/* .. */
#endif
void ATTR log_function(int DEBUG_LEVEL, FILE *LOG_OUTPUT, const char *fmt, ...)
{
va_list args;
/* some code */
vfprintf(LOG_OUTPUT, fmt, args);
}

Related

pass 3 dots argument to another function

I have function called foo and that function takes ..., I want to pass all the argument that passed in ... to the function called oof, sample code:
#include <stdio.h>
#include <stdarg.h>
void oof(FILE * f, const char * fmt, ...){
va_list args;
va_start(args, fmt);
vfprintf(f, fmt, args);
va_end(args);
}
void foo(const char * fmt, ...){
oof(stdout, fmt, ...); // how can I pass the 3 dots?
// do other things in the function block
}
int main(int argc, char *argv[]){
foo("Hello %s\n", "World");
}
I don't want to pass to oof a va_list, but the arguments themselves
You need to change oof to accept a va_list and pass that.
void oof(FILE * f, const char * fmt, va_list args){
vfprintf(f, fmt, args);
}
void foo(const char * fmt, ...){
va_list args;
va_start(args, fmt);
oof(stderr, fmt, args);
va_end(args);
}
You cannot do this with a function but you can do this with a macro:
#define foo(...) oof(__VA_ARGS__)
There is still an issue.
Where do you get FILE *f argument from?
Assuming it is stdout then define the macro as:
#define foo(...) oof(stdout, __VA_ARGS__)
amd if you want to eliminate some parameters (doing the inverse of above, just do the following:
#define foo(a, b, ...) oof(__VA_ARGS__)
This is similar to dbush's answer (dbush should get the credit - do not accept my answer over theirs), but slightly modified because of OP's statement:
I don't want to pass to oof a va_list, but the arguments themselves
Because a va_list needs to be passed to something, I define a different function voof called by both oof and foo:
#include <stdio.h>
#include <stdarg.h>
void voof(FILE * f, const char * fmt, va_list args){
vfprintf(f, fmt, args);
}
void oof(FILE * f, const char * fmt, ...){
va_list args;
va_start(args, fmt);
voof(f, fmt, args);
va_end(args);
}
void foo(const char * fmt, ...){
va_list args;
va_start(args, fmt);
voof(stderr, fmt, args);
va_end(args);
}
int main(int argc, char *argv[]){
foo("Hello %s\n", "World");
}
foo and oof currently do the same thing, apart from the extra FILE * parameter in oof.

in #define directive, how to fix the "expected declaration specifiers before"?

I have source code contains cccp.c file. on line 195 there is:
#if defined (__STDC__) && defined (HAVE_VPRINTF)
# include <stdarg.h>
# define VA_START(va_list, var) va_start (va_list, var)
# define PRINTF_ALIST(msg) char *msg, ...
# define PRINTF_DCL(msg)
# define PRINTF_PROTO(ARGS, m, n) \
PROTO (ARGS) __attribute__ ((format (__printf__, m, n)))
#else
# include <varargs.h>
# define VA_START(va_list, var) va_start (va_list)
# define PRINTF_ALIST(msg) msg, va_alist
# define PRINTF_DCL(msg) char *msg; va_dcl ////====here is the error
# define PRINTF_PROTO(ARGS, m, n) () __attribute__ ((format (__printf__, m, n)))
# define vfprintf(file, msg, args) \
{ \
char *a0 = va_arg(args, char *); \
char *a1 = va_arg(args, char *); \
char *a2 = va_arg(args, char *); \
char *a3 = va_arg(args, char *); \
fprintf (file, msg, a0, a1, a2, a3); \
}
#endif
...
void
warning (PRINTF_ALIST (msg))
PRINTF_DCL (msg) ///the use macro part
{
va_list args;
VA_START (args, msg);
vwarning (msg, args);
va_end (args);
}
static void
fatal (PRINTF_ALIST (msg))
PRINTF_DCL (msg)
{
va_list args;
fprintf (stderr, "%s: ", progname);
VA_START (args, msg);
vfprintf (stderr, msg, args);
va_end (args);
fprintf (stderr, "\n");
exit (FATAL_EXIT_CODE);
}
and my compiler shows this error for:
error: expected declaration specifiers before ‘va_dcl’
195 | # define PRINTF_DCL(msg) char *msg; va_dcl
when to define macro apply, it is like:
PRINTF_DCL (msg)
the source file similar like: https://cis.temple.edu/~ingargio/cis307/software/mico/cpp/cccp.c
But in that file that code line is 199.
I don't know why there is ';' before va_dcl. And do not know how to fix it.
va_dcl is an obsolete facility, used to declare variadic functions in language dialects dating to long before standardization. (Reference 1, 2)
I suspect that the compiler is going down the wrong path here. Try compiling with -DHAVE_VPRINTF on the command line.

Assignment of variadic function fails with incompatible pointer type

I'm using ceedling with the 'fake funcion framework'(fff).
I declare a custom fake for my debug_printf function.
My debug.h
#ifndef DEBUG_H
#define DEBUG_H
void debug_printf(const char*, ...);
#endif // DEBUG_H
My some.h
#ifndef SOME_H
#define SOME_H
void f(void);
#endif // SOME_H
My some.c
#include "some.h"
#include "debug.h"
void f()
{
debug_printf("test");
}
My test_some.c
#include "fff.h"
#include "some.h"
#include <stdio.h>
#include <stdarg.h>
DEFINE_FFF_GLOBALS;
FAKE_VOID_FUNC2_VARARG(debug_printf, const char*, ...);
void debug_printf_custom(const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
void test_f(void)
{
debug_printf_fake.custom_fake = debug_printf_custom;
f();
}
int main(void)
{
test_f();
return 0;
}
When I try to run the test I've got the message:
> gcc test_some.c some.c -o test
test_some.c: In function ‘test_f’:
test_some.c:23:35: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
debug_printf_fake.custom_fake = debug_printf_custom;
^
What should I change to get the correct pointer type.
Got all together.
First of all:
void debug_printf_custom(const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
must be
void debug_printf_custom(const char* fmt, va_list ap)
{
vprintf(fmt, ap);
}
as pointed by #ianabbott.
Unfortunately the minimal example will work, but not the one with Ceedling.
The solution when using the fff-plugin from ElectronVector.
The fff.h is outdated and should be updated.
In your project folder:
cd vendor/ceedling/plugins/fake_function_framework/vendor/fff
git fetch
git checkout master

Is it possible to pass 2 functions which have different signature as an argument to another function?

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.

Custom print function with _TIME_, _FILE_, _FUNCTION_, _LINE_

I have following code in debug.h:
#ifndef __DEBUG_H__
#define __DEBUG_H__
#ifdef DEBUG
int al_debug(const char *format,
const char * time,
const char * file,
const char * function,
int line,
...);
#define debug(fmt, args...) al_debug(fmt, __TIME__, __FILE__, __FUNCTION__, __LINE__, args...)
#else /* IF DEBUG NOT DEFINED*/
#define debug(fmt, ...) /* DO NOT PRINT ANYTHING IF DEBUG IS NOT PRESENT */
#endif /* END OF DEBUG */
#endif /* END OF __DEBUG_H__ */
In debug.c:
#include <stdarg.h>
#include <string.h>
#define __WRAP_FUNCTION__
#include "debug.h"
#ifdef DEBUG
int debug(const char *format,
const char * time,
const char * file,
const char * function,
int line,
...)
{
int done=0;
va_list arg;
va_start(arg, format);
done = vfprintf(stdout, "%s :%s:%s:%d", time, file, function, line);
done += vfprintf(stdout, format, arg);
va_end(arg);
return done;
}
#endif
And after compiling it I am getting following errors:
gcc -g -Wall -DDEBUG -c debug.c -o d_debug.o
debug.c:16:1: error: expected declaration specifiers or ‘...’ before string constant
debug.c:16:1: error: expected declaration specifiers or ‘...’ before string constant
debug.c:11:5: error: expected declaration specifiers or ‘...’ before ‘__FUNCTION__’
debug.c:16:1: error: expected declaration specifiers or ‘...’ before numeric constant
How I can fix this problem? What is problem here? Thank you in advance.
1) Change function name
// int debug(
int al_debug(
2) Use regular fprintf()
// done = vfprintf(stdout, "%s :%s:%s:%d", time, file, function, line);
done = fprintf(stdout, "%s :%s:%s:%d", time, file, function, line);
3) Start at line. Use parameter before ...
// va_start(arg, format);
va_start(arg, line);
4) Pedantic note: add done check for < 0 after each print.
done = fprintf(stdout, "%s :%s:%s:%d", time, file, function, line);
if (done >= 0) {
int done_former = done
done = vfprintf(stdout, format, arg);
if (done >= 0) done += done_former;
}
va_end(arg);
return done;
5) Change args...) to args) #leeduhem
[Edit] to work with no args after format.
int al_debug(const char * time, const char * file, const char * function,
int line, const char *format, ...);
#define debug(...) al_debug(__TIME__, __FILE__, __func__, __LINE__, __VA_ARGS__)
// #leeduhem for __VA_ARGS__
#else /* IF DEBUG NOT DEFINED*/
#define debug(...) /* DO NOT PRINT ANYTHING IF DEBUG IS NOT PRESENT */
#endif /* END OF DEBUG */
int al_debug(const char * time, const char * file, const char * function,
int line, const char *format, ...) {
int done = 0;
va_list arg;
va_start(arg, format); // change to `format`
...
Fixed version:
debug.h
#ifndef __DEBUG_H__
#define __DEBUG_H__
#ifdef DEBUG
int al_debug(const char *time, const char *file, const char *func, int line, const char * format, ...);
#define debug(...) al_debug(__TIME__, __FILE__, __func__, __LINE__, __VA_ARGS__)
#else /* IF DEBUG NOT DEFINED*/
#define debug(...) /* DO NOT PRINT ANYTHING IF DEBUG IS NOT PRESENT */
#endif /* END OF DEBUG */
#endif /* END OF __DEBUG_H__ */
debug.c
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#define __WRAP_FUNCTION__
#include "debug.h"
#ifdef DEBUG
int al_debug(const char *time, const char *file, const char *func, int line, const char *format, ...)
{
int done=0;
va_list arg;
va_start(arg, format);
done = fprintf(stdout, "%s :%s:%s:%d: ", time, file, func, line);
done += vfprintf(stdout, format, arg);
va_end(arg);
return done;
}
#endif
test.c
#include <stdio.h>
#include <stdlib.h>
#include "debug.h"
void foo(void)
{
debug("debugg...\n");
debug("%s and %d\n", "debug information", 42);
}
int
main(int argc, char *argv[])
{
foo();
exit(EXIT_SUCCESS);
}
Testing:
$ gcc -g -Wall -I. -DDEBUG debug.c test.c
test.c: In function ‘main’:
test.c:12:10: warning: unused parameter ‘argc’ [-Wunused-parameter]
test.c:12:22: warning: unused parameter ‘argv’ [-Wunused-parameter]
$ ./a.out
10:46:40 :test.c:foo:8: debugg...
10:46:40 :test.c:foo:9: debug information and 42
$ gcc -g -Wall -I. debug.c test.c
test.c: In function ‘main’:
test.c:12:10: warning: unused parameter ‘argc’ [-Wunused-parameter]
test.c:12:22: warning: unused parameter ‘argv’ [-Wunused-parameter]
$ ./a.out
$
A few links that may be helpful:
6.20 Macros with a Variable Number of Arguments
6.47 Function Names as Strings

Resources