We help IT Professionals succeed at work.

The variable number of arguments....argument

DJ_AM_Juicebox
on
294 Views
Last Modified: 2011-09-20
Hi,

I have a base class with a message printing statement like:

     class Base {
         virtual void LogMsg(const char* psz, ...) = 0;
    };

Now derived classes should implement it. I'm wondering if it is possible to pass through the '...' variable to another function. I want to do something like:

    class Derived {
         void LogMsg(const char* psz, ...)
         {
              // let this class do it:
             m_Log.LogMsg(psz, ...);
         }
    };

    class Log {
        void LogMsg(const char* psz, ...)
       {
              // handle it
       }
    };

Can you pass the '...' parameter through somehow  ?

Thanks
Comment
Watch Question

Senior Software Engineer
CERTIFIED EXPERT
Commented:
Unlock this solution and get a sample of our free trial.
(No credit card required)
UNLOCK SOLUTION
AxterSenior Software Engineer
CERTIFIED EXPERT

Commented:
To be safer, you should use _vsnprintf if your implementation supports it.

Example:
_vsnprintf( buf, sizeof( buf ) - 1, format,marker);


void logRecord(const char *format, ...)
{
     
      va_list marker;
      char buf[1024] = "";
      va_start(marker,format);

      _vsnprintf( buf, sizeof( buf ) - 1, format,marker);
      buf[ sizeof(buf) - 1 ] = '\0';

      AnotherFunction("%s", buf);
}
CERTIFIED EXPERT
Top Expert 2009

Commented:
Something like this :

    #include <cstdarg>

    class Derived {
         void LogMsg(const char* psz, ...)
         {
             va_list args;
             va_start(args, psz);
             m_Log.vLogMsg(psz, args);
             va_end(args);
         }
    };

    class Log {
        void LogMsg(const char* psz, ...)
        {
             va_list args;
             va_start(args, psz);
             vfprintf(stderr, psz, args);
             va_end(args);
        }
        void vLogMsg(const char* psz, va_list args)
        {
             vfprintf(stderr, psz, args);
        }
    };
CERTIFIED EXPERT
Top Expert 2009

Commented:
More info on stdarg here :

        http://www.cplusplus.com/reference/clibrary/cstdarg/

Author

Commented:
Hi Axter,

Will that degrade performance a lot? Every function down the chain is going to have to format it themselves, then pass it on to the next guy - if there's no other way, it doesn't matter, just wondering though,

Thanks
CERTIFIED EXPERT

Commented:
hi DJ_AM_Juicebox,

no you cant pass it like this, you have to *unwrap* the arguments into a va_list and pass that:


    class Base {
         virtual void LogMsgIntern(const char* psz, va_list args) = 0;

         void LogMsgVar( const char* psz, ... )
         {
            va_list args;
            va_start( args, psz );
            LogMsgIntern( psz, args );
            va_end( args );
         }
    };


    class Derived : public Base {
         void LogMsgIntern(const char* psz, va_list args)
         {
              // let this class do it:
              m_Log.LogMsgIntern(psz, args);
         }
    };

    class Log : public Base {
        void LogMsgIntern(const char* psz, va_list args)
       {
            // xmpl: printf it it to stdout
            vfprintf( stdout, psz, args );
       }
    };


now you call it like this:

Log logger;
logger.LogMsg( "any logging %s", "test" );


hope it helps,
ike
AxterSenior Software Engineer
CERTIFIED EXPERT

Commented:
>>Will that degrade performance a lot? Every function down the chain is going to have to format it themselves, then pass it on to the next guy - if there's no
>>other way, it doesn't matter, just wondering though,

There's an assumption, that your derived function is going to want to do something with the logging, otherwise there would be no point in calling the derived function, and the base function should be called instead.

If your derived function wants to process the log, and add something to it, you're going to need to format it.

I recommend using C++ style logging over older C-Style logging.   C++ version is safer, since it has type safety.
CERTIFIED EXPERT

Commented:
typo .. LogMsgVar -> LogMsg

    class Base {
         virtual void LogMsgIntern(const char* psz, va_list args) = 0;

         void LogMsg( const char* psz, ... )
         {
            va_list args;
            va_start( args, psz );
            LogMsgIntern( psz, args );
            va_end( args );
         }
    };

CERTIFIED EXPERT
Top Expert 2009

Commented:
>> Will that degrade performance a lot? Every function down the chain is going to have to format it themselves, then pass it on to the next guy

No, if you pass a va_list, then it doesn't need to be parsed again ... Only the last function in the chain will format it into a string.
>>>> void LogMsg(const char* psz, ...)

The ... is very unsafe. You may pass any number of arguments and arbitrary argument type and the called function has no chance to actually find out what was passed and how much was passed. I always wonder what kind of fascination that C construct seems to have to people who on the other hand claim for const argument types and passing arguments by value rather than by pointer.

I have posted a C++ alternative to ... sometime ago and will dou it again here, though I doubt that I can fight the fascination which seem go with the ...  

The alternative is using a ostringstring reference object as an argument. Using operator<< you can use any number of arguments and any arbitrary type where a operator<< (ostream&, const type&) was defined together with the io manipulators directly in the call:

   void log(ostringstream& oss);
   ....
   
    ostringstream oss;
    log(oss << "First an integer " << i << " then a char* " << psz << " and finally a string " << str);

You easily can make that more convenient by defining a macro:

#define LOG(s) log((ostringstream&)(ostringstream() << s))

what allows to define the above sample like

   LOG("First an integer " << i << " then a char* " << psz << " and finally a string " << str);

Regards, Alex
CERTIFIED EXPERT
Top Expert 2009

Commented:
>> though I doubt that I can fight the fascination which seem go with the ...  

The ... is indeed not very typesafe, but it's quite common in the C language, and merits understanding how it works, because sooner or later you will come into contact with it. (remember, the question was about ... so that's what was answered).

For the record, I do agree that a C++ stream solution is most likely better for DJ_AM_Juicebox's purposes, but that wasn't the question ;)

        >> Can you pass the '...' parameter through somehow  ? <<
>>>>  but that wasn't the question ;)
You are right, but I didn't blame you experts that you answered to the question.

My concern was to tell DJ that the ... rarely goes together with C++ type safety therefore it would be better to use a C++ stream solution instead.

>>>> is indeed not very typesafe
you are tending to understatements in last comments ;-)

why not simply "is not typesafe".

Regards, Alex

   
CERTIFIED EXPERT
Top Expert 2009

Commented:
>> why not simply "is not typesafe".

Because it's not much less typesafe than the "rest" of C. And when you pay attention, it doesn't matter that it's not typesafe - you just have to make sure that you pass the exact variables that the compiler expects.

But yes, I do like understatements it seems eh ;)
>>>> Because it's not much less typesafe than the "rest" of C
My mother always said: don't trust people arguing with terms like "not much less" ;-)

But I don't agree principally: C is typesafe beside of 3 points: casting, arrays, ...

casting is not much different in C++ though you have 4 casting templates plus old C cast. You can break type safety with casting both in C and C++.

arrays turn to pointers when passed to a function. That is the same in C and C++. In C++ you have the 'option' to using container classes what helps with arrays if used.

... is the same in C / C++. In C++ you have a streaming alternative.

>>>> you just have to make sure that you pass the exact
>>>> variables that the compiler expects.
Great. Do it right and it works.
CERTIFIED EXPERT
Top Expert 2009

Commented:
>> don't trust people arguing with terms like "not much less" ;-)

Oh but you can trust me ... /me grins evilly


>> But I don't agree principally: C is typesafe beside of 3 points: casting, arrays, ...

And pointers ... How much of all C code ever written do you think uses those constructs ? Pretty much I'd say :)


About C++ : since C is a superset of C, of course pretty much all the "downsides" of C also ended up in C++ ... I never claimed otherwise.


>> Great. Do it right and it works.

Don't you just love it ;)
Unlock the solution to this question.
Thanks for using Experts Exchange.

Please provide your email to receive a sample view!

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.