[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 274
  • Last Modified:

The variable number of arguments....argument

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
0
DJ_AM_Juicebox
Asked:
DJ_AM_Juicebox
  • 6
  • 3
  • 3
  • +2
1 Solution
 
AxterCommented:
Yes.
Example:
int logRecord(const char *format, ...)
{
      
      va_list marker;
      char buf[1024];
      va_start(marker,format);

      vsprintf(buf, format,marker);
      
      AnotherFunction("%s", buf);
}
0
 
AxterCommented:
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);
}
0
 
Infinity08Commented:
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);
        }
    };
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
Infinity08Commented:
More info on stdarg here :

        http://www.cplusplus.com/reference/clibrary/cstdarg/
0
 
DJ_AM_JuiceboxAuthor 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
0
 
ikeworkCommented:
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
0
 
AxterCommented:
>>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.
0
 
ikeworkCommented:
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 );
         }
    };

0
 
Infinity08Commented:
>> 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.
0
 
itsmeandnobodyelseCommented:
>>>> 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
0
 
Infinity08Commented:
>> 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  ? <<
0
 
itsmeandnobodyelseCommented:
>>>>  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

   
0
 
Infinity08Commented:
>> 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 ;)
0
 
itsmeandnobodyelseCommented:
>>>> 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.
0
 
Infinity08Commented:
>> 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 ;)
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 6
  • 3
  • 3
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now