Page 1 of 1

Accepting an unspecified number of arguments to a function Making a printf() like function Rate Topic: -----

#1 gabehabe  Icon User is offline

  • GabehabeSwamp
  • member icon




Reputation: 1385
  • View blog
  • Posts: 10,962
  • Joined: 06-February 08

Posted 07 September 2008 - 08:10 AM

Creating a function which accepts an unspecified number of arguments
Using va_list, va_arg, va_start and va_end

What should I know before following this tutorial?
You should have at least a basic knowledge of stringstreams. Other than that, you only really need to know the basics.

So what will I get out of this tutorial?
This tutorial is going to explain how you can pass an undefined number of arguments to a function. The code from this tutorial is going to be a basic version of sprintf, which is used to format a string, using tokens %s and %d. Sound fun? Read on!

So, all programs start with the header files that should be included. We're going to be using cstdarg, string, iostream, and sstream. yeah, I know, quite a few. Don't worry though, they're really only for the example. To include those va macros, you need to include cstdarg.

Let's start:
#include <cstdarg>   // for the macros va_list, va_arg, va_start and va_end
#include <iostream>  // standard input/output stream
#include <string>    // string data type
#include <sstream>   // string stream

using namespace std;


So now we can get started on writing our function. Let's call it MYsprintf, since it will have a similar effect to that of sprintf()
string MYsprintf (const char *str, ...)
{

Let's go over what's going to be passed to the function. We have const char *str which is basically the main string. Think of that of printf, it takes a string first, right? Same applies here.

After that, we have ... to which I guess your reaction is a little like this:
Posted Image

No worries, it basically means that we're going to be accepting more arguments, but we don't know what they are yet. :)

Now, the first thing we want to do is create a va_list which is basically a list of arguments. It will store all those additional arguments that were passed to MYsprintf. We're also going to want to initialise that, using va_start() like so:
    va_list args;
    va_start(args, str);

Next, I'm just going to create an ostringstream so that we can add everything to a string.
    ostringstream output;


The next thing that we need to do is loop through str Nothing new here (or it shouldn't be) we just loop, like so:
    for (unsigned int i = 0; i < strlen(str); i++)
    {

Now, as we loop through str we just need to format it. A little pseudocode:
if the current character is % and the following character is d or s
{
	add it to the output stream
	increment the counter to avoid adding % to the output string
}
else add the current character to the output string stream
Let's have a look at the code for the if statement:
        if (str[i] == '%' && str[i+1] != '%' && i+1 < strlen(str)) // we need to format it
        {
making sense so far? I hope so! Basically, we're making sure i+1 is still in the scope of str, and making sure that the character after '%' is not '%' (in which case, there's nothing to format)
Now, since we only accept s and d as parameters (string or int) we're gonna need a switch, too.
Let's take a look at the cases:
            switch (str[i+1]) // switch the next character
            {
                case 's':
                    char *temp = va_arg (args, char*);
                    output << temp;
                    break;
                case 'd':
                    int Temp = va_arg (args, int);
                    output << Temp;
                    break;
                default:
                    output << str[i];
            } // end switch
            i++; // increment to avoid outputting the next character
        } // end if

Pretty simple, the only new thing there should be va_arg()
Basically, we add our temporary value to args (our va_list) and then add it to the output string stream. The reason that we add it to args is to make sure that all arguments are accounted for by the end of the function.

After that, it's our else statement, and then the end of the function! So we're nearly there! :)
        else output << str[i];
    } // end for

Now all we need to do is clear up args, return the string, and exit the function, like so:
    va_end(args);
    return output.str();
} // end MYsprintf

And there we have it! Let's take a quick look at the implementation of MYsprintf:
int main ()
{
    string myStr = MYsprintf ("%s is %d years old. %s", "gabehabe", 18, "(^_^/>)\"");
    cout << myStr; // output the string
    
    cin.get (); // hold the window open
    return EXIT_SUCCESS; // successful execution
}


And here's the whole code:
#include <cstdarg>   // for the macros va_list, va_start and va_end
#include <iostream>  // standard input/output stream
#include <string>    // string data type
#include <sstream>   // string stream

using namespace std;

string MYsprintf (const char *str, ...)
{
    va_list args;
    va_start(args, str);
    ostringstream output;
    for (unsigned int i = 0; i < strlen(str); i++)
    {
        if (str[i] == '%' && str[i+1] != '%' && i+1 < strlen(str)) // we need to format it
        {
            switch (str[i+1]) // switch the next character
            {
                case 's':
                    char *temp = va_arg (args, char*);
                    output << temp;
                    break;
                case 'd':
                    int Temp = va_arg (args, int);
                    output << Temp;
                    break;
                default:
                    output << str[i];
            } // end switch
            i++; // increment to avoid outputting the next character
        } // end if
        else output << str[i];
    } // end for
    va_end(args);
    return output.str();
} // end MYsprintf

int main ()
{
    string myStr = MYsprintf ("%s is %d years old. %s", "gabehabe", 18, "(^^/>.)\"");
    cout << myStr; // output the string

    cin.get (); // hold the window open
    return EXIT_SUCCESS; // successful execution
}


Is This A Good Question/Topic? 1
  • +

Replies To: Accepting an unspecified number of arguments to a function

#2 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Posted 09 September 2008 - 07:12 AM

I think you got some smiles in your code. You have to be careful of that when using the syntax coloring.

Though to be honest the syntax coloring is wrong. In C/C++ "// " is valid and not a open quotes followed by an end of line comment.

I would like to point out my own snippet to you (Centered Printf) -- The vsprintf() function can be of value when trying to create your own member of the printf family. Of course to use it you need to be familiar with va_list, va_arg, va_start, and va_end.
Was This Post Helpful? 1
  • +
  • -

Page 1 of 1