Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Problem with printf-like logging framework and inline hexdumps.

Reply
Thread Tools

Problem with printf-like logging framework and inline hexdumps.

 
 
Jef Driesen
Guest
Posts: n/a
 
      09-13-2012
Hi,

For one of my projects, I created a simple logging framework. I have an
(opaque) context object which contains a buffer:

typedef struct dc_context_t {
char msg[4096];
/* More stuff here. */
} dc_context_t;

and a printf-like function to log a message. The logging function uses
the vsnprintf() function to write a string representation of the
arguments to the context buffer. The resulting string is then passed to
a user-defined callback function.

int
dc_context_log (dc_context_t *context, const char *format, ...)
{
va_list ap;

va_start (ap, format);
vsnprintf (context->msg, sizeof (context->msg), format, ap);
va_end (ap);

callback (context->msg);
}

This works perfect for all printf-style logging. For example:

dc_context_log (context, "%s (%d)", "An error message", 3);

However, I would like to log some binary data too. Let's say I want to
log a message with an inline hexdump, something like this:

"DEBUG: data=<DATA>, size=<SIZE>"

Since there is no printf format string available for hexdumps, I could
write a little dc_context_hexdump() helper function to convert the
binary data into a hex string, and then produce the log message in 3
steps:

dc_context_log (context, "%s: data=", "DEBUG");
dc_context_hexdump (context, hexdata, size);
dc_context_log (context, "", size);

This would work fine, except that I need the output in only one piece.
There are several reasons for that. I simplified the code a bit for this
post, but in reality the dc_context_log() function also receives extra
info, like the __FILE__ and __LINE__ macros. When printing the output, I
only want to see the file and line info appear only once, and not three
times. Also in a multithreaded environment, another thread may call the
logging function between each of the three calls, and thus interleaving
the output.

How would you workaround this?

A possible approach might be to convert the binary data to string
first, and then pass that to the dc_context_log call. Something like
this:

void
somefunction (dc_context_t *context, unsigned char *data, unsigned int
size)
{
/* Do something useful here. */

/* Log the binary data. */
hexstr = hexdump (data, size);
dc_context_log (context, "%s: data=%s, size=%u",
"DEBUG", hexstr, size);
}

But this hexdump function isn't thread-safe (because it would have to
return a pointer to a static buffer). And even if I would make a
re-entrant hexdump_r() version, it's not ideal because then I would have
to allocate a buffer inside the somefunction(). I don't want to do that
because the dc_context_log is actually called through a macro, which can
be configured at compile time to omit the actual call (e.g. in
production builds). But then the memory allocation would still be there,
and I don't want to pollute application code with #ifdef's. An temporary
buffer on the stack won't work either, because the size isn't fixed.

Suggestions are welcome!

Jef

 
Reply With Quote
 
 
 
 
Heinrich Wolf
Guest
Posts: n/a
 
      09-13-2012

"Jef Driesen" <(E-Mail Removed)> schrieb im Newsbeitrag
news:k2t0k8$cjf$(E-Mail Removed)...
....
> But this hexdump function isn't thread-safe (because it would have to
> return a pointer to a static buffer). And even if I would make a
> re-entrant hexdump_r() version, it's not ideal because then I would have
> to allocate a buffer inside the somefunction(). I don't want to do that
> because the dc_context_log is actually called through a macro, which can
> be configured at compile time to omit the actual call (e.g. in
> production builds). But then the memory allocation would still be there,
> and I don't want to pollute application code with #ifdef's. An temporary
> buffer on the stack won't work either, because the size isn't fixed.
>
> Suggestions are welcome!
>
> Jef


Maybe you supply the buffer for the hex dump locally from a the caller of
the logging.

Heiner

 
Reply With Quote
 
 
 
 
Barry Schwarz
Guest
Posts: n/a
 
      09-13-2012
On Thu, 13 Sep 2012 18:11:55 +0200, Jef Driesen
<(E-Mail Removed)> wrote:

>Hi,
>
>For one of my projects, I created a simple logging framework. I have an
>(opaque) context object which contains a buffer:
>
>typedef struct dc_context_t {
> char msg[4096];
> /* More stuff here. */
>} dc_context_t;
>
>and a printf-like function to log a message. The logging function uses
>the vsnprintf() function to write a string representation of the
>arguments to the context buffer. The resulting string is then passed to
>a user-defined callback function.
>
>int
>dc_context_log (dc_context_t *context, const char *format, ...)
>{
> va_list ap;
>
> va_start (ap, format);
> vsnprintf (context->msg, sizeof (context->msg), format, ap);
> va_end (ap);
>
> callback (context->msg);
>}
>
>This works perfect for all printf-style logging. For example:
>
> dc_context_log (context, "%s (%d)", "An error message", 3);
>
>However, I would like to log some binary data too. Let's say I want to
>log a message with an inline hexdump, something like this:
>
> "DEBUG: data=<DATA>, size=<SIZE>"
>
>Since there is no printf format string available for hexdumps, I could
>write a little dc_context_hexdump() helper function to convert the
>binary data into a hex string, and then produce the log message in 3
>steps:
>
> dc_context_log (context, "%s: data=", "DEBUG");
> dc_context_hexdump (context, hexdata, size);
> dc_context_log (context, "", size);
>
>This would work fine, except that I need the output in only one piece.
>There are several reasons for that. I simplified the code a bit for this
>post, but in reality the dc_context_log() function also receives extra
>info, like the __FILE__ and __LINE__ macros. When printing the output, I
>only want to see the file and line info appear only once, and not three
>times. Also in a multithreaded environment, another thread may call the
>logging function between each of the three calls, and thus interleaving
>the output.
>
>How would you workaround this?
>
>A possible approach might be to convert the binary data to string
>first, and then pass that to the dc_context_log call. Something like
>this:
>
>void
>somefunction (dc_context_t *context, unsigned char *data, unsigned int
>size)
>{
> /* Do something useful here. */
>
> /* Log the binary data. */
> hexstr = hexdump (data, size);
> dc_context_log (context, "%s: data=%s, size=%u",
> "DEBUG", hexstr, size);
>}
>
>But this hexdump function isn't thread-safe (because it would have to
>return a pointer to a static buffer). And even if I would make a
>re-entrant hexdump_r() version, it's not ideal because then I would have
>to allocate a buffer inside the somefunction(). I don't want to do that
>because the dc_context_log is actually called through a macro, which can
>be configured at compile time to omit the actual call (e.g. in
>production builds). But then the memory allocation would still be there,
>and I don't want to pollute application code with #ifdef's. An temporary
>buffer on the stack won't work either, because the size isn't fixed.


You need a buffer that will survive after hexdump returns. I can
think of only three ways to achieve this: static, dynamic, and
provided by somefunction.

If you need hexdump to be thread safe, then static is probably not the
answer.

If you choose to allocate the buffer dynamically, it need not be done
in somefunction. It could be done in hexdump. Either way though, it
would need to be freed in somefunction after dc_context_log returns.
You could insure that hexdump is called only when dc_context_log is
about to be called by moving the assignment of hexstr into the
argument list of the call to dc_context_log. That is, replace
hexstr = hexdump (data, size);
dc_context_log (context, "%s: data=%s, size=%u",
"DEBUG", hexstr, size);
with
dc_context_log (context, "%s: data=%s, size=%u",
"DEBUG", hexstr = hexdump (data, size), size);
In this way, when your macro suppresses the call to dc_context_log it
also suppresses the call to hexdump and no allocation is performed. If
you initialize hexstr to NULL, then you can call free() regardless of
whether an allocation has been performed or not and your code is not
polluted.

If you choose to define the buffer as an automatic array in
somefunction, it need not be any bigger than the array msg in your
structure. You may not know how much of it you will use but you do
know it cannot be any bigger or your call to vsnprintf will overrun
members in the structure.

--
Remove del for email
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      09-13-2012
Jef Driesen <(E-Mail Removed)> writes:
<snip>
> However, I would like to log some binary data too. Let's say I want to
> log a message with an inline hexdump, something like this:
>
> "DEBUG: data=<DATA>, size=<SIZE>"
>
> Since there is no printf format string available for hexdumps, I could
> write a little dc_context_hexdump() helper function to convert the
> binary data into a hex string, and then produce the log message in 3
> steps:
>
> dc_context_log (context, "%s: data=", "DEBUG");
> dc_context_hexdump (context, hexdata, size);
> dc_context_log (context, "", size);
>
> This would work fine, except that I need the output in only one piece.
> There are several reasons for that. I simplified the code a bit for this
> post, but in reality the dc_context_log() function also receives extra
> info, like the __FILE__ and __LINE__ macros. When printing the output, I
> only want to see the file and line info appear only once, and not three
> times. Also in a multithreaded environment, another thread may call the
> logging function between each of the three calls, and thus interleaving
> the output.
>
> How would you workaround this?
>
> A possible approach might be to convert the binary data to string
> first, and then pass that to the dc_context_log call. Something like
> this:
>
> void
> somefunction (dc_context_t *context, unsigned char *data, unsigned int
> size)
> {
> /* Do something useful here. */
>
> /* Log the binary data. */
> hexstr = hexdump (data, size);
> dc_context_log (context, "%s: data=%s, size=%u",
> "DEBUG", hexstr, size);
> }
>
> But this hexdump function isn't thread-safe (because it would have to
> return a pointer to a static buffer). And even if I would make a
> re-entrant hexdump_r() version, it's not ideal because then I would have
> to allocate a buffer inside the somefunction(). I don't want to do that
> because the dc_context_log is actually called through a macro, which can
> be configured at compile time to omit the actual call (e.g. in
> production builds). But then the memory allocation would still be there,
> and I don't want to pollute application code with #ifdef's. An temporary
> buffer on the stack won't work either, because the size isn't fixed.
>
> Suggestions are welcome!


GCC's printf (well, glibs's printf in fact) allows you to define new
format specifiers. Maybe that would suit?

Rolling your own format processing is not that hard. I'd process the
format in chunks that bracket your own format codes. Here's an
example:

void logit(const char *fmt, ...)
{
va_list al;
va_start(al, fmt);
const char *fp;
while ((fp = strstr(fmt, "%H")) != NULL) {
char tmp[fp - fmt + 1];
memcpy(tmp, fmt, sizeof tmp - 1);
tmp[sizeof tmp - 1] = 0;
vprintf(tmp, al);

printf("<%d>", va_arg(al, int));

fmt = fp + 2;
}
vprintf(fmt, al);
va_end(al);
}

Here, a format of %H is treated as if it meant "<%d>" but you can do
whatever you like at that point (in fact, my use-case called a function
keyed from the the format letter).

Obviously you'll need a cleverer function to match a custom format. %H
should not match after a %, for example, and your formats are likely to
have counts in them.

I don't think it even breaks any of the rules about handling va_lists.

--
Ben.
 
Reply With Quote
 
Jef Driesen
Guest
Posts: n/a
 
      09-13-2012
On 13-09-12 22:24, Ben Bacarisse wrote:
> Jef Driesen <(E-Mail Removed)> writes:
> <snip>
>> However, I would like to log some binary data too. Let's say I want to
>> log a message with an inline hexdump, something like this:
>>
>> "DEBUG: data=<DATA>, size=<SIZE>"
>>
>> Since there is no printf format string available for hexdumps, I could
>> write a little dc_context_hexdump() helper function to convert the
>> binary data into a hex string, and then produce the log message in 3
>> steps:
>>
>> dc_context_log (context, "%s: data=", "DEBUG");
>> dc_context_hexdump (context, hexdata, size);
>> dc_context_log (context, "", size);
>>
>> This would work fine, except that I need the output in only one piece.
>> There are several reasons for that. I simplified the code a bit for this
>> post, but in reality the dc_context_log() function also receives extra
>> info, like the __FILE__ and __LINE__ macros. When printing the output, I
>> only want to see the file and line info appear only once, and not three
>> times. Also in a multithreaded environment, another thread may call the
>> logging function between each of the three calls, and thus interleaving
>> the output.
>>
>> How would you workaround this?
>>
>> A possible approach might be to convert the binary data to string
>> first, and then pass that to the dc_context_log call. Something like
>> this:
>>
>> void
>> somefunction (dc_context_t *context, unsigned char *data, unsigned int
>> size)
>> {
>> /* Do something useful here. */
>>
>> /* Log the binary data. */
>> hexstr = hexdump (data, size);
>> dc_context_log (context, "%s: data=%s, size=%u",
>> "DEBUG", hexstr, size);
>> }
>>
>> But this hexdump function isn't thread-safe (because it would have to
>> return a pointer to a static buffer). And even if I would make a
>> re-entrant hexdump_r() version, it's not ideal because then I would have
>> to allocate a buffer inside the somefunction(). I don't want to do that
>> because the dc_context_log is actually called through a macro, which can
>> be configured at compile time to omit the actual call (e.g. in
>> production builds). But then the memory allocation would still be there,
>> and I don't want to pollute application code with #ifdef's. An temporary
>> buffer on the stack won't work either, because the size isn't fixed.
>>
>> Suggestions are welcome!

>
> GCC's printf (well, glibs's printf in fact) allows you to define new
> format specifiers. Maybe that would suit?


No, because I also need to support non glibc systems (Windows).

> Rolling your own format processing is not that hard. I'd process the
> format in chunks that bracket your own format codes. Here's an
> example:
>
> void logit(const char *fmt, ...)
> {
> va_list al;
> va_start(al, fmt);
> const char *fp;
> while ((fp = strstr(fmt, "%H")) != NULL) {
> char tmp[fp - fmt + 1];
> memcpy(tmp, fmt, sizeof tmp - 1);
> tmp[sizeof tmp - 1] = 0;
> vprintf(tmp, al);
>
> printf("<%d>", va_arg(al, int));
>
> fmt = fp + 2;
> }
> vprintf(fmt, al);
> va_end(al);
> }
>
> Here, a format of %H is treated as if it meant "<%d>" but you can do
> whatever you like at that point (in fact, my use-case called a function
> keyed from the the format letter).
>
> Obviously you'll need a cleverer function to match a custom format. %H
> should not match after a %, for example, and your formats are likely to
> have counts in them.
>
> I don't think it even breaks any of the rules about handling va_lists.


I also thought of something like this, but the manpages for the vprintf
functions says this on my linux system:

These functions do not call the va_end macro. Because they invoke the va_arg
macro, the value of ap is undefined after the call. See stdarg(3).

Jef

 
Reply With Quote
 
Jef Driesen
Guest
Posts: n/a
 
      09-13-2012
On 13-09-12 20:26, Barry Schwarz wrote:
> On Thu, 13 Sep 2012 18:11:55 +0200, Jef Driesen
> <(E-Mail Removed)> wrote:
>
>> Hi,
>>
>> For one of my projects, I created a simple logging framework. I have an
>> (opaque) context object which contains a buffer:
>>
>> typedef struct dc_context_t {
>> char msg[4096];
>> /* More stuff here. */
>> } dc_context_t;
>>
>> and a printf-like function to log a message. The logging function uses
>> the vsnprintf() function to write a string representation of the
>> arguments to the context buffer. The resulting string is then passed to
>> a user-defined callback function.
>>
>> int
>> dc_context_log (dc_context_t *context, const char *format, ...)
>> {
>> va_list ap;
>>
>> va_start (ap, format);
>> vsnprintf (context->msg, sizeof (context->msg), format, ap);
>> va_end (ap);
>>
>> callback (context->msg);
>> }
>>
>> This works perfect for all printf-style logging. For example:
>>
>> dc_context_log (context, "%s (%d)", "An error message", 3);
>>
>> However, I would like to log some binary data too. Let's say I want to
>> log a message with an inline hexdump, something like this:
>>
>> "DEBUG: data=<DATA>, size=<SIZE>"
>>
>> Since there is no printf format string available for hexdumps, I could
>> write a little dc_context_hexdump() helper function to convert the
>> binary data into a hex string, and then produce the log message in 3
>> steps:
>>
>> dc_context_log (context, "%s: data=", "DEBUG");
>> dc_context_hexdump (context, hexdata, size);
>> dc_context_log (context, "", size);
>>
>> This would work fine, except that I need the output in only one piece.
>> There are several reasons for that. I simplified the code a bit for this
>> post, but in reality the dc_context_log() function also receives extra
>> info, like the __FILE__ and __LINE__ macros. When printing the output, I
>> only want to see the file and line info appear only once, and not three
>> times. Also in a multithreaded environment, another thread may call the
>> logging function between each of the three calls, and thus interleaving
>> the output.
>>
>> How would you workaround this?
>>
>> A possible approach might be to convert the binary data to string
>> first, and then pass that to the dc_context_log call. Something like
>> this:
>>
>> void
>> somefunction (dc_context_t *context, unsigned char *data, unsigned int
>> size)
>> {
>> /* Do something useful here. */
>>
>> /* Log the binary data. */
>> hexstr = hexdump (data, size);
>> dc_context_log (context, "%s: data=%s, size=%u",
>> "DEBUG", hexstr, size);
>> }
>>
>> But this hexdump function isn't thread-safe (because it would have to
>> return a pointer to a static buffer). And even if I would make a
>> re-entrant hexdump_r() version, it's not ideal because then I would have
>> to allocate a buffer inside the somefunction(). I don't want to do that
>> because the dc_context_log is actually called through a macro, which can
>> be configured at compile time to omit the actual call (e.g. in
>> production builds). But then the memory allocation would still be there,
>> and I don't want to pollute application code with #ifdef's. An temporary
>> buffer on the stack won't work either, because the size isn't fixed.

>
> You need a buffer that will survive after hexdump returns. I can
> think of only three ways to achieve this: static, dynamic, and
> provided by somefunction.
>
> If you need hexdump to be thread safe, then static is probably not the
> answer.
>
> If you choose to allocate the buffer dynamically, it need not be done
> in somefunction. It could be done in hexdump. Either way though, it
> would need to be freed in somefunction after dc_context_log returns.
> You could insure that hexdump is called only when dc_context_log is
> about to be called by moving the assignment of hexstr into the
> argument list of the call to dc_context_log. That is, replace
> hexstr = hexdump (data, size);
> dc_context_log (context, "%s: data=%s, size=%u",
> "DEBUG", hexstr, size);
> with
> dc_context_log (context, "%s: data=%s, size=%u",
> "DEBUG", hexstr = hexdump (data, size), size);
> In this way, when your macro suppresses the call to dc_context_log it
> also suppresses the call to hexdump and no allocation is performed. If
> you initialize hexstr to NULL, then you can call free() regardless of
> whether an allocation has been performed or not and your code is not
> polluted.


One of the reasons to have the msg array embedded into the context structure is
to avoid any memory allocations in the logging functions. The necessary memory
is already pre-allocated with the context structure. With this approach the
memory allocation would be re-introduced. But it's still an interesting idea of
course.

> If you choose to define the buffer as an automatic array in
> somefunction, it need not be any bigger than the array msg in your
> structure. You may not know how much of it you will use but you do
> know it cannot be any bigger or your call to vsnprintf will overrun
> members in the structure.


True, but the size of the msg array in the context structure isn't public. It's
an opaque data structure and thus the size can be changed at will. And then the
code would be out of sync again.

Jef
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      09-13-2012
Jef Driesen <(E-Mail Removed)> writes:

> On 13-09-12 22:24, Ben Bacarisse wrote:

<snip>
>> Rolling your own format processing is not that hard. I'd process the
>> format in chunks that bracket your own format codes. Here's an
>> example:
>>
>> void logit(const char *fmt, ...)
>> {
>> va_list al;
>> va_start(al, fmt);
>> const char *fp;
>> while ((fp = strstr(fmt, "%H")) != NULL) {
>> char tmp[fp - fmt + 1];
>> memcpy(tmp, fmt, sizeof tmp - 1);
>> tmp[sizeof tmp - 1] = 0;
>> vprintf(tmp, al);
>>
>> printf("<%d>", va_arg(al, int));
>>
>> fmt = fp + 2;
>> }
>> vprintf(fmt, al);
>> va_end(al);
>> }
>>
>> Here, a format of %H is treated as if it meant "<%d>" but you can do
>> whatever you like at that point (in fact, my use-case called a function
>> keyed from the the format letter).
>>
>> Obviously you'll need a cleverer function to match a custom format. %H
>> should not match after a %, for example, and your formats are likely to
>> have counts in them.
>>
>> I don't think it even breaks any of the rules about handling va_lists.

>
> I also thought of something like this, but the manpages for the vprintf
> functions says this on my linux system:
>
> These functions do not call the va_end macro. Because they invoke the va_arg
> macro, the value of ap is undefined after the call. See stdarg(3).


Ah, yes. The above is from some old code when things were a little
wilder...

I don't think all is lost. I think there is a fiddly dance you can do
with va_end and va_copy to get the job done:

void logit(const char *fmt, ...)
{
va_list al;
va_start(al, fmt);
const char *fp;
while ((fp = strstr(fmt, "%H")) != NULL) {
char tmp[fp - fmt + 1];
memcpy(tmp, fmt, sizeof tmp - 1);
tmp[sizeof tmp - 1] = 0;

va_list al_copy;
va_copy(al_copy, al);

vprintf(tmp, al_copy);

va_end(al_copy);
va_end(al);
va_copy(al, al_copy);

printf("<%d>", va_arg(al, int));
fmt = fp + 2;
}
vprintf(fmt, al);
va_end(al);
}

This passes a cursory reading of the va_* macro restrictions in the
standard but I'd welcome some more input...

--
Ben.
 
Reply With Quote
 
Nick Keighley
Guest
Posts: n/a
 
      09-14-2012
On Sep 13, 10:47*pm, Jef Driesen <(E-Mail Removed)>
wrote:
> On 13-09-12 22:24, Ben Bacarisse wrote:
>
>
>
>
>
> > Jef Driesen <(E-Mail Removed)> writes:
> > <snip>
> >> However, I would like to log some binary data too. Let's say I want to
> >> log a message with an inline hexdump, something like this:

>
> >> * * * "DEBUG: data=<DATA>, size=<SIZE>"

>
> >> Since there is no printf format string available for hexdumps, I could
> >> write a little dc_context_hexdump() helper function to convert the
> >> binary data into a hex string, and then produce the log message in 3
> >> steps:

>
> >> * * * dc_context_log (context, "%s: data=", "DEBUG");
> >> * * * dc_context_hexdump (context, hexdata, size);
> >> * * * dc_context_log (context, "", size);

>
> >> This would work fine, except that I need the output in only one piece.
> >> There are several reasons for that. I simplified the code a bit for this
> >> post, but in reality the dc_context_log() function also receives extra
> >> info, like the __FILE__ and __LINE__ macros. When printing the output,I
> >> only want to see the file and line info appear only once, and not three
> >> times. Also in a multithreaded environment, another thread may call the
> >> logging function between each of the three calls, and thus interleaving
> >> the output.

>
> >> How would you workaround this?

>
> >> A possible approach might be to convert the binary data to string
> >> first, and then pass that to the dc_context_log call. Something like
> >> this:

>
> >> void
> >> somefunction (dc_context_t *context, unsigned char *data, unsigned int
> >> size)
> >> {
> >> * * * /* Do something useful here. */

>
> >> * * * /* Log the binary data. */
> >> * * * hexstr = hexdump (data, size);
> >> * * * dc_context_log (context, "%s: data=%s, size=%u",
> >> * * * * * "DEBUG", hexstr, size);
> >> }

>
> >> But this hexdump function isn't thread-safe (because it would have to
> >> return a pointer to a static buffer). And even if I would make a
> >> re-entrant hexdump_r() version, it's not ideal because then I would have
> >> to allocate a buffer inside the somefunction(). I don't want to do that
> >> because the dc_context_log is actually called through a macro, which can
> >> be configured at compile time to omit the actual call (e.g. in
> >> production builds). But then the memory allocation would still be there,
> >> and I don't want to pollute application code with #ifdef's. An temporary
> >> buffer on the stack won't work either, because the size isn't fixed.

>
> >> Suggestions are welcome!

>
> > GCC's printf (well, glibs's printf in fact) allows you to define new
> > format specifiers. *Maybe that would suit?

>
> No, because I also need to support non glibc systems (Windows).
>
>
>
>
>
> > Rolling your own format processing is not that hard. *I'd process the
> > format in chunks that bracket your own format codes. *Here's an
> > example:

>
> > * *void logit(const char *fmt, ...)
> > * *{
> > * * * * va_list al;
> > * * * * va_start(al, fmt);
> > * * * * const char *fp;
> > * * * * while ((fp = strstr(fmt, "%H")) != NULL) {
> > * * * * * * *char tmp[fp - fmt + 1];
> > * * * * * * *memcpy(tmp, fmt, sizeof tmp - 1);
> > * * * * * * *tmp[sizeof tmp - 1] = 0;
> > * * * * * * *vprintf(tmp, al);

>
> > * * * * * * *printf("<%d>", va_arg(al, int));

>
> > * * * * * * *fmt = fp + 2;
> > * * * * }
> > * * * * vprintf(fmt, al);
> > * * * * va_end(al);
> > * *}

>
> > Here, a format of %H is treated as if it meant "<%d>" but you can do
> > whatever you like at that point (in fact, my use-case called a function
> > keyed from the the format letter).

>
> > Obviously you'll need a cleverer function to match a custom format. *%H
> > should not match after a %, for example, and your formats are likely to
> > have counts in them.

>
> > I don't think it even breaks any of the rules about handling va_lists.

>
> I also thought of something like this, but the manpages for the vprintf
> functions says this on my linux system:
>
> These functions do not call the va_end macro. *Because they invoke the va_arg
> macro, the value of ap is undefined after the call. See stdarg(3).



could you pase the format string yourself? If its %H do your special
stuff otherwise pass it on to *printf()?
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      09-14-2012
Nick Keighley <(E-Mail Removed)> writes:

> On Sep 13, 10:47*pm, Jef Driesen <(E-Mail Removed)>
> wrote:
>> On 13-09-12 22:24, Ben Bacarisse wrote:

<snip>
>> > Rolling your own format processing is not that hard. *I'd process the
>> > format in chunks that bracket your own format codes. *Here's an
>> > example:

>>
>> > * *void logit(const char *fmt, ...)
>> > * *{
>> > * * * * va_list al;
>> > * * * * va_start(al, fmt);
>> > * * * * const char *fp;
>> > * * * * while ((fp = strstr(fmt, "%H")) != NULL) {
>> > * * * * * * *char tmp[fp - fmt + 1];
>> > * * * * * * *memcpy(tmp, fmt, sizeof tmp - 1);
>> > * * * * * * *tmp[sizeof tmp - 1] = 0;
>> > * * * * * * *vprintf(tmp, al);

>>
>> > * * * * * * *printf("<%d>", va_arg(al, int));

>>
>> > * * * * * * *fmt = fp + 2;
>> > * * * * }
>> > * * * * vprintf(fmt, al);
>> > * * * * va_end(al);
>> > * *}

>>
>> > Here, a format of %H is treated as if it meant "<%d>" but you can do
>> > whatever you like at that point (in fact, my use-case called a function
>> > keyed from the the format letter).

>>
>> > Obviously you'll need a cleverer function to match a custom format. *%H
>> > should not match after a %, for example, and your formats are likely to
>> > have counts in them.

>>
>> > I don't think it even breaks any of the rules about handling va_lists.

>>
>> I also thought of something like this, but the manpages for the vprintf
>> functions says this on my linux system:
>>
>> These functions do not call the va_end macro. *Because they invoke the va_arg
>> macro, the value of ap is undefined after the call. See stdarg(3).

>
>
> could you pase the format string yourself? If its %H do your special
> stuff otherwise pass it on to *printf()?


That is essentially what my example does.

But you are proposing, I think, to pass to v*printf a format containing
only one conversion string at a time, rather than in chunks as my code
does. If so, that does not really solve the problem of the va_list
becoming indeterminate. If I've misunderstood, please post a code
sketch.

Either way, you have to juggle the lists using va_copy and whilst I
think passing one argument at a time to v*printf may make it slightly
simpler, the small extras needed to fix the above (code that I posted
more recently) pays off compared to having to parse every format.

--
Ben.
 
Reply With Quote
 
Jef Driesen
Guest
Posts: n/a
 
      09-14-2012
On 14-09-12 00:54, Ben Bacarisse wrote:
> Jef Driesen <(E-Mail Removed)> writes:
>
>> On 13-09-12 22:24, Ben Bacarisse wrote:

> <snip>
>>> Rolling your own format processing is not that hard. I'd process the
>>> format in chunks that bracket your own format codes. Here's an
>>> example:
>>>
>>> void logit(const char *fmt, ...)
>>> {
>>> va_list al;
>>> va_start(al, fmt);
>>> const char *fp;
>>> while ((fp = strstr(fmt, "%H")) != NULL) {
>>> char tmp[fp - fmt + 1];
>>> memcpy(tmp, fmt, sizeof tmp - 1);
>>> tmp[sizeof tmp - 1] = 0;
>>> vprintf(tmp, al);
>>>
>>> printf("<%d>", va_arg(al, int));
>>>
>>> fmt = fp + 2;
>>> }
>>> vprintf(fmt, al);
>>> va_end(al);
>>> }
>>>
>>> Here, a format of %H is treated as if it meant "<%d>" but you can do
>>> whatever you like at that point (in fact, my use-case called a function
>>> keyed from the the format letter).
>>>
>>> Obviously you'll need a cleverer function to match a custom format. %H
>>> should not match after a %, for example, and your formats are likely to
>>> have counts in them.
>>>
>>> I don't think it even breaks any of the rules about handling va_lists.

>>
>> I also thought of something like this, but the manpages for the vprintf
>> functions says this on my linux system:
>>
>> These functions do not call the va_end macro. Because they invoke the va_arg
>> macro, the value of ap is undefined after the call. See stdarg(3).

>
> Ah, yes. The above is from some old code when things were a little
> wilder...
>
> I don't think all is lost. I think there is a fiddly dance you can do
> with va_end and va_copy to get the job done:
>
> void logit(const char *fmt, ...)
> {
> va_list al;
> va_start(al, fmt);
> const char *fp;
> while ((fp = strstr(fmt, "%H")) != NULL) {
> char tmp[fp - fmt + 1];
> memcpy(tmp, fmt, sizeof tmp - 1);
> tmp[sizeof tmp - 1] = 0;
>
> va_list al_copy;
> va_copy(al_copy, al);
>
> vprintf(tmp, al_copy);
>
> va_end(al_copy);
> va_end(al);
> va_copy(al, al_copy);
>
> printf("<%d>", va_arg(al, int));
> fmt = fp + 2;
> }
> vprintf(fmt, al);
> va_end(al);
> }
>
> This passes a cursory reading of the va_* macro restrictions in the
> standard but I'd welcome some more input...


I think this version has the same problem as the original. After
calling the va_end macro, the va_list variable becomes undefined
according to the manpages on my linux system:

"After the call va_end(ap) the variable ap is undefined."

So I guess that if this version works, then the original would have
worked too.

Jef

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Multiple functions (one version being inline and other beingnon-inline) Rahul C++ 3 02-28-2008 03:28 PM
about extern inline and static inline Sean C++ 4 04-30-2006 03:18 PM
Tool which expands implicitly inline inline functions tthunder@gmx.de C++ 3 06-16-2005 12:54 AM
To inline or not to inline? Alvin C++ 7 05-06-2005 03:04 PM
inline or not to inline in C++ Abhi C++ 2 07-03-2003 12:07 AM



Advertisments