variable argument lists

  • Thread starter deathwillendthiswar
  • Start date
D

deathwillendthiswar

Hi,

I'm not too keen on variable argument lists, but I'd like to use them
for some logging functions.
Suppose the prototype for logging functions is

void Log( const String& sMessage, const int iLevel, const bool
bUseLineEnd );

String already takes variable argument lists in it's sPrintf function,
which also returns a reference to this, so I could use

Log( String().sPrintf( "test %d", 5 ), level, true ); for example.

That's not really a problem, but what happens: String is constructed,
then allocates memory, then calls sprintf and returns a reference to
itself. Internally, Log copies the memory from String into pre-
allocated space. The call returns, and String and it's memory are
deallocated.
What I would rather like to see, is that the sprintf call happens
directly on the pre-allocated memory in Log. That saves allocating,
memcpy and deallocation. However, I would also like to keep the same
prototype, so that I can use Log( "test %d", 5, level, true );

Variable arguments can't be listed first, so my first thought was to
simply declare all possible functions:

template< class T0 >
void Log( const char* Format, const T0 arg0, const int iLevel, const
bool bLineEnd );
template< class T0, class T1 >
void Log( const char* Format, const T0 arg0, const T1 arg1, const int
iLevel, const bool bLineEnd );

and so on.
Implementating this was easy at first sight: let the Logger class have
a method that accepts a va_list and construct the list from the
arguments.
template< class T0 >
void Log( const char* Format, const T0 arg0, const int iLevel, const
bool bLineEnd )
{
va_list args;
va_start( args, Format );
InternalLogFunction( Format, args, iLevel, bLineEnd );
va_end( args );
}

This does excatly what I want, though it's not overly pretty, but gcc
(4.1.2) chokes on this saying "va_start used in function with fixed
args".
I don't really understand why it is not allowed (afaik va_start just
lets args point to the next element on the stack, arg0 in this case),
can someone explain this?
Also, what would be other ways to achieve this? What I basically want
is a variable argument list of some kind, followed by 2 named
parameters. (ok I could just use Log( const int, const bool, const
char*, ... ) but it would just make writing much easier if it behaved
like the non-printf Log)

Thanks!
 
L

Larry Evans

On 11/26/08 10:06, (e-mail address removed) wrote:
[snip]
Variable arguments can't be listed first, so my first thought was to
simply declare all possible functions:

template< class T0 >
void Log( const char* Format, const T0 arg0, const int iLevel, const
bool bLineEnd );
template< class T0, class T1 >
void Log( const char* Format, const T0 arg0, const T1 arg1, const int
iLevel, const bool bLineEnd );

and so on.
Implementating this was easy at first sight: let the Logger class have
a method that accepts a va_list and construct the list from the
arguments.
template< class T0 >
void Log( const char* Format, const T0 arg0, const int iLevel, const
bool bLineEnd )
{
va_list args;
va_start( args, Format );
InternalLogFunction( Format, args, iLevel, bLineEnd );
va_end( args );
}

This does excatly what I want, though it's not overly pretty, but gcc
(4.1.2) chokes on this saying "va_start used in function with fixed
args".
[snip]

Section 3.2 of:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1704.pdf

shows printf implementation using variadic templates. Maybe you
could emulate that if using gcc and variadic templates is acceptable
to you.
 
G

gyakoo

Another form is to overload the single comma operator (,) of an
object. Yes, it is a strange form.
If the comma operator returns a reference to the object itself, you
could be something like:

(this code is neither tested nor compiled (I'm writing it on the fly),
and it isn't efficient, in fact it is only a wrapper syntax to pass
directly a vector to a function, but I think that it is interesting as
alternative, at least you could use the main idea behind comma
operator overload.)

// Overloading comma operator.
// ---------------------------
class object
{
public:
object( int val ){ values.push_back( val ); }

object& operator ,( const object& obj )
{
std::copy( obj.values.begin(), obj.values.end(),
std::back_inserter( values ) );
return *this;
}
object& operator ,( int v )
{
values.push_back( v );
return *this;
}
protected:
std::vector< int > values;
};

void func( const object& obj )
{
}

// using it
func ( object(5), 3, 4, 5 );



On 11/26/08 10:06, (e-mail address removed) wrote:
[snip]
Variable arguments can't be listed first, so my first thought was to
simply declare all possible functions:
template< class T0 >
void Log( const char* Format, const T0 arg0, const int iLevel, const
bool bLineEnd );
template< class T0, class T1 >
void Log( const char* Format, const T0 arg0, const T1 arg1, const int
iLevel, const bool bLineEnd );
and so on.
Implementating this was easy at first sight: let the Logger class have
a method that accepts a va_list and construct the list from the
arguments.
template< class T0 >
void Log( const char* Format, const T0 arg0, const int iLevel, const
bool bLineEnd )
{
va_list args;
va_start( args, Format );
InternalLogFunction( Format, args, iLevel, bLineEnd );
va_end( args );
}
This does excatly what I want, though it's not overly pretty, but gcc
(4.1.2) chokes on this saying "va_start used in function with fixed
args".

[snip]

Section 3.2 of:

   http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1704.pdf

shows printf implementation using variadic templates.  Maybe you
could emulate that if using gcc and variadic templates is acceptable
to you.
 
P

peter koch

Hi,

I'm not too keen on variable argument lists, but I'd like to use them
for some logging functions.
Suppose the prototype for logging functions is

void Log( const String& sMessage, const int iLevel, const bool
bUseLineEnd );

String already takes variable argument lists in it's sPrintf function,
which also returns a reference to this, so I could use

Log( String().sPrintf( "test %d", 5 ), level, true ); for example.

That's not really a problem, but what happens: String is constructed,
then allocates memory, then calls sprintf and returns a reference to
itself. Internally, Log copies the memory from String into pre-
allocated space. The call returns, and String and it's memory are
deallocated.

Why not just imitate the stream iterators found in the standard
library? You could do that very easily by using a std::stringstream
and then - if performance requires it - replace it with your own
"stream", that goes directly to the underlying buffer without any
additional use of dynamic memory.

/Peter
 
D

deathwillendthiswar

[ hmm, seems I misunderstood "Reply To Author", sorry for that, here's
what I wanted to say in short: ]

Nice tip about overloading comma operator, didn't even know that is
was defined.

I already got a similair solution to stream iterators from a collegue.
You mean something like
void Log( const char* Format, const T0 arg0, const T1 arg1, const int
iLevel, const bool bLineEnd )
{
InternalLogStream( iLevel, bLineEnd, Format ) << arg0 << arg1;
Flush();
}

right?
I like the templates: there's no way you can write a call with an
argument that doesn't support conversion as the compiler would spot it
immedeately so it's safer than normal printf. But wouldn't it require
me to rewrite/reinvent the printf machanism? Ever call to << would
have to move to the next '%', then do the appropriate conversion to
the internal string. That's exactly what printf() does.
nonetheless, interesting solution.

I came up with something myself, it's more of a quick hack but works
fine:
template< class T0, class T1 >
void Log( const char* fmt, const T0 arg0, const T1 arg1, const int
iLevel, const bool bLineEnd )
{
InternalString.Printf( fmt, arg0, arg1 );
Log( InternalString, iLevel, bLineEnd );
}

Thanks!
 

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

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top