Thread: Create a ostringstream like class.

  1. #1
    Registered User
    Join Date
    Jan 2008
    Posts
    66

    Create a ostringstream like class.

    I'm creating a class to handle timed message dialogues on screen for my game, it all went well, but what I'm trying to achieve is something like a std:stringstream object class, but with another abilities.

    Code:
    #include "cMessage.h"
    
    cMessage::cMessage(int X, int Y, int ID) : X(X), Y(Y), FontID(ID)
    {
    	StartTime = 0;
    	Active = false;
    }
    
    void cMessage::PostMessage(std::string Message)
    {
    	StartTime = SDL_GetTicks();
    
    	this -> Message = Message;
    
    	Active = true;
    }
    
    void cMessage::OnWrite(SDL_Surface *Surf_Display, SDL_Surface *Surf_Balloon)
    {
    	if(Surf_Display == NULL || !Active || Surf_Balloon == NULL)
    		return;
    
    	if(StartTime + MESSAGE_DURATION > SDL_GetTicks())
    	{
    		CSurface::OnDraw(Surf_Display, Surf_Balloon, BALLOON_X, BALLOON_Y);
    		SDL_Color Fg = {0, 0, 0, 0};
    		CFont::FontControl.OnWrite(&Surf_Display, Message.c_str(), FontID, X, Y, &Fg, NULL, CFONT_BLENDED | CFONT_ALIGN_CENTER);
    	}
    	else
    	{
    		Message.clear();
    		Active = false;
    	}
    }
    With this class, each time I want to post a message, I need to:
    1. Create an otringstream object
    2. Post this ostringstream message object with PostMessage()

    How can I make a function that works like ostringstream, but that directly activate my message?

    Like this PostMessage << "Message";

    I'm really new to C++, so I don't have too much experience with operator overloadings.

  2. #2
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Like this PostMessage << "Message";
    Okay, sounds like a job for iostreams, not operator overloading. That's fairly easy to do, starting with deriving a class from std::streambuf, overriding the virtual functions overflow and sync at least, and probably creating an IO manipulator called 'activate' that takes a tick count as an argument which has a default of MESSAGE_DURATION.

    In the end, your code might look like this:
    Code:
    PostMessage << "Message" << activate(nticks);
    Which is pretty close to what you describe, with no need for an intermediate ostringstream object. With that, you could write anything to it that you could through std::cout after you hooked it to an ostream object.

    std:stringstream std:stream
    What an unfortunate substitution on this forum! I guess smilies are more useful than those classes .
    Last edited by whoie; 09-06-2008 at 03:24 AM.

  3. #3
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Code:
    cMessage::cMessage(int X, int Y, int ID) : X(X), Y(Y), FontID(ID)
    {
    	StartTime = 0;
    	Active = false;
    }
    Do not use the same name for your constructor parameters as you use for your class member variables. At some point sooner or later that will confuse you [and probably lead to you spending an hour looking at the code until you suddenly slap yourself for being such a fool and not realizing that you are chagning the incoming parameter instead of the class member]...

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  4. #4
    Registered User
    Join Date
    Jan 2008
    Posts
    66
    Quote Originally Posted by whoie View Post
    Okay, sounds like a job for iostreams, not operator overloading. That's fairly easy to do, starting with deriving a class from std::streambuf, overriding the virtual functions overflow and sync at least, and probably creating an IO manipulator called 'activate' that takes a tick count as an argument which has a default of MESSAGE_DURATION.

    In the end, your code might look like this:
    Code:
    PostMessage << "Message" << activate(nticks);
    Which is pretty close to what you describe, with no need for an intermediate ostringstream object. With that, you could write anything to it that you could through std::cout after you hooked it to an ostream object.



    What an unfortunate substitution on this forum! I guess smilies are more useful than those classes .
    Now, that sounds difficult, how do I get started?
    Making this class a base class from std::streambuf first?

    matsp: I'll remember that

  5. #5
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Quote Originally Posted by ThLstN View Post
    Now, that sounds difficult, how do I get started?
    Making this class a base class from std::streambuf first?
    You're right, it does sound difficult, but when you get past the initial fuzziness, it's not that difficult at all. It just seems a lot harder than it really is when you do it for the first time. You're going to have to decide whether replacing ostringstream is going to be worth the learning investment. If you are using the SDL libraries reasonably successfully, then you can handle iostreams.

    You could make cMessage a child of std::streambuf, but you will end up dropping the PostMessage and OnWrite member functions. Instead, PostMessage will be mostly replaced by overflow and OnWrite will be mostly replaced by sync. It's going to take a little more surgery to fill out the rest, but that's a start.

    Does this sound like it's worth getting rid of all the ostringstream uses or not?

  6. #6
    Registered User
    Join Date
    Jan 2008
    Posts
    66
    Quote Originally Posted by whoie View Post
    You're right, it does sound difficult, but when you get past the initial fuzziness, it's not that difficult at all. It just seems a lot harder than it really is when you do it for the first time. You're going to have to decide whether replacing ostringstream is going to be worth the learning investment. If you are using the SDL libraries reasonably successfully, then you can handle iostreams.

    You could make cMessage a child of std::streambuf, but you will end up dropping the PostMessage and OnWrite member functions. Instead, PostMessage will be mostly replaced by overflow and OnWrite will be mostly replaced by sync. It's going to take a little more surgery to fill out the rest, but that's a start.

    Does this sound like it's worth getting rid of all the ostringstream uses or not?
    Yeah, you're right, I have been programming only for about one year and each time I found the solution for a specific problem, it will be very easy for almost all similar problems.

    So first, I should declare cMessage as a child of std::streambuf, I suppose.
    I've read some tutorials about streambuf, I know that streambuf is a virtual base class that I need to inherit, but I still don't have any idea how to achieve something like I said.
    Yeah, it's worth to go on.

    So this is the header, and I've declared it to be a child of std::streambuf, so what now?

    Code:
    #ifndef _CMESSAGE_H_
    #define _CMESSAGE_H_
    
    #include <SDL/SDL.h>
    #include <string>
    #include <streambuf>
    #include "defines.h"
    #include "CSurface.h"
    #include "CFont.h"
    
    #define MESSAGE_DURATION 1500
    
    #define NUM_MESSAGES        8
    
    #define CONSECUTIVESIX      0
    #define NO_MOVES_AVAIL      1
    #define SELECT_PIECE        2
    #define THROW_DICE          3
    #define INVALID_MOVE        4
    #define EXTRA_MOVE          5
    #define GET_ONPATH          6
    #define SAFELY_HOME         7
    
    class cMessage : public std::streambuf
    {
    	private :
    		int X;
    		int Y;
    		int FontID;
    		std::string Message;
    		unsigned long StartTime;
    	public :
    		bool Active;
    		bool oneTime;
    		cMessage(int X, int Y, int ID);
    		void PostMessage(std::string Message, bool oneTime_flag = false);
    		void OnWrite(SDL_Surface *Surf_Display, SDL_Surface *Surf_Balloon);
    		static const char *GameMessage[NUM_MESSAGES];
    };
    
    #endif
    Oh yeah, I've recently modified something in this class, so here it is.

    Code:
    #include "cMessage.h"
    
    cMessage::cMessage(int X, int Y, int ID) : X(X), Y(Y), FontID(ID)
    {
    	StartTime = 0;
    	Active    = false;
    	oneTime   = false;
    }
    
    void cMessage::PostMessage(std::string Message, bool oneTime_flag)
    {
    	if(!Active)
    	{
    		StartTime = SDL_GetTicks();
    		this -> Message = Message;
    		Active  = !oneTime_flag;
    		oneTime = oneTime_flag;
    	}
    }
    
    void cMessage::OnWrite(SDL_Surface *Surf_Display, SDL_Surface *Surf_Balloon)
    {
    	if(Surf_Display == NULL || (!Active && !oneTime) || Surf_Balloon == NULL)
    		return;
    	
    	if(StartTime + MESSAGE_DURATION > SDL_GetTicks() || oneTime)
    	{
    		CSurface::OnDraw(Surf_Display, Surf_Balloon, BALLOON_X, BALLOON_Y);
    		SDL_Color Fg = {0, 0, 0, 0};
    		CFont::FontControl.OnWrite(&Surf_Display, Message.c_str(), FontID, X, Y, &Fg, NULL, CFONT_BLENDED | CFONT_ALIGN_CENTER);
    
    		if(oneTime)
    		{
    			oneTime = false;
    			Active  = false;
    			Message.clear();
    		}
    	}
    	else
    	{
    		Message.clear();
    		Active = false;
    	}
    }
    
    const char* cMessage::GameMessage[NUM_MESSAGES] =
    {
    	", You threw two\nconsecutive sixes. Your\nbonus turn is canceled.",
    	", You Have No\nMoves Available. You have\nto skip your turn.",
    	",\nPlease select your piece.",
    	",\nPlease throw your dice.",
    	",\nThat is an invalid move!",
    	", You threw a\nsix, you'll get a\nbonus turn!",
    	", You threw a six,\nyou can now take\na piece on play.",
    	",\none of your pieces has\nmade it safely home!"
    };
    Last edited by ThLstN; 09-06-2008 at 01:21 PM.

  7. #7
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Try overriding the inherited virtual function overflow in the protected section of your class so that it appends the character to your Message member.

    Something like (not tested):
    Code:
    class cMessage : public std::streambuf
    {
        ...
        protected :
            virtual int overflow(int c) {
                if ( c != EOF ) Message += c;
                return c;
            }
        ...
    };
    Then hook your cMessage object to an ostream (not tested):
    Code:
    cMessage msgbuf(x, y, id);
    ostream msgstream(&msgbuf);
    msgstream << "Here's a message.";
    msgbuf.PostMessage(false);
    msgbuf.OnWrite(display, balloon);
    Then remove the Message parameter, and the one line of code that uses it from your PostMessage member function and test it. I don't have a clue what your Active member is doing, what are you using it for? Why not just test for Message.empty()?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. help on class coupling
    By andrea72 in forum C++ Programming
    Replies: 4
    Last Post: 04-17-2011, 10:16 AM
  2. class composition constructor question...
    By andrea72 in forum C++ Programming
    Replies: 3
    Last Post: 04-03-2008, 05:11 PM
  3. Replies: 8
    Last Post: 07-24-2006, 08:14 AM
  4. My Window Class
    By Epo in forum Game Programming
    Replies: 2
    Last Post: 07-10-2005, 02:33 PM
  5. class member access denied
    By chiqui in forum C++ Programming
    Replies: 2
    Last Post: 05-27-2002, 02:02 PM