0

I am trying to make a nested function call with a variable number of parameters without retrieving them. And I get the wrong result.

Here is my simple c++ program:

extern "C" {
#include <stdio.h>
}
#include <cstdarg>

class ctty {
    public:
        ctty();
        int logger(int prior, const char* format, ...);
    private:
};

ctty::ctty(){};

int ctty::logger(int prior, const char* format, ...)
{
    va_list ap;
    va_start(ap,format);
    printf(format, ap);
    va_end(ap);
    return 0;
}

int main(int argc, char** argv)
{
    ctty tty;
    tty.logger(0, "Test %d %d %d\n", 7, 5, 5);
    return 0;
}

result:

Test -205200 7 5

I expect a result

Test 7 5 5

I don’t understand what am I doing wrong? Thanks in advance.

  • Please give more detailed information about what you are expecting and the difference to the outcome. It's hard to read code from others, where many types and functions are unclear – Seb Feb 21 '20 at 00:31
  • 2
    What makes you think that you can pass a `va_list` to `printf`? Have a look at https://en.cppreference.com/w/cpp/io/c/vfprintf. – walnut Feb 21 '20 at 00:59
  • `extern "C" { }` around a standard library include is clearly wrong. I doubt it is legal. – walnut Feb 21 '20 at 01:02
  • Duplicate of [call printf using va_list](https://stackoverflow.com/questions/5977326/call-printf-using-va-list) – walnut Feb 21 '20 at 01:03

1 Answers1

0

You can't directly pass va_list to printf. va_list is a wrapper around the actual list of arguments (whatever its representation is). Although C way to do should be to use vprintf, in C++ there are safer alternatives, like variadic templates allowing to create a safer version of formatted printing, e.g. (a fictional format string for brevity of example):

#include <iostream>
#include <cstdlib>

class ctty {
    public:
        ctty();

        template<typename T, typename... Args>
        int logger(int prior, const char* format, T value, Args... args);
    private:
        void logger(int prior, const char *s);
};

ctty::ctty(){};

void ctty::logger(int prior, const char *s)
{
    while (*s) {
        if (*s == '%') {
            if (*(s + 1) == '%') {
                ++s;
            }
            else {
                throw std::runtime_error("invalid format string: missing arguments");
            }
        }
        std::cout << *s++;
    }
}

template<typename T, typename... Args>
int ctty::logger(int prior, const char* format, T value, Args... args)
{
    while (*format) {
        if (*format == '%') {
                std::cout << value;
                logger(prior, format + 1, args...); 
                return 0;
            }
        std::cout << *format++;
    }
    throw std::logic_error("extra arguments provided to logger");
}

int main(int argc, char** argv)
{
    ctty tty;
    tty.logger(0, "Test % % %\n", 7.55f, "Tada!", 888);
    return 0;
} 

This part of your code:

extern "C" {
#include <stdio.h>
}

Is technically an undefined behavior, while it may compile and not have adverse effect in some cases, it's not portable. You have to use C++ headers, e.g. <cstdio>

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42