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

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.

Related

How to update a vargs to send to printf

I have the following code where I format a message that has been passed via a logger:
va_list args;
va_start(args, level);
// pop the msg and do some string replaces
char *msg = va_arg(args, char*);
ssize_t len = str_replace(tmp, "%(msg)s", msg, output, 1024);
// now how to get it 'back in' so I can print it?'
// msg = output
vfprintf(stderr, output, args);
The above code prints nothing, as it seems to have 'popped' the msg without me placing the updated msg back in there. What would be a possible way to do this?
Instead of fiddling with the format string, you could print the variadic arguments passed to the log function to a string first, then print that string with a regular fprintf via the %s format.
You haven't posted your function signature, but it looks as if you passed the format string as first variadic argument. That's unusual. I've changed the signature so that the format string is the last non-variadic arg. That's common and it also opens up the possibility to chech your format strings for correctness. (See below.)
Here's an example implementation:
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
enum {
FATAL, ERROR, WARNING, INFO, NLEVEL
};
void say(int level, const char *fmt, ...)
{
static const char *templates[NLEVEL] = {
"\033[1m!!! FATAL: %s !!!\033[0m\n",
"ERROR: %s\n",
"Warning: %s\n",
"%s Just sayin'.\n"
};
const char *tmp;
char output[1024];
va_list args;
// print arguments to temporary string
va_start(args, fmt);
vsnprintf(output, sizeof(output), fmt, args);
va_end(args);
// find and print template string with message
if (level < 0) level = 0;
if (level >= NLEVEL) level = INFO;
tmp = templates[level];
fprintf(stderr, tmp, output);
va_end(args);
if (level == FATAL) exit(1);
}
Use it like this:
int main(int argc, char *argv[])
{
int a = 6;
int b = 7;
double Tmin = -273.15;
say(INFO, "%d * %d == %d.", a, b, a * b);
say(INFO, "The %s is %s.", "apple", "red and juicy");
say(WARNING, "Temperature falls below Tmin (%gC)", Tmin);
say(ERROR, "That should %s have happened.",
(rand() % 2) ? "never" : "not really");
say(FATAL, "Out of coffee!");
return 0;
}
Finally, you can make your logging functions safer and let the compiler check the validity of format strings. GCC and Clang do this with arguments, the Microsoft compiler does it with SAL, the Source-code Annotation Language. Other compilers may have their own method to do such checks. (More likely, they just emulate one of the GC or MS systems.)
With a little macro dance in the header you can cater for both variants:
#if defined(_MSC_VER)
#define PRINTF_FMT _Printf_format_string_
#endif
#if defined(__GNUC__) || defined(__clang__)
#define PRINTF_ARGS(IFMT, IARGS) __attribute__((format(printf, IFMT, IARGS)))
#endif
#ifndef PRINTF_ARGS
#define PRINTF_ARGS(IFMT, IARGS)
#endif
#ifndef PRINTF_FMT
#define PRINTF_FMT
#endif
void say(int level, PRINTF_FMT const char *fmt, ...) PRINTF_ARGS(2, 3);
Now a call like this:
say(WARNING, "Meta: Wrong formt for %d.", ~0ull);
warns you about the wrong format specifier.

function polymorphisms depending on debug level

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);
}

Set a function argument with function pointers

In C is there a way to set a function argument using function pointers such that all calls to that function pointer just use the set parameter? As an example, is it possible to do something like the code below?
void do_something(int *a, int *b, int *c){
*a = *c - 5;
*b = *c - 10;
}
typedef void (*funcptr)(int *a, int *b, int *c);
int main(){
int num = 5;
funcptr f = do_something(c=&num); // c = 5 for all calls to f(a, b)
int *a; int *b;
f(a, b); //a = 0, b = -5
num = 10;
f = do_something(c=&num); // c = 10 for all calls to f(a, b)
f(a, b); //a = 5, b = 0
}
This seems possible with external C libraries like Blocks and FFCALL, but it appears I'd need to set a, b, and c instead of setting a single argument.
no, this is not a language feature of C or of the C calling convention.
You'll need to construct function that calls your function pointer with that argument yourself; external libraries that provide such functionality are available, as you've noticed.
Another way, though I'm personally not overly fond of that is using <stdarg> and varargs, letting your function do one thing or another, depending on the number of arguments passed.
In C frameworks, you'll often find a lot of functions that work in some kind of context. That is often even used in implementation of object-oriented programming. I don't know the larger picture of what you want to implement, but often questions like these are rised in a context where someone wants to do something that resembles function overloading in C++; if that's the case, you might really just make functions that take a "state/context" argument, and further arguments, which might or might not be relevant, depending on the state.
Rather than using function pointers, you would need a wrapper function that uses a global for state.
void do_something(int *a, int *b, int c){
*a = c - 5;
*b = c - 10;
}
static int c_val = NULL;
void do_something_wrap(int *a, int *b)
{
do_something(a, b, c);
}
int main(){
int num = 5;
c_val = num;
int a; int b;
do_something_wrap(&a, &b); //a = 0, b = -5
num = 10;
c_val = num;
do_something_wrap(&a, &b); //a = 5, b = 0
}
C has no such features like default arguments or function overloading.
Moreover in this declaration
funcptr f = do_something;
the initializer shall be a function pointer.
Standard C has no such facility. Compilers offer extensions that allow this though. Nested functions in GCC, Blocks in clang or via a library like CALLBACK in clisp or assembling your call via libffi.
[..] is there a way [..] ?
There always is a way. The question is if it's worth the effort and possible downsides.
The following is based on another answer I gave about deferred (that is, called e.g. at the end of scope) function execution.
Start off with a unified type for a "partially applied function":
struct partially_applied {
void * data; // parameters
void (*function)(void *); // function unpacking parameters and calling actual function
void (*store)(void *, char const *, void *); // storing parameters
};
typedef struct partially_applied * FUN;
To make a function partially apply-able we need
a struct holding the (eventually) already applied parameters (I added an init function and an allocation function)
a function to unpack these and call the actual function
a function to store ("partially apply") parameters
Here we go:
#define MAKE_PARTIAL(fn, N, ...) \
struct partially_applied_ ## fn ## _data { \
DATA_DEF(N, __VA_ARGS__) \
}; \
\
static void init_partially_applied_ ## fn ## _data (void * p) { \
struct partially_applied_ ## fn ## _data * data = p; \
DATA_INIT(N, __VA_ARGS__); \
} \
\
static void * allocate_partially_applied_ ## fn ## _data (void) { \
void * data = malloc(sizeof(struct partially_applied_ ## fn ## _data)); \
if (data == NULL) { \
fprintf(stderr, "Allocation failure for " #fn " data\n"); \
exit(1); \
} \
init_partially_applied_ ## fn ## _data(data); \
return data; \
} \
\
static void partially_applied_ ## fn (void * p) { \
struct partially_applied_ ## fn ## _data * data = p; \
if (DATA_CHECK(N, __VA_ARGS__)) { \
fn(DATA_ACCESS(N, __VA_ARGS__)); \
} else { \
fprintf(stderr, "Not all parameters for " #fn " are vaild\n"); \
} \
} \
\
static void partially_applied_ ## fn ## _store ( \
void * p, char const * id, void * src) { \
struct partially_applied_ ## fn ## _data * data = p; \
DATA_STORE_CODE(N, __VA_ARGS__) \
fprintf(stderr, "Cannot store %s in " #fn "!\n", id); \
}
The above contains some macros. These depend on the number of macro arguments (which could be counted by the preprocessor, but I want to keep it simpler). To expand the correct macro (depending on the number of arguments) we need a little helper:
#define SPLICE_2(l,r) l##r
#define SPLICE_1(l,r) SPLICE_2(l,r)
#define SPLICE(l,r) SPLICE_1(l,r)
Now on to the macros. DATA_DEF defines the structure contents:
#define DATA_DEF_0(...)
#define DATA_DEF_1(type, name) type name; bool name ## _valid;
#define DATA_DEF_2(type, name, ...) type name; bool name ## _valid; DATA_DEF_1(__VA_ARGS__)
#define DATA_DEF_3(type, name, ...) type name; bool name ## _valid; DATA_DEF_2(__VA_ARGS__)
#define DATA_DEF_4(type, name, ...) type name; bool name ## _valid; DATA_DEF_3(__VA_ARGS__)
// add more to support more parameters
#define DATA_DEF(N, ...) SPLICE(DATA_DEF_,N)(__VA_ARGS__)
DATA_INIT expands to code to initialize such a structure:
#define DATA_INIT_0(...)
#define DATA_INIT_1(t, name) data->name ## _valid = false;
#define DATA_INIT_2(t, name, ...) data->name ## _valid = false; DATA_INIT_1(__VA_ARGS__)
#define DATA_INIT_3(t, name, ...) data->name ## _valid = false; DATA_INIT_2(__VA_ARGS__)
#define DATA_INIT_4(t, name, ...) data->name ## _valid = false; DATA_INIT_3(__VA_ARGS__)
// add more to support more parameters
#define DATA_INIT(N, ...) SPLICE(DATA_INIT_,N)(__VA_ARGS__)
DATA_CHECK expands to a condition testing if all arguments have been applied:
#define DATA_CHECK_0(...) true
#define DATA_CHECK_1(t, name) data->name ## _valid
#define DATA_CHECK_2(t, name, ...) data->name ## _valid && DATA_CHECK_1(__VA_ARGS__)
#define DATA_CHECK_3(t, name, ...) data->name ## _valid && DATA_CHECK_2(__VA_ARGS__)
#define DATA_CHECK_4(t, name, ...) data->name ## _valid && DATA_CHECK_3(__VA_ARGS__)
// add more to support more parameters
#define DATA_CHECK(N, ...) SPLICE(DATA_CHECK_,N)(__VA_ARGS__)
DATA_ACCESS expands into code for passing the parameters to the actual function (actually it's only the comma separated arguments list):
#define DATA_ACCESS_0(...)
#define DATA_ACCESS_1(t, name) data->name
#define DATA_ACCESS_2(t, name, ...) data->name, DATA_ACCESS_1(__VA_ARGS__)
#define DATA_ACCESS_3(t, name, ...) data->name, DATA_ACCESS_2(__VA_ARGS__)
#define DATA_ACCESS_4(t, name, ...) data->name, DATA_ACCESS_3(__VA_ARGS__)
// add more to support more parameters
#define DATA_ACCESS(N, ...) SPLICE(DATA_ACCESS_,N)(__VA_ARGS__)
And finally DATA_STORE_CODE expands into code to store the parameters:
#define DATA_STORE_CODE_OP(type, name) \
if (strcmp(id, #name) == 0) { data->name = *((type *) src); data->name ## _valid = true; return; }
#define DATA_STORE_CODE_0(...)
#define DATA_STORE_CODE_1(type, name) DATA_STORE_CODE_OP(type, name)
#define DATA_STORE_CODE_2(type, name, ...) DATA_STORE_CODE_OP(type, name) DATA_STORE_CODE_1(__VA_ARGS__)
#define DATA_STORE_CODE_3(type, name, ...) DATA_STORE_CODE_OP(type, name) DATA_STORE_CODE_2(__VA_ARGS__)
#define DATA_STORE_CODE_4(type, name, ...) DATA_STORE_CODE_OP(type, name) DATA_STORE_CODE_3(__VA_ARGS__)
// more
#define DATA_STORE_CODE(N, ...) SPLICE(DATA_STORE_CODE_,N)(__VA_ARGS__)
Adding little helpers to allocate and free partially applied function structures (data is expected to be allocated by malloc here) ...
FUN make_fun(void (*function)(void *), void (*store)(void *, char const *, void *), void * data) {
FUN f = malloc(sizeof(*f));
if (f == NULL) {
fprintf(stderr, "Allocation of FUN failed\n");
exit(1);
}
f->function = function;
f->store = store;
f->data = data;
return f;
}
void free_fun(FUN f) {
free(f->data);
free(f);
}
... we can go on to define a macro that actually makes an instance of a partially applied function:
#define PARTIAL(fn) make_fun(&(partially_applied_ ## fn), \
&(partially_applied_ ## fn ## _store), \
allocate_partially_applied_ ## fn ## _data())
Of course we want to be able to apply some arguments:
#define APPLY(PFN, N, ...) \
do { \
struct partially_applied * pfn = (PFN); \
DATA_STORE(N, __VA_ARGS__) \
} while(0)
The macro DATA_STORE expands into code to call the store functions multiple times, so that we can apply multiple arguments at once:
#define DATA_STORE_OP(name, value) pfn->store(pfn->data, #name, &(value));
#define DATA_STORE_0(...)
#define DATA_STORE_1(name, value) DATA_STORE_OP(name, value)
#define DATA_STORE_2(name, value, ...) DATA_STORE_OP(name, value) DATA_STORE_1(__VA_ARGS__)
#define DATA_STORE_3(name, value, ...) DATA_STORE_OP(name, value) DATA_STORE_2(__VA_ARGS__)
#define DATA_STORE_4(name, value, ...) DATA_STORE_OP(name, value) DATA_STORE_3(__VA_ARGS__)
#define DATA_STORE(N, ...) SPLICE(DATA_STORE_,N)(__VA_ARGS__)
Last but not least we want to be able to call such a function (this could also be a function, but well):
#define CALL(fn) (fn)->function((fn)->data)
Finally, an example:
void foo(char * str, int i) {
printf("FOO| str = %s, i = %d\n", str, i);
}
void bar(float f, int i, size_t s) {
printf("BAR| f = %f, i = %d, s = %zu\n", f, i, s);
}
MAKE_PARTIAL(foo, 2, char *, string, int, integer)
MAKE_PARTIAL(bar, 3, float, floating, int, INT, size_t, SOME_SIZE)
int main() {
FUN f = PARTIAL(foo);
char * c = "Crazy";
APPLY(f, 1, string, c);
printf("doing other stuff\n");
FUN g = PARTIAL(bar);
size_t size = 99;
APPLY(g, 1, SOME_SIZE, size);
int answer = 42;
APPLY(f, 1, integer, answer);
answer = 21;
float pi = 3.14;
APPLY(g, 2, INT, answer, floating, pi);
CALL(f);
printf("done\n");
CALL(g);
printf("now completely done\n");
return 0;
}
Some downsides:
macros. macros everywhere.
Losing some type safety (in APPLY)
Need for lvalues (APPLY(f, 1, integer, 42) does not work)

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

Passing variable number of arguments around

Say I have a C function which takes a variable number of arguments: How can I call another function which expects a variable number of arguments from inside of it, passing all the arguments that got into the first function?
Example:
void format_string(char *fmt, ...);
void debug_print(int dbg_lvl, char *fmt, ...) {
format_string(fmt, /* how do I pass all the arguments from '...'? */);
fprintf(stdout, fmt);
}
To pass the ellipses on, you initialize a va_list as usual and simply pass it to your second function. You don't use va_arg(). Specifically;
void format_string(char *fmt,va_list argptr, char *formatted_string);
void debug_print(int dbg_lvl, char *fmt, ...)
{
char formatted_string[MAX_FMT_SIZE];
va_list argptr;
va_start(argptr,fmt);
format_string(fmt, argptr, formatted_string);
va_end(argptr);
fprintf(stdout, "%s",formatted_string);
}
There's no way of calling (eg) printf without knowing how many arguments you're passing to it, unless you want to get into naughty and non-portable tricks.
The generally used solution is to always provide an alternate form of vararg functions, so printf has vprintf which takes a va_list in place of the .... The ... versions are just wrappers around the va_list versions.
Variadic Functions can be dangerous. Here's a safer trick:
void func(type* values) {
while(*values) {
x = *values++;
/* do whatever with x */
}
}
func((type[]){val1,val2,val3,val4,0});
In magnificent C++11 you could use variadic templates:
template <typename... Ts>
void format_string(char *fmt, Ts ... ts) {}
template <typename... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts... ts)
{
format_string(fmt, ts...);
}
Though you can solve passing the formatter by storing it in local buffer first, but that needs stack and can sometime be issue to deal with. I tried following and it seems to work fine.
#include <stdarg.h>
#include <stdio.h>
void print(char const* fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vprintf(fmt, arg);
va_end(arg);
}
void printFormatted(char const* fmt, va_list arg)
{
vprintf(fmt, arg);
}
void showLog(int mdl, char const* type, ...)
{
print("\nMDL: %d, TYPE: %s", mdl, type);
va_list arg;
va_start(arg, type);
char const* fmt = va_arg(arg, char const*);
printFormatted(fmt, arg);
va_end(arg);
}
int main()
{
int x = 3, y = 6;
showLog(1, "INF, ", "Value = %d, %d Looks Good! %s", x, y, "Infact Awesome!!");
showLog(1, "ERR");
}
Hope this helps.
You can try macro also.
#define NONE 0x00
#define DBG 0x1F
#define INFO 0x0F
#define ERR 0x07
#define EMR 0x03
#define CRIT 0x01
#define DEBUG_LEVEL ERR
#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...) if((DEBUG_LEVEL & X) == X) \
DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)
int main()
{
int x=10;
DEBUG_PRINT(DBG, "i am x %d\n", x);
return 0;
}
You can use inline assembly for the function call. (in this code I assume the arguments are characters).
void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
{
va_list argumentsToPass;
va_start(argumentsToPass, fmt);
char *list = new char[numOfArgs];
for(int n = 0; n < numOfArgs; n++)
list[n] = va_arg(argumentsToPass, char);
va_end(argumentsToPass);
for(int n = numOfArgs - 1; n >= 0; n--)
{
char next;
next = list[n];
__asm push next;
}
__asm push fmt;
__asm call format_string;
fprintf(stdout, fmt);
}
Ross' solution cleaned-up a bit. Only works if all args are pointers. Also language implementation must support eliding of previous comma if __VA_ARGS__ is empty (both Visual Studio C++ and GCC do).
// pass number of arguments version
#define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}
// NULL terminated array version
#define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}
Short answer
/// logs all messages below this level, level 0 turns off LOG
#ifndef LOG_LEVEL
#define LOG_LEVEL 5 // 0:off, 1:error, 2:warning, 3: info, 4: debug, 5:verbose
#endif
#define _LOG_FORMAT_SHORT(letter, format) "[" #letter "]: " format "\n"
/// short log
#define log_s(level, format, ...) \
if (level <= LOG_LEVEL) \
printf(_LOG_FORMAT_SHORT(level, format), ##__VA_ARGS__)
usage
log_s(1, "fatal error occurred");
log_s(3, "x=%d and name=%s",2, "ali");
output
[1]: fatal error occurred
[3]: x=2 and name=ali
log with file and line number
const char* _getFileName(const char* path)
{
size_t i = 0;
size_t pos = 0;
char* p = (char*)path;
while (*p) {
i++;
if (*p == '/' || *p == '\\') {
pos = i;
}
p++;
}
return path + pos;
}
#define _LOG_FORMAT(letter, format) \
"[" #letter "][%s:%u] %s(): " format "\n", _getFileName(__FILE__), __LINE__, __FUNCTION__
#ifndef LOG_LEVEL
#define LOG_LEVEL 5 // 0:off, 1:error, 2:warning, 3: info, 4: debug, 5:verbose
#endif
/// long log
#define log_l(level, format, ...) \
if (level <= LOG_LEVEL) \
printf(_LOG_FORMAT(level, format), ##__VA_ARGS__)
usage
log_s(1, "fatal error occurred");
log_s(3, "x=%d and name=%s",2, "ali");
output
[1][test.cpp:97] main(): fatal error occurred
[3][test.cpp:98] main(): x=2 and name=ali
custom print function
you can write custom print function and pass ... args to it and it is also possible to combine this with methods above. source from here
int print_custom(const char* format, ...)
{
static char loc_buf[64];
char* temp = loc_buf;
int len;
va_list arg;
va_list copy;
va_start(arg, format);
va_copy(copy, arg);
len = vsnprintf(NULL, 0, format, arg);
va_end(copy);
if (len >= sizeof(loc_buf)) {
temp = (char*)malloc(len + 1);
if (temp == NULL) {
return 0;
}
}
vsnprintf(temp, len + 1, format, arg);
printf(temp); // replace with any print function you want
va_end(arg);
if (len >= sizeof(loc_buf)) {
free(temp);
}
return len;
}
Let's say you have a typical variadic function you've written. Because at least one argument is required before the variadic one ..., you have to always write an extra argument in usage.
Or do you?
If you wrap your variadic function in a macro, you need no preceding arg. Consider this example:
#define LOGI(...)
((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
This is obviously far more convenient, since you needn't specify the initial argument every time.
I'm unsure if this works for all compilers, but it has worked so far for me.
void inner_func(int &i)
{
va_list vars;
va_start(vars, i);
int j = va_arg(vars);
va_end(vars); // Generally useless, but should be included.
}
void func(int i, ...)
{
inner_func(i);
}
You can add the ... to inner_func() if you want, but you don't need it. It works because va_start uses the address of the given variable as the start point. In this case, we are giving it a reference to a variable in func(). So it uses that address and reads the variables after that on the stack. The inner_func() function is reading from the stack address of func(). So it only works if both functions use the same stack segment.
The va_start and va_arg macros will generally work if you give them any var as a starting point. So if you want you can pass pointers to other functions and use those too. You can make your own macros easily enough. All the macros do is typecast memory addresses. However making them work for all the compilers and calling conventions is annoying. So it's generally easier to use the ones that come with the compiler.

Resources