Hello,

for my error handling I have a class with static method overloads to print
error messages (I will probably make this a bit more general with some
pattern later). But for the start it is enough to print messages with
different numbers of arguments. I don't want to write endless overloads for
different numbers of arguments. So my first design looks like this:

// header file:

class ErrorHandler
{
public:
static inline void PrintError(const char* source, const int& numMsgParams,
...);
private:
explicit ErrorHandler(){};
};

// cpp file:

void ErrorHandler::PrintError(const char* source, const int& numMsgParams,
...)
{
cerr << source << ": ";

if (numMsgParams > 0)
{
va_list params;
va_start(params, numMsgParams);

char* addParam = NULL;

for (int i=0; i< numMsgParams; i++)
{
addParam = va_arg(params, char*);
cerr << addParam << ", ";
}

va_end(params);
}
cerr << endl;
}

Having to provide the number of arguments to the method call looks pretty
error-prone over maintenance time to me. Is there an alternative?

Thx for your suggestions,

Fabian

Re: safe alternative to va_arg by Igor

Igor
Thu Apr 17 07:18:27 CDT 2008

"Fabian" <Fabian@discussions.microsoft.com> wrote in message
news:B76FAB6E-A6A2-4354-9780-4E229F5A8758@microsoft.com
> for my error handling I have a class with static method overloads to
> print error messages (I will probably make this a bit more general
> with some pattern later). But for the start it is enough to print
> messages with different numbers of arguments. I don't want to write
> endless overloads for different numbers of arguments. So my first
> design looks like this:
>
> static inline void PrintError(const char* source, const int&
> numMsgParams, ...);
>
> void ErrorHandler::PrintError(const char* source, const int&
> numMsgParams, ...)
> {
> if (numMsgParams > 0)
> {
> va_list params;
> va_start(params, numMsgParams);
>
> char* addParam = NULL;
>
> for (int i=0; i< numMsgParams; i++)
> {
> addParam = va_arg(params, char*);
> cerr << addParam << ", ";
> }
>
> va_end(params);
>
> Having to provide the number of arguments to the method call looks
> pretty error-prone over maintenance time to me. Is there an
> alternative?

If you are willing to limit yourself to some maximum number of
parameters, try something like this:

// Add as many parameters as needed.
void PrintError(const char* source, const char* param1 = 0, const char*
param2 = 0) {
const char* params[] = {param1, param2};
const int nParams = sizeof(params) / sizeof(params[0]);

for (int i = 0; i < nParams; ++i) {
if (params[i]) cout << params[i];
}
}

--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925



Re: safe alternative to va_arg by Ulrich

Ulrich
Thu Apr 17 07:25:15 CDT 2008

Fabian wrote:
> for my error handling I have a class with static method overloads to print
> error messages (I will probably make this a bit more general with some
> pattern later). But for the start it is enough to print messages with
> different numbers of arguments. I don't want to write endless overloads
> for different numbers of arguments. So my first design looks like this:
[..]
> class ErrorHandler
> {
> public:
> static inline void PrintError(const char* source, const int& numMsgParams,
> ...);
> private:
> explicit ErrorHandler(){};
> };

A few things up front:
1. Passing 'int' by reference is overkill. The typical implementation of
references is to pass a pointer, which might even be larger than just
passing the integer itself and it incurs the indirection overhead.
2. Not every '}' is followed by a ';'!
3. ErrorHandler::PrintError() - I'd remove at least one instance of 'Error'
here.
4. Classes that only have static functions are conveniently replaced with
namespaces.


Anyway, back to the issue you actually came for, instead of adding various
overloads, use templates:

// no arguments
void print( char const* msg);
// one argument
template<typename A1>
print( char const* msg, A1 const& a1);
// two arguments
template<typename A1, typename A2>
print( char const* msg, A1 const& a1, A2 const& a2);

This is still some writing, but it's not that much, and how many additional
arguments do you need anyway?

Take a look at Boost.Format, btw!

Uli

--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Michael Wöhrmann, Amtsgericht Hamburg HR B62 932

Re: safe alternative to va_arg by Fabian

Fabian
Thu Apr 17 08:13:02 CDT 2008

Hello Uli,

> 4. Classes that only have static functions are conveniently replaced with
> namespaces.

This one was just the first step. I thought about writing different
implementations using the factory method pattern later. I.e. different
methods of error output (cerr, log file, whatever...). How big is the
performance loss for the virtual method call in this case?

regards,

Fabian


Re: safe alternative to va_arg by Ulrich

Ulrich
Thu Apr 17 10:18:12 CDT 2008

Fabian wrote:
> Hello Uli,
>
>> 4. Classes that only have static functions are conveniently replaced with
>> namespaces.
>
> This one was just the first step. I thought about writing different
> implementations using the factory method pattern later. I.e. different
> methods of error output (cerr, log file, whatever...). How big is the
> performance loss for the virtual method call in this case?

The answer to pretty much every performance question is: you have to measure
it. Anyway, estimations are possible...

So, typically, calling a function means fetching the address of that
function from somewhere and then jumping to it. With a virtual function,
all that changes is that the place where you read that function's address
from is relative to the object. In other words, it's like writing to an int
on the stack compared to writing an int via a pointer to it.

Further, since we are talking about logging, the function call actually
involves IO, which is by leagues slower than a simple memory accesses like
virtual function calls.

Summary: you won't notice the overhead.


BTW: a very convenient feature of C++ IOStreams is the overloaded rdbuf()
function. Using that, you can easily redirect a stream to stdout, a file or
any other stream:

int main() {
std::ofstream out("program.log");
std::cout.rdbuf(out.rdbuf());
:

The output via std::cout is now redirected to a file.

Uli

--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Michael Wöhrmann, Amtsgericht Hamburg HR B62 932

Re: safe alternative to va_arg by Fabian

Fabian
Fri Apr 18 01:36:00 CDT 2008

Hi Uli,

> BTW: a very convenient feature of C++ IOStreams is the overloaded rdbuf()
> function. Using that, you can easily redirect a stream to stdout, a file or
> any other stream:
>
> int main() {
> std::ofstream out("program.log");
> std::cout.rdbuf(out.rdbuf());
> :

Is it possible to redirect the output to multiple targets, e.g. to both a
file and the console?

Thanks,

Fabian

Re: safe alternative to va_arg by Ulrich

Ulrich
Fri Apr 18 02:16:12 CDT 2008

Fabian wrote:
>> std::ofstream out("program.log");
>> std::cout.rdbuf(out.rdbuf());
>> :
>
> Is it possible to redirect the output to multiple targets, e.g. to both a
> file and the console?

Yes. For that, you need a 'streambuf' that distributes output to two
targets. The 'streambuf' is the part of an IOStream that does the actual IO
and it has several methods that you can override and customise. I believe
that Boost has several helpers to make such things easier than with plain
C++, otherwise you can surely detect a few examples on the net.

Uli

--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Michael Wöhrmann, Amtsgericht Hamburg HR B62 932

Re: safe alternative to va_arg by Fabian

Fabian
Fri Apr 18 03:08:01 CDT 2008

Hi Uli,

> Anyway, back to the issue you actually came for, instead of adding various
> overloads, use templates:

There obviously is a problem: As the template (be it a standalone function
or a static class method) has to be usable from different dlls I have to put
it in a "common" dll. Dll-exporting templates only works with explicit
specialization (in the cpp file). So in this case there's no difference to
writing the overloads. Or have I overseen sth.?

Thx,

Fabian

Re: safe alternative to va_arg by Ulrich

Ulrich
Fri Apr 18 03:58:58 CDT 2008

Fabian wrote:
>> Anyway, back to the issue you actually came for, instead of adding
>> various overloads, use templates:
>
> There obviously is a problem: As the template (be it a standalone function
> or a static class method) has to be usable from different dlls I have to
> put it in a "common" dll. Dll-exporting templates only works with explicit
> specialization (in the cpp file). So in this case there's no difference to
> writing the overloads. Or have I overseen sth.?

Yes, you basically split the code in two parts:
1. Template functions that format the log messages.
2. Non-template functions that write the log message or configure the
logger.

The first category is inline in a header, the second one can be exported
from a DLL.

BTW: in case you are using VC6, now is a time to upgrade. With that
compiler, you further have to split the code in a baseclass that can be
exported from a DLL and a derived class (completely inline) that contains
the templates. Otherwise, you will get linker problems. At least I seem to
remember something like that...

Uli

--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Michael Wöhrmann, Amtsgericht Hamburg HR B62 932

Re: safe alternative to va_arg by Fabian

Fabian
Fri Apr 18 05:45:00 CDT 2008

Hello Uli,

is there any possibility to define default parameters? Default parameters
for templates are the default types, right? But if I could do sth. like

template void PrintError(const char*, const char* = 0, const char* = 0,
const char* = 0, const char* = 0);

for the specialization in the cpp file, I wouldn't have to write
specializations for a different number of parameters. But the compiler (btw:
I'm quite up to date: Visual Studio 2008) doesn't like it:

'PrintError' : an explicit specialization of a function template cannot have
any default arguments

Thx,

Fabian

Re: safe alternative to va_arg by Ulrich

Ulrich
Fri Apr 18 06:20:45 CDT 2008

Fabian wrote:
> is there any possibility to define default parameters? Default parameters
> for templates are the default types, right? But if I could do sth. like
>
> template void PrintError(const char*, const char* = 0, const char* = 0,
> const char* = 0, const char* = 0);

If you already know that you will only be logging strings, you don't need
any template, just an overloaded function with default argument like above.

Otherwise, my compiler claims that I can't use default template parameters
(_not_ functions parameters!) with template functions.

> for the specialization in the cpp file, I wouldn't have to write
> specializations for a different number of parameters. But the compiler
> (btw: I'm quite up to date: Visual Studio 2008) doesn't like it:
>
> 'PrintError' : an explicit specialization of a function template cannot
> have any default arguments

Well, of course you are not repeating the default arguments, right? You
never do that when you define a function that was declared with default
arguments.

Uli

--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Michael Wöhrmann, Amtsgericht Hamburg HR B62 932

Re: safe alternative to va_arg by Fabian

Fabian
Fri Apr 18 07:37:01 CDT 2008

Hello Uli,

> If you already know that you will only be logging strings, you don't need
> any template, just an overloaded function with default argument like above.

I am not sure that I'm always logging strings. I wrote overloads for the
template functions now - with a different number of arguments each:

// header file:
template <typename T1> COMMON_API inline void PrintError(const T1& arg1);
template <typename T1, typename T2> COMMON_API inline void PrintError(const
T1& arg1, const T2& arg2);
template <typename T1, typename T2, typename T3> COMMON_API inline void
PrintError(const T1& arg1, const T2& arg2, const T3& arg3);
// and so on...

// cpp file:
template <typename T1> void PrintError(const T1& arg1)
{
cerr << arg1 << endl;
}
template <typename T1, typename T2> void PrintError(const T1& arg1, const
T2& arg2)
{
cerr << arg1 << ": " << arg2 << endl;
}
template <typename T1, typename T2, typename T3> void PrintError(const T1&
arg1, const T2& arg2, const T3& arg3)
{
cerr << arg1 << ": " << arg2 << ", " << arg3 << endl;
}
// and so forth...

But when I now compile a specialization with more than one parameter I get a
compiler error:

template inline void COMMON_API PrintError<const char*>(const char*); // works
template inline void COMMON_API PrintError<const char*, const char*>(const
char*, const char*); // produces C2977

error C2977: 'PrintError' : too many template arguments

So what is this now???

Thanks for your patience with me,

Fabian

Re: safe alternative to va_arg by Fabian

Fabian
Fri Apr 18 07:38:00 CDT 2008

Hello Uli,

> If you already know that you will only be logging strings, you don't need
> any template, just an overloaded function with default argument like above.

I am not sure that I'm always logging strings. I wrote overloads for the
template functions now - with a different number of arguments each:

// header file:
template <typename T1> COMMON_API inline void PrintError(const T1& arg1);
template <typename T1, typename T2> COMMON_API inline void PrintError(const
T1& arg1, const T2& arg2);
template <typename T1, typename T2, typename T3> COMMON_API inline void
PrintError(const T1& arg1, const T2& arg2, const T3& arg3);
// and so on...

// cpp file:
template <typename T1> void PrintError(const T1& arg1)
{
cerr << arg1 << endl;
}
template <typename T1, typename T2> void PrintError(const T1& arg1, const
T2& arg2)
{
cerr << arg1 << ": " << arg2 << endl;
}
template <typename T1, typename T2, typename T3> void PrintError(const T1&
arg1, const T2& arg2, const T3& arg3)
{
cerr << arg1 << ": " << arg2 << ", " << arg3 << endl;
}
// and so forth...

But when I now compile a specialization with more than one parameter I get a
compiler error:

template inline void COMMON_API PrintError<const char*>(const char*); // works
template inline void COMMON_API PrintError<const char*, const char*>(const
char*, const char*); // produces C2977

error C2977: 'PrintError' : too many template arguments

So what is this now???

Thanks for your patience with me,

Fabian