[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c++

variable argument lists

deathwillendthiswar

11/26/2008 4:06:00 PM

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!
5 Answers

Larry Evans

11/26/2008 5:27:00 PM

0

On 11/26/08 10:06, deathwillendthiswar@gmail.com 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...

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

gyakoo

11/26/2008 6:56:00 PM

0

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 Nov 26, 6:26 pm, Larry Evans <cppljev...@suddenlink.net> wrote:
> On 11/26/08 10:06, deathwillendthis...@gmail.com 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...
>
> shows printf implementation using variadic templates.  Maybe you
> could emulate that if using gcc and variadic templates is acceptable
> to you.

peter koch

11/26/2008 7:19:00 PM

0

On 26 Nov., 17:06, deathwillendthis...@gmail.com wrote:
> 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

deathwillendthiswar

11/27/2008 9:06:00 AM

0

[ 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!

Hendrik Schober

11/27/2008 1:29:00 PM

0

deathwillendthiswar@gmail.com wrote:
> [ 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?

No, not at all.
Streams prove that it is possible to create an IO lib that compile-
time checks its arguments (instead of relying on programmers to get
it right every single time, as 'printf()' does). So define a log
stream and stream into this. This could be as simple as
std::ostream& logstrm = std::cerr;
and it could be as sophisticated as
http://sourceforge.net/projec...
which uses a templatized 'operator<<()' to delay argument evaluation.

> [...]

Schobi