Results 1 to 3 of 3

Thread: C/CPP Programming Tutorials/Questions.

  1. #1
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default C/CPP Programming Tutorials/Questions.

    A couple forum members PM me for help in this section and I tend to sorta delete PM's or just read them quick. This thread is for specific questions. Instead of PMing me, post it here so everyone else can help or see it as well.

    I was asked a question earlier today. How does std::cout, printf, etc work? Well that user isn't online atm but I thought I'd share it with everyone:

    NOTE: I'll format the tutorial better later!

    C++ Code:
    #include <iostream>
    #include <windows.h>
    #include <sstream>
    #include <stdexcept>
    #include <cstdarg>              //Our preprocessor directives telling the compiler to include these files!

    using namespace std;


    void Brandon_Printf(const char* fmt, ...)
    {
        int fmtLength = strlen(fmt);           //Get the length of the first argument/string.
        va_list VariableArgs;                       //Declare a variadic list.
        va_start(VariableArgs, fmt);                //Tell the list where to start from.
        std::stringstream DataHolder;               //An open stream that holds no data for now.

        for (int I = 0; I < fmtLength; I++)         //For the entire length of the string, we iterate through each character to find %variables.
        {
            if (fmt[I] == '%')                      //Before we switch you should note that variadic lists aren't type safe and do not implicitly convert variables passed.
            {
                switch(tolower(fmt[++I]))           //Increment I to get the char that indicates the type of variable being parsed and switch on it!
                {
                    case 'd':                                                   //Floats are promoted to doubles automatically! no need for an "f" case.
                                DataHolder << va_arg(VariableArgs, double);     //Variadic lists are not type-safe, thus we must specify the type to be handled!
                        break;

                    case 'i':
                                DataHolder << va_arg(VariableArgs, int);        //This also includes chars. Chars are promoted to int's automatically!
                        break;

                    case 's':
                                DataHolder << va_arg(VariableArgs, const char*); //Strings, std::strings, etc..
                        break;

                    default:        throw std::runtime_error("Error! Invalid Arguments passed to Brandon_Printf!");
                        break;
                }
            }
            else
                DataHolder << fmt[I];     //If it isn't %Var, just add the character to the stream.
        }
        va_end(VariableArgs);

        std::cout<<DataHolder.str();        //cout is defined as an output stream to the console buffer. I'd make my own but hooking the console.. meh -_-
    }

    struct CustomPoint                      //Create a simple class to hold data for a point.
    {
        private:
            int X, Y;                       //Class Members X and Y which represents the X && Y location on the screen or from our user.

        public:
            CustomPoint();                  //Default Construct just because.
            CustomPoint(int x, int y);      //Alternate Constructor to allow creation of a point with defined values.
            ~CustomPoint();                 //Finally our Destructor.

            friend ostream& operator << (ostream& Output, const CustomPoint& CP);   //Declare our outstream as a friend to allow access to class members regardless of it's inheritance.
    };

    CustomPoint::CustomPoint(){}                               //Implement our Default Constructor.
    CustomPoint::CustomPoint(int x, int y) : X(x), Y(y) {}     //Implement our Alternate Constructor && Initialize our class members.
    CustomPoint::~CustomPoint(){};                             //Implement our Destructor.



    std::ostream& operator << (std::ostream& Output, const CustomPoint& CP)  //Implement the outstream. This makes print functions behave nicely when parsing out type.
    {
        Output<< "Point(" << CP.X << ", " << CP.Y << ")";       //This makes out Custom Point print nicely like: Point(X, Y);
        return Output;                                          //We Return a value to allow chaining outputs. Such as cout<< P << P2 << P3; Etc..
    }



    int main()
    {
        Brandon_Printf("Mehfds %d gsgs %i \n\n", 1900.128, 99);     //Test our custom printf function to make sure it works!

        CustomPoint P = CustomPoint(-1, -1);    //Construct a point with values (-1, -1) respectively.
        std::cout<< P <<std::endl;              //Yes that's right. We can just print a point! Without the outstream, this would throw a compilation error!
    }

    The first couple lines, we include our directives that tell the compiler to include those specific files from the standard C/C++ library and the STL a.k.a. Standard Template Library.

    Next we do using namespace std; which allows us to import the entire namespace to use functions without prefixing it with std::function name.

    We then declare Brandon_Printf which is a variadic function. This means that it takes a variable/unknown amount of arguments. This allows the user to insert as many parameters as they wish!

    Now for such a function, we have two options. Either we use variadic templates which I have covered already (Link at the end of this post) OR we use variadic lists which I have demonstrated above since PrintF does exactly this. It uses a variadic list which requires that you specify what type of argument is being passed.

    So.. Variadic lists require that you declare the first argument and its type! This is a must, because variadic lists have no way of unpacking the rest of parameters without this information.

    For the first argument, we declare it as a const char* aka an array of characters or better known as a string. The next parameter we declared as an ellipsis. This is known as a packed parameter and specifies that it can be an infinite amount of parameters!

    Now variadic lists aren't type-safe. They WILL throw an exception or cause undefined behaviour if the parameters passed are INCORRECT. They do not do implicit conversions either. Example:

    C++ Code:
    Brandon_Printf("%d", 100);  //INCORRECT! Undefined behaviour! 100 is not a double, it is an integer!

    In the above, it's incorrect because the variadic list does not do implicit conversions! I specified that it will be passed a double. But what do I do? I go and I specify an integer! Now these may look the same:

    C++ Code:
    double D = 100;
    int I = 100;

    They look the same but are they? No they are not the same. Because of the double specifier, one is explicitly defined to have an appending decimal or period. We can implicitly cast one to the other by just assigning I to D or vice-versa but Variadic templates will not do this.

    Instead it requires we explicitly define the value being passed:

    C++ Code:
    Brandon_Printf("%d", 100);  //Incorrect! No implicit conversions.
    Brandon_Printf("%d", 100.0); //That decimal explicitly says that the value parsed is a float/double!

    For this reason and many others, we say variadic lists aren't type safe and can lead to undefined behaviour. Variadic templates on the other hand are C++ 11 features and you are required to have a compiler that supports that standard to compile such code. It is much safer and better than lists!

    Next is the demonstration of std::cout. Cout pronounced see-out, is the exact same as the overload output as shown in the example above. There is one thing though, std::cout is special because it outputs directly to the console buffer rather than another stream. That is the only difference!

    So the code shown is essentially a simulation of std::cout except that it outputs to the stream provided aka "osstream Output".

    Anymore questions I will answer them if I see them or you post here and I'll write you up something.

    Links (Variadic Templates && Infinite Parameters): http://villavu.com/forum/showthread.php?t=82610

    Goodnight to all. I promise to finish the Regular Expressions tutorial when I have time.
    Last edited by Brandon; 07-04-2012 at 12:28 AM.
    I am Ggzz..
    Hackintosher

  2. #2
    Join Date
    Dec 2011
    Location
    -bash
    Posts
    515
    Mentioned
    0 Post(s)
    Quoted
    27 Post(s)

    Default

    W8, Brandon did you actually make your own printf function?? Man does your awesomeness have no end!?

  3. #3
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by Chig View Post
    W8, Brandon did you actually make your own printf function?? Man does your awesomeness have no end!?

    LOL yes I made my own. You asked how does printf work and I have no other way of explaining that in simple terms other than to show a coded example of printf itself in action with an explanation. I mean it's not exception safe by any means but its the exact simulation of printf which isn't exception safe either.

    Either way that's how it's done and you can see the internals and how it works.. Also demonstrates good use of pointers and variadic lists.. Cout also works like the osstream overload I demonstrated.

    Though if you are going to use variadic lists, remember the types you pass to it must be exact so that no exceptions are thrown and that behaviour will be proper. Otherwise use variadic templates as shown in the link at the very end.
    I am Ggzz..
    Hackintosher

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •