passing variable number of arguments to a function


A

arnuld

PURPOSE: To print the logs according to the value of the global variable
DESIRED_LOG_LEVEL. See comments.

WHAT I GOT: compile-time errors



/* The purpose of this program is to print logs to the stdout. Which logs
will be printed will depend on the first parameter given
* to write_log(). LOG FLAGS are hierarcy based. If global variable
DESIRED_LOG_FLAG is set to PRINT_VAR, then it will execute
* every write_log() call containing PRINT_VAR, PRINT_EXTRA and
PRINT_NIL. If PRINT_EXTRA is set then it will execute the write_log()
* calls containing PRINT_EXTRA and PRINT_NILL but not PRINT_VAR and if
PRINT_NIL is set, it will only execute write_log() calls
* containing PRINT_NIL and nothing else. Means all the flags existing
above the one in use will always be printed. PRINT_NIL will
* actually print nothing on stdout. Its code will only contain a semi-
colon. e.g. if(DESIRED_LOG_LEVEL == PRINT_NIL) ;
*
* Here is a general call to write_log:
*
* write_log( LOG_FLAG, string literal, variable-number-of-arguments)
*
* the first 2 parameter will always be there, only the last parmeter may
or may not be there and it may contain any number of arguments.
*
*
* VERSION 0.0
*
*/


#include <stdio.h>
#include <stdarg.h>

enum LOG_FLAG
{
PRINT_NIL,
PRINT_EXTRA,
PRINT_VAR
};


enum SIZES { ARRSIZE = 50 };


int DESIRED_LOG_FLAG = PRINT_EXTRA;
/* enum LOG_FLAG flag_type[] = { PRINT_NIL, PRINT_EXTRA, PRINT_VAR, 0 };
Last zero will work as NUL, the array terminator */
void write_log(int pflag, const char*, ...);


int main(void)
{
int idx;
char* p = "This is comp.lang.c";
char arrc[] = "This is comp.lang.c";

write_log(PRINT_EXTRA, NULL, "This is a program which prints logs
depending the values given to it");
for( idx = 0; idx != 3; ++idx)
{
write_log(PRINT_VAR, "PRINT_VAR, p = %s", p);
write_log(PRINT_EXTRA, "PRINT_EXTRA: arrc = %s", arrc);
write_log(PRINT_NIL, "Just don't print this");
}


return 0;
}




/* Exmaples:

write_log( PRINT_VAR, "Filename is %s\n", __FILE__);
write_log( PRINT_EXTRA, "FILE: %s, Error occured at LINE: %d\n",
__FILE__, __LINE__);
*/
void write_Log(int log_flag, const char* ptr, ...)
{
char c;
va_list ap;
va_start(ap, ptr);

while((c = *ptr++))
{
switch(c)
{
case PRINT_VAR:
printf("[PRINT_VAR] : %d", va_arg(ap, int));
break;

case PRINT_NIL:
printf("LOG_NIL\n");
break;

default:
break;
}
}

va_end(ap);
}

==================== OUTPUT ============================
[[email protected] programs]$ gcc -ansi -pedantic -Wall -Wextra log.c
log.c:65: warning: unused parameter ‘log_flag’
/tmp/ccwxEoR5.o: In function `main':
log.c:(.text+0x58): undefined reference to `write_log'
log.c:(.text+0x7c): undefined reference to `write_log'
log.c:(.text+0x97): undefined reference to `write_log'
log.c:(.text+0xab): undefined reference to `write_log'
collect2: ld returned 1 exit status
[[email protected] programs]$



Since DESIRED_LOG_LEVEL = PRINT_EXTRA, it should print the information in
PRINT_EXTRA and PRINT_NIL but it does not.
 
Ad

Advertisements

I

Ike Naar

#include <stdio.h>
#include <stdarg.h>

enum LOG_FLAG
{
PRINT_NIL,
PRINT_EXTRA,
PRINT_VAR
};


enum SIZES { ARRSIZE = 50 };


int DESIRED_LOG_FLAG = PRINT_EXTRA;
/* enum LOG_FLAG flag_type[] = { PRINT_NIL, PRINT_EXTRA, PRINT_VAR, 0 };
Last zero will work as NUL, the array terminator */
void write_log(int pflag, const char*, ...);


int main(void)
{
int idx;
char* p = "This is comp.lang.c";
char arrc[] = "This is comp.lang.c";

write_log(PRINT_EXTRA, NULL, "This is a program which prints logs
depending the values given to it");
for( idx = 0; idx != 3; ++idx)
{
write_log(PRINT_VAR, "PRINT_VAR, p = %s", p);
write_log(PRINT_EXTRA, "PRINT_EXTRA: arrc = %s", arrc);
write_log(PRINT_NIL, "Just don't print this");
}


return 0;
}




/* Exmaples:

write_log( PRINT_VAR, "Filename is %s\n", __FILE__);
write_log( PRINT_EXTRA, "FILE: %s, Error occured at LINE: %d\n",
__FILE__, __LINE__);
*/
void write_Log(int log_flag, const char* ptr, ...)
{
char c;
va_list ap;
va_start(ap, ptr);

while((c = *ptr++))
{
switch(c)
{
case PRINT_VAR:
printf("[PRINT_VAR] : %d", va_arg(ap, int));
break;

case PRINT_NIL:
printf("LOG_NIL\n");
break;

default:
break;
}
}

va_end(ap);
}

==================== OUTPUT ============================
[[email protected] programs]$ gcc -ansi -pedantic -Wall -Wextra log.c
log.c:65: warning: unused parameter ‘log_flag’
/tmp/ccwxEoR5.o: In function `main':
log.c:(.text+0x58): undefined reference to `write_log'
log.c:(.text+0x7c): undefined reference to `write_log'
log.c:(.text+0x97): undefined reference to `write_log'
log.c:(.text+0xab): undefined reference to `write_log'
collect2: ld returned 1 exit status
[[email protected] programs]$

In the definition of your ``write_log'' function, you spell the
name as ``write_Log'' (capital 'L').
 
A

arnuld

The program works fine, I am posting it here to get suggestions. All
kinds of suggestions and improvements are welcomed :)


/* The purpose of this program is to print logs to the stdout. Which logs
will be printed will depend on the first parameter given
* to write_log(). LOG FLAGS are hierarcy based. If global variable
DESIRED_LOG_FLAG is set to PRINT_VAR, then it will execute
* every write_log() call containing PRINT_VAR, PRINT_EXTRA and
PRINT_NIL. If PRINT_EXTRA is set then it will execute the write_log()
* calls containing PRINT_EXTRA and PRINT_NILL but not PRINT_VAR and if
PRINT_NIL is set, it will only execute write_log() calls
* containing PRINT_NIL and nothing else. Means all the flags existing
above the one in use will always be printed. PRINT_NIL will
* actually print nothing on stdout. Its code will only contain a semi-
colon. e.g. if(DESIRED_LOG_LEVEL == PRINT_NIL) ;
*
* Here is a general call to write_log:
*
* write_log( LOG_FLAG, string literal, variable-number-of-arguments)
*
* the first 2 parameter will always be there, only the last parmeter may
or may not be there and it may contain any number of arguments.
*
*
* VERSION 0.1
*
*/



#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include "log.h"

int main(void)
{
char* p = "pppppp";
char arrc[] = "arr.....";

write_log(PRINT_VAR, "p = %s", p);
write_log(PRINT_EXTRA, "IN: %s, arrc = %s", __FILE__, arrc);
write_log(PRINT_NIL, "This must not print");

return 0;
}




/* Exmaples:
write_log( PRINT_VAR, "Filename is %s\n", __FILE__);
write_log( PRINT_EXTRA, "FILE: %s, Error occured at LINE: %d\n",
__FILE__, __LINE__);
*/
void write_log(int log_flag, const char* ptr, ...)
{
char arrc[20] = {0};
time_t t = time(NULL);
va_list ap;
va_start(ap, ptr);

if((PRINT_NIL != log_flag) &&
(DESIRED_LOG_FLAG >= log_flag))
{
get_log_level(log_flag, arrc);
printf("[%s], [%s]: ", ctime(&t), arrc);
vprintf(ptr,ap);
printf("\n");
}

va_end(ap);
}



void get_log_level(int f, char* c)
{
switch(f)
{
case PRINT_EXTRA:
strcpy(c, "PRINT_EXTRA");
break;
case PRINT_VAR:
strcpy(c, "PRINT_VAR");
break;
}
}

===================== OUTPUT ============================
[[email protected] programs]$ gcc -ansi -pedantic -Wall -Wextra log.c
[[email protected] programs]$ ./a.out
[Mon Aug 3 15:38:11 2009
], [PRINT_EXTRA]: IN: log.c, arrc = arr.....
[[email protected] programs]$



The only problem is the newline that ctime() puts. Though its not much of
a problem , it will be good if Log and Time are on the same line.
 
N

Nick Keighley

The program works fine, I am posting it here to get suggestions. All
kinds of suggestions and improvements are welcomed :)

nothing major just some thoughts

void write_log(int log_flag, const char* ptr, ...)
{
  char arrc[20] = {0};
  time_t t = time(NULL);
  va_list ap;
  va_start(ap, ptr);

  if((PRINT_NIL != log_flag) &&
     (DESIRED_LOG_FLAG >= log_flag))
    {
      get_log_level(log_flag, arrc);
      printf("[%s], [%s]: ", ctime(&t), arrc);
      vprintf(ptr,ap);
      printf("\n");
    }

  va_end(ap);

}

void get_log_level(int f, char* c)
{
  switch(f)
    {
    case PRINT_EXTRA:
      strcpy(c, "PRINT_EXTRA");
      break;      
    case PRINT_VAR:
      strcpy(c, "PRINT_VAR");
      break;
    }

I'm always a little nervous of case statements without defaults.
So I'd probably trap for this "can-never-happen-error". You could
avoid the
strcpy() by returning a pointer.

I like to avoid unnecessary forward declarations by ordering
functions so they aren't used until after they are defined (subjected
to Pascal at an early age).

char *get_log_level (int f)
{
switch (f)
{
case PRINT_EXTRA:
return "PRINT_EXTRA";
case PRINT_VAR:
return "PRINT_VAR";
default:
assert (BAD_LOG_LEVEL);
}
}

void write_log(int log_flag, const char* ptr, ...)
{
time_t t = time(NULL);
va_list ap;
va_start(ap, ptr);

if((PRINT_NIL != log_flag) &&
(DESIRED_LOG_FLAG >= log_flag))
{
printf("[%s], [%s]: ", ctime(&t), get_log_level(log_flag));
vprintf(ptr,ap);
printf("\n");
}

va_end(ap);
}

The only problem is the newline that ctime() puts. Though its not much of
a problem , it will be good if Log and Time are on the same line.

this is one of the reasons I don't use ctime() (The "American" style
format
isn't to my liking either). Take a look a strftime() or even roll your
own
with sprintf() and localtime().
 
A

arnuld

this is one of the reasons I don't use ctime() (The "American" style
format isn't to my liking either). Take a look a strftime() or even roll
your own with sprintf() and localtime().

I wrote a wrapper function to print the ctime() without newline. Tell
me what you think:

void print_time(void)
{
char arrt[100] = {0};
char* p = arrt;
time_t t = time(NULL);

strcpy(arrt, ctime(&t));
while(*p != '\n') printf("%c", *p++);
}
 
Ad

Advertisements

K

Keith Thompson

arnuld said:
On Aug 3, 4:49 pm, Nick Keighley <[email protected]> wrote:
this is one of the reasons I don't use ctime() (The "American" style
format isn't to my liking either). Take a look a strftime() or even roll
your own with sprintf() and localtime().

I wrote a wrapper function to print the ctime() without newline. Tell
me what you think:

void print_time(void)
{
char arrt[100] = {0};
char* p = arrt;
time_t t = time(NULL);

strcpy(arrt, ctime(&t));
while(*p != '\n') printf("%c", *p++);
}

You don't really need to copy the result of ctime() to an array -- and
even if you did, the array doesn't need to be 100 characters long.

Take a look at this:

char *ctime_without_newline(time_t *timep)
{
char *result = ctime(timep);
if (result != NULL) {
#ifdef QUICK_AND_DIRTY
result[24] = '\0';
#else
*(strchr(result, '\n')) = '\0';
#endif
}
return result;
}
 
A

arnuld

You don't really need to copy the result of ctime() to an array -- and
even if you did, the array doesn't need to be 100 characters long.
Take a look at this:

char *ctime_without_newline(time_t *timep)
{
    char *result = ctime(timep);
    if (result != NULL) {
#ifdef QUICK_AND_DIRTY
        result[24] = '\0';
#else
        *(strchr(result, '\n')) = '\0';

strchr() returns NULL if character is not found, hence *NULL ==
Segfault. I mean, I know '\n' will always be there in ctime() and we
do check for inconsistencies all the time. So is this case an
exception ?
 
A

arnuld

char *ctime_without_newline(time_t *timep)
{
    char *result = ctime(timep);
    if (result != NULL) {
#ifdef QUICK_AND_DIRTY
        result[24] = '\0';
#else
        *(strchr(result, '\n')) = '\0';
#endif
    }
    return result;


You are returning a local string literal. Its memory is freed by the
compiler as soon as the function exits but then why it works ?
 
A

arnuld

strchr() returns NULL if character is not found, hence *NULL ==
Segfault. I mean, I know '\n' will always be there in ctime() and we
do check for inconsistencies all the time. So is this case an
exception ?

My bad.. you did check for NULL.. I should stop using Google Groups.
 
N

Nick Keighley

char *ctime_without_newline(time_t *timep)
{
    char *result = ctime(timep);
    if (result != NULL) {
#ifdef QUICK_AND_DIRTY
        result[24] = '\0';
#else
        *(strchr(result, '\n')) = '\0';
#endif
    }
    return result;

You are returning a local string literal.

no he isn't. he's returning a *pointer* to a static string (the
one returned by ctime()).
 
Ad

Advertisements

N

Nick Keighley

I wrote a wrapper function to print the ctime() without newline. Tell
me what you think:
void print_time(void)
{
  char arrt[100] = {0};
  char* p = arrt;
  time_t t = time(NULL);
  strcpy(arrt, ctime(&t));
  while(*p != '\n')  printf("%c", *p++);
}

You don't really need to copy the result of ctime() to an array -- and
even if you did, the array doesn't need to be 100 characters long.

Take a look at this:

char *ctime_without_newline(time_t *timep)
{
    char *result = ctime(timep);
    if (result != NULL) {
#ifdef QUICK_AND_DIRTY
        result[24] = '\0';
#else
        *(strchr(result, '\n')) = '\0';
#endif
    }
    return result;

}

is the string returned by ctime(0 guaranteed to be modifiable?
I know 8 out of 10 implementations just return a pointer to a
static buffer but does the DS9K?
 
N

Nick Keighley

this is one of the reasons I don't use ctime() (The "American" style
format isn't to my liking either). Take a look a strftime() or even roll
your own with sprintf() and localtime().

I wrote a wrapper function to print the ctime() without newline. Tell
me what you think:

void print_time(void)
{
  char arrt[100] = {0};
  char* p = arrt;
  time_t t = time(NULL);

  strcpy(arrt, ctime(&t));
  while(*p != '\n')  printf("%c", *p++);
}

same as i thought before. I don't like the format of the string
returned by ctime() so I'd write a function that did what i wanted
in the first place! But your code looks ok.
 
J

James Kuyper

Nick Keighley wrote:
....
is the string returned by ctime(0 guaranteed to be modifiable?
I know 8 out of 10 implementations just return a pointer to a
static buffer but does the DS9K?

The ctime() function is defined as being equivalent to

asctime(localtime(timer))

(7.23.3.2p2). The asctime() function's required behavior is defined
entirely in terms of a reference implementation (7.23.3.1p2). This was a
serious mistake, in my opinion, because it makes it hard to be sure
which features of the reference implementation were meant to be
normative, and which are merely to be taken as examples. Also, if
there's a change in some other part of the standard which changes the
meaning of the reference implementation's code, there's a significant
chance that the committee might forget to modify the reference
implementation's code accordingly.

That reference implementation creates the string in an array declared as

static char result[26];

and returns a char* pointer to the start of that buffer - since the
string pointed at by the value returned by the reference implementation
would be modifiable, the value returned by any actual implementation
must also be modifiable.
 
J

James Kuyper

arnuld said:
On Aug 3, 4:49 pm, Nick Keighley <[email protected]> wrote:
this is one of the reasons I don't use ctime() (The "American" style
format isn't to my liking either). Take a look a strftime() or even roll
your own with sprintf() and localtime().

I wrote a wrapper function to print the ctime() without newline. Tell
me what you think:

void print_time(void)
{
char arrt[100] = {0};
char* p = arrt;
time_t t = time(NULL);

strcpy(arrt, ctime(&t));
while(*p != '\n') printf("%c", *p++);
}

If I wanted the current time in the format specified for asctime(), I
would prefer to use strftime() with a format string of "%a %b %e %T %Y".
Unlike asctime(), this produces locale-specific results for the day and
month abbreviations. I would consider that an advantage for many
purposes, but if that is not desired, switch to the "C" locale before
calling strftime().

However, asctime() format is seldom what I'd want; for most purposes I
prefer ISO 8601 format; in fullest form, equivalent to the strftime
format "%FT%T%Z", though I'd usually replace the first "T" with a blank,
and not worry about the "%Z" for local times.
 
N

Nick Keighley

My bad.. you did check for NULL.. I should stop using Google Groups.

the intense bogon flux from Google Groups' poor design begins to
destroy brain tissue after a time
 
Ad

Advertisements

N

Nick Keighley

However, asctime() format is seldom what I'd want; for most purposes I
prefer ISO 8601 format; in fullest form, equivalent to the strftime
format "%FT%T%Z", though I'd usually replace the first "T" with a blank,
and not worry about the "%Z" for local times

sadly though %F is a C99 feature
 
K

Keith Thompson

arnuld said:
You don't really need to copy the result of ctime() to an array -- and
even if you did, the array doesn't need to be 100 characters long.
Take a look at this:

char *ctime_without_newline(time_t *timep)
{
    char *result = ctime(timep);
    if (result != NULL) {
#ifdef QUICK_AND_DIRTY
        result[24] = '\0';
#else
        *(strchr(result, '\n')) = '\0';

strchr() returns NULL if character is not found, hence *NULL ==
Segfault. I mean, I know '\n' will always be there in ctime() and we
do check for inconsistencies all the time. So is this case an
exception ?

As you say in a later followup, I do check whether ctime returned
a null pointer. But I don't check whether strchr returned a
null pointer. It could do so only if ctime returns a pointer
to a string, but that string doesn't contain a '\n' character.
I don't believe that behavior is permitted by the standard.

On a non-conforming implementation, if *timep represents, for
example, a time in the distant past or future, before the year
-999 or after the year 9999, then that time can't be correctly
represented in the required format. ctime *should* detect an error
and return a null pointer in that case, but I wouldn't be surprised
if some implementations get this wrong, and one way of getting it
wrong would be to drop the '\n'. (For many implementations this
error can't happen, since time_t can't represent times outside a
relatively narrow range, typically from 1901 to 2038.)

But there's only so much you can or should do to cater to buggy
implementations.

Still, I wouldn't be entirely comfortable using the code I posted.
(I said to take a look at it, not necessarily to use it. :cool:})
It should work correctly on any conforming implementation, but
somehow it feels "brittle". And I rarely use ctime() or asctime()
anyway, since I dislike the output format; I use ISO 8601 format
(YYYY-MM-DD HH:MM:SS) whenever possible.
 
Ad

Advertisements

K

Keith Thompson

Keith Thompson said:
On a non-conforming implementation, if *timep represents, for
example, a time in the distant past or future, before the year
-999 or after the year 9999, then that time can't be correctly
represented in the required format. ctime *should* detect an error
and return a null pointer in that case, but I wouldn't be surprised
if some implementations get this wrong, and one way of getting it
wrong would be to drop the '\n'. (For many implementations this
error can't happen, since time_t can't represent times outside a
relatively narrow range, typically from 1901 to 2038.)
[...]

Editiing error: Delete the phrase "On a non-conforming implementation, ".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top