0

I wrote a Logger class for dumping out information to files. The following code is a miniature of Logger. The functions look almost the same... But I don't know how to simplify it. Can you make it more elegant?

My earlier version used varadic macros to achieve this goal. I later noticed that those two data, file pointer and indent level, could be encapsulated into a class, so that I don't need to pass (FILE *fp, size_t indent) every time. (Xml_Logger &logger) is enough.

So I hope I could just use member functions, otherwise it's like I'm rolling back...

void ind_print(const char *format, ...) {
  print_indent();

  va_list args;
  va_start(args, format);
  vprintf(format, args);
  va_end(args);
}

void ind_println(const char *format, ...) {
  print_indent();

  va_list args;
  va_start(args, format);
  vprintf(format, args);
  va_end(args);

  printf("\n");
}

void print(const char *format, ...) {
  va_list args;
  va_start(args, format);
  vprintf(format, args);
  va_end(args);
}

void println(const char *format, ...) {
  va_list args;
  va_start(args, format);
  vprintf(format, args);
  va_end(args);

  printf("\n");
}

UPDATE: Look at these two functions. I hope println could invoke print, but unfortunately it could not. Isn't it?

void Xml_Logger::print(const char *format, ...) const {
  print_indent();
  va_list args;
  va_start(args, format);
  vfprintf(fp, format, args);
  va_end(args);
}

void Xml_Logger::println(const char *format, ...) const {
  print_indent();
  va_list args;
  va_start(args, format);
  vfprintf(fp, format, args);
  va_end(args);
  fputc('\n', fp);
}
Community
  • 1
  • 1
lzl124631x
  • 4,485
  • 2
  • 30
  • 49
  • 1
    I asked a similar question recently and the [top voted answer](http://stackoverflow.com/a/20639708/1145760) is pretty much what you have done. – Vorac May 30 '14 at 14:08
  • My advice is to avoid the macro, since it adds nothing but obfuscation. – William Pursell May 30 '14 at 14:28
  • @Vorac I don't think it's a similar question... You want to minimize modification when the underlying function is changed, but I want to remove the redundancy of my code. – lzl124631x May 30 '14 at 14:28
  • Use putchar/putc/fputc instead of printf to write a single newline – William Pursell May 30 '14 at 14:29
  • @WilliamPursell Me too. Whenever macro appears, people will criticize it. So I am asking advise other than macro (which I could come up with). – lzl124631x May 30 '14 at 14:31
  • Doesn't the answer linked by @Vorac reduce redundancy? – alk May 30 '14 at 15:04
  • @alk. I don't think it will help in my case. The answer Vorac mentioned could not reduce redundancy but decouple the underlying function from his code. – lzl124631x May 30 '14 at 15:24
  • @Moon, Xml_Logger::println couldn't invoke Xml_Logger::print, thats why vfprintf and va_list being invented. – alexander May 30 '14 at 15:46

4 Answers4

1

I would write something like that (and yes, also using a macro):

print.h:

void _print(int indent, int eol, const char *format, ...);
#define print(format, ...) _print(0, 0, format, __VA_ARGS__)
#define println(format, ...) _print(0, 1, format, __VA_ARGS__)
#define ind_print(format, ...) _print(1, 0, format, __VA_ARGS__)
#define ind_println(format, ...) _print(1, 1, format, __VA_ARGS__)

print.c:

void _print(int indent, int eol, const char *format, ...) {
  va_list args;

  if (ident) {
    print_indent();
  }

  va_start(args, format);
  vprintf(format, args);
  va_end(args);

  if (eol) {
     printf("\n");
  }
}
alexander
  • 2,703
  • 18
  • 16
  • Thanks. Seemingly it's only possible to forward arguments using variadic macro. Since there is just one statement in the macro, you don't need to wrap it with do{..}while(0) – lzl124631x May 30 '14 at 14:49
1

Just like you don't forward your arguments to fprintf, which is variadic, but vfprintf, you would do the same in your case.

void Xml_Logger::master_print(bool indent, bool newline, const char* format, va_list& args) const
{
  if (indent) print_indent();
  vfprintf(fp, format, args);
  if (newline) fputc('\n', fp);
}

void Xml_Logger::print(const char *format, ...) const
{
  va_list args;
  va_start(args, format);
  master_print(true, false, format, args);
  va_end(args);
}

void Xml_Logger::println(const char *format, ...) const
{
  va_list args;
  va_start(args, format);
  master_printf(true, true, format, args);
  va_end(args);
}

But this unfortunately requires repeating the va_list stuff in every wrapper. So if you have C++11, try using a template with perfect forwarding

Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Thanks. It seems that I have to live with the redundancy of `va_list` stuff. Your answer about variadic templates in C++11 is really impressive. That's what I want, namely, forwarding arguments between variadic functions. – lzl124631x May 31 '14 at 11:22
0

You can reduce each of these functions to a variadic macro, and this way you will get rid of the redundant calls to va_start and va_end:

#define IND_PRINT(fmt, ...) { \
    print_indent(); \
    printf(fmt, ## __VA_ARGS__); \
}

Then you should put each of these macros in a header file, so they are visible to the rest of your project.

Blagovest Buyukliev
  • 42,498
  • 14
  • 94
  • 130
  • Thanks. I did use these varadic macros but [people](http://stackoverflow.com/questions/23853332/how-to-mimic-variadic-macro-in-vc6-0) advised me not to use macro and create a `Logger` class instead. – lzl124631x May 30 '14 at 14:42
  • @Moon, your other question is about `C++`, which is entirely different story. – Vorac May 30 '14 at 14:54
  • @Vorac, whoops, mistake... I changed the label to `C++` now. Thanks. – lzl124631x May 30 '14 at 15:06
  • @Moon, in that case, IMO `streams` are your solution. However, my C++ is quite rusty and I can't put up an answer. I am sure `Boost` provides a solution to this problem, heck that library probably has code to land on the moon in it. – Vorac May 30 '14 at 15:14
0

You could use C++11 variadic templates to forward an variable number of parameters:

template<typename ...TArgs>
void Xml_Logger::println(const char *format, TArgs&& ...args) const
{
  print(format, std::forward(args)...);
  fputc('\n', fp);
}
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720