I am looking to make a small logger to be used like this:
logger log;
const char* text = "World";
log.write("Hello %s", text);
log.write("Step %d", 1);
This is my code, which doesn't work correctly:
class logger
{
public:
void write(const char* msg, ...)
{
FILE* file = fopen("c:/test.txt", "a");
if(file != NULL)
{
va_list args;
va_start(args, msg);
fprintf(file, "%s\n", msg, args);
va_end(args);
fclose(file);
}
}
};
This is what I get:
Hello %s
Step %d
I never used varargs before so I am not sure if I am using it correctly.
You're thinking that C behaves in a manner similar to Java or Python or other languages that can "splat" an array argument to a function that accepts varargs, but C isn't that sophisticated. When you pass args to fprintf, C literally pushes the value of args (a variable of type va_list) onto the stack. What you need instead is to push the contents of args onto the stack.
Or, instead, you could use a function that accepts a va_list as a parameter. That function is vprintf (and friends vsprintf, vfprintf, etc.).
You have another issue, which is that you're fprintf-ing the caller's "msg" parameter using "%s" but then apparently expecting fprintf to recursively use the result as a format string and fprintf the args too. That's not going to work. Instead just use the caller's msg as the format string.
va_list args;
va_start(args, msg);
vfprintf(file, msg, args);
fputc('\n', file);
va_end(args);
Related
How to make a function printf its output on a file or on console? Being more specific, If in the main function I have two opinions: 1. printf the output on a file or 2. printf the output on console; and I have that other function which has the output, I would need to return that output to the main function, right? Or is there another way?
The easiest way is to NOT use printf but to use fprintf instead. Basically printf(arg1, arg2, ...) is the same as fprintf(stdout, arg1, arg2, ...). As far as I know, printf will always print to the console no matter what you do, so if you want to redirect it, you will have to do it via the shell while invoking the program. In bash, it would be something like ./a.out > file.txt. It's possible that there exist some way to redirect stdout to a file from within the program, but I suspect that it would not be a very idiomatic solution, but rather just a proof that it's theoretically possible.
Here is an example of how you can solve it with fprintf:
int main(int argc, char ** argv)
{
FILE * fp;
// If the program was invoked with an argument, treat it as a filename
// and write the output to that file. Otherwise, write to stdout.
if(argc == 2) fp = fopen(argv[1], "w");
else fp = stdout;
fprintf(fp, "Hello, world!\n");
fflush(fp); // Make sure everything gets printed.
fclose(fp);
}
I excluded all error checks here. You shouldn't. You should check if fopen succeeded for instance. The condition in the if statement is also far from perfect. This is just a demonstration.
You could simply have a boolean flag, that if set then you call fprintf to print to the file, or else you print using printf (which will go to the console).
Something like
bool print_to_file = false;
FILE *file;
print_to_file = get_print_to_file_choice();
if (print_to_file)
file = get_file_to_print_to();
// ...
if (print_to_file)
fprintf(file, "some text here...\n");
else
printf("some text here...\n");
Now the above is easy to implement, but hard to maintain. What if a string needs to be changed? Then you need to remember to change both for the fprintf and the printf calls.
So you could put this in a function, which takes the flag and file variables as arguments, together with the format string and arguments. This function could use the varargs variants of the printing functions (vfprintf and vprintf) and do the condition in the function. In your main code you could then call this function instead.
Perhaps something like
void my_printf(bool print_to_file, FILE *file, const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
if (print_to_file)
vfprintf(file, fmt, va);
else
vprintf(fmt, va);
va_end(va);
}
// ...
my_printf(print_to_file, file, "Some text here...\n");
There are other solutions possible, like using freopen as mentioned in a comment. Or you could simply print to stdout (with plain printf) and then redirect the output in the shell or command prompt to a file.
In your case you need fprintf instead of printf.
fprintf works almost similar to printf except that you pass an additional first argument to it. That argument is where your output is going to land.
If you want the output on a file, pass the FILE* pointer to your file as that argument.
If you want the output on the console, pass stdout as that argument.
For example,
FILE* myFile = fopen("demo.txt", "w");
int dummy = 22;
fprintf(myFile, "%d will be written in <demo.txt>", dummy);
fprintf(stdout, "%d will be written in the console", dummy);
Note:
You don't need any new header file for fprintf. It resides inside the regularly included stdio.h. Here is a complete reference for fprintf.
I am coding under Arduino and I would like to develop serial print formatting function, so I am trying to use sprintf of unknown sized buffer. Basically, we can avoid talking about Arduino and its serial output and consider writing text to a buffer and then printing it by using printf. I've tried this one:
#include <stdio.h>
#include <stdarg.h>
void printf0( const char* format, ... ) {
va_list args;
va_start(args, format);
vprintf(format, args);
va_end( args );
}
void printf1(const char* format,...) {
va_list args;
va_start(args, format);
char buf[vsnprintf(NULL, 0, format, args)];
sprintf(buf, format, args);
printf(buf);
va_end(args);
}
int main()
{
printf0("Hello, %d!\n", 15);
printf1("Hello, %d!\n", 15);
return 0;
}
printf0 function is an accurate example I found here. My tries is function printf1, which produces unpredictable number. Example output of the above programme is:
Hello, 15!
Hello, 860799736!
args is a va_list, so you cannot call sprintf with it. You have to use vsprintf or vsnprintf:
sprintf(buf, format, args);
should be
vsnprintf(buf, sizeof buf, format, args);
Also you should add 1 to the size of buf for the 0-terminator of the string:
char buf[vsnprintf(NULL, 0, format, args) + 1];
It seems that the first call to vsnprintf changes args, so you have to add
va_end(args);
va_start(args, format);
between the 2 calls:
http://ideone.com/5YI4Or
It seems that the first call to vsnprintf changes args, but you should not call va_start twice. You should use va_copy instead, so add
va_list args2;
va_copy(args2, args);
after initializing args. Also do not forget to call va_end(args2); too:
http://ideone.com/loTRNL
Link to the va_copy man page: https://linux.die.net/man/3/va_copy
I have function Log that logs variable length arguments. Unfortunately this function logs just first element. What is wrong?
Log(INFO,"aaa","bbb","ccc");
void Log(int level, char const * fmt, ...)
{
int len = 4096;
char buffer[len];
int ret;
va_list args;
va_start(args, fmt);
ret = vsnprintf(buffer, len, fmt, args);
va_end(args);
FILE *fOut;
fOut = fopen(nvLog_File, "at");
if(fOut)
{
fprintf(fOut, "%s\n",buffer);//,
printf("%s\n",buffer);
fclose(fOut);
} else fprintf(stderr, "can't open file %s", nvLog_File);
}
Your logging function works in a similar way to printf(). The first argument after log level should be formatting string, according to which the rest of arguments is interpreted. In order to print three strings, you should use log(INFO, "%s%s%s", "aaa", "bbb", "ccc");
The first string argument is the format. You need to specify where/how to format the other arguments. Try something like this:
Log(INFO, "aaa %s %s", "bbb", "ccc");
You should look at a reference for printf-style functions (http://www.cplusplus.com/reference/cstdio/printf/, for example).
According to this
Log(INFO,"aaa","bbb","ccc");
You pass in "aaa" as the format string, so when you forward it to printf you should see precisley that in your log. Everything else is ignored, as you are not making use of it.
I have one problem with variable length argument debug log print function. I will just simulate the code here.
void SecondPrint(int level, const char* format,...)
{
//Printing the log here
va_list arg;
va_start(arg, format);
vprintf(format, arg);
va_end(arg);
}
void FirstPrint(int level, const char* format,...)
{
SecondPrint(level,format);
}
void main()
{
int level = 100;
FirstPrintf("Log level = %d message = %s \n",level,"Error message");
}
"SecondPrint" is supposed to print "100 Error message" as expected, But its not printing like that its printing " Error message".
I am not getting whats wrong with this one. I am suspecting the way of calling "SecondPrint" from "FirstPrint" function. FirstPrint is receiving remaining arguments through ... but its invoking SecondPrint with level and format arguments.
I can't use SecondPrint from main directly. I have to use FirstPrint and FirstPrint has to call SecondPrint to print the log. So how can I achieve this one.. I thought to use VA_ARGS but it is for only macro definitions not in function definition.
And one more thing I can't do like *#define FirstPrint(a,b,...) SecondPrint(a,b,...)*
any help is highly appreciated thanks in advance.
C varargs are not designed to be passed more than one level; the kind of necessary stack manipulation required is too deep for the language. Typically, in a case like this, you would have a version of SecondPrint analagous to vprintf -- SecondPrintV or similar, and you would have FirstPrint invoke SecondPrintV after extracting the varargs, rather than invoking SecondPrint directly (and, for consistency, usually have SecondPrint invoke SecondPrintV internally).
You need to pass a va_list to SecondPrint, so it can access the arguments to FirstPrint
void SecondPrint(int level, const char* format, va_list args)
{
//Printing the log here
vprintf(format, arg);
}
void FirstPrint(int level, const char* format,...)
{
va_list args;
va_start(args, format);
SecondPrint(level, format, args);
va_end(args);
}
void main()
{
int level = 100;
FirstPrintf("Log level = %d message = %s \n",level,"Error message");
}
It may be possible to do it in specific platform-specific ways, or (since the arguments are exactly the same in the same order) use assembly to "replace" the current function frame with the function to call. GCC also has an extension to do it with the __builtin_apply_args() and __builtin_apply() functions (although you must somehow figure out how to compute the size of the stack argument data).
I'm writing an assignment which involves adding some functionality to PostgreSQL on a Solaris box. As part of the assignment, we need to print some information on the client side (i.e.: using elog.)
PostgreSQL already has lots of helper methods which print out the required information, however, the helper methods are packed with hundreds of printf calls, and the elog method only works with c-style strings.
Is there I way that I could temporarily redirect printf calls to a buffer so I could easily send it over elog to the client?
If that's not possible, what would be the simplest way to modify the helper methods to end up with a buffer as output?
If you define your own version of printf and link to it prior to the libc version, your version will supersede the standard version.
You should also be able to supersede the standard version by using LD_PRELOAD to load a library that has printf defined.
To write your own printf, you will want to use stdarg functionality:
int printf(const char *fmt, ...)
{
int rv;
va_list ap;
va_start(ap, fmt);
if (redirect_printf)
{
#ifdef HAVE_VLOG
// If you have a vlog function that takes a va_list
vlog(fmt, ap);
rv = ...;
#else
char buffer[LARGESIZE];
rv = vsnprintf(buffer, sizeof(buffer), fmt, ap);
log(buffer);
#endif;
}
else
{
rv = vprintf(fmt, ap);
}
return rv;
}
This simple version will truncate data when the final formatted output is greater than LARGESIZE. If you don't want that, you can also call vsnprintf first with a NULL buffer to get the final size, do a dynamic allocation and then a second call to vsprintf to format the buffer.
You're wrong — elog supports format strings just like printf. Here's an example from Postgres source code:
elog(DEBUG4, "TZ \"%s\" gets max score %d", tzname, i);
So all you need is to add elog where there is printf using the same parameters.
The simplest way is to modify the helper methods to call sprintf(). Whether or not you can hack that in easily, I don't know. Maybe
#define printf(...) sprintf(buffer, __VA_ARGS__)
Will do it for you. You'll still need to define buffer for each helper function, and get its contents returned to whoever cares about them.
If you can tolerate the use of a temporary file you could redirect standard out with the freopen() call:-
newstdout = freopen("/tmp/log", "w", stdout);
This will force all the printf's to be written to /tmp/log instead of the console output. At some convenient point later in your program you could then open the same file for reading:-
readfd = fopen("/tmp/log", "r");
and forward the contents that have been added using something like this:-
void forward_to_elog(void)
{
int bytesread;
char buf[100];
memset(buf,0,100);
do {
memset(buf,0,100);
bytesread = fread(buf, sizeof(buf)-1, 1, readfd);
/* call elog(buf) */ ;
} while(bytesread);
}
If you keep the file open you can call forward_to_elog() multiple times to incrementally forward the contents that have been added.
The tmpnam() function can be used to get a name for the temporary file if you don't want to have to statically code one.