Thread: using class to retrieve and store data

  1. #1
    Registered User
    Join Date
    Aug 2005
    Posts
    128

    using class to retrieve and store data

    I'm trying to write a class that when called the first time, it retrieves data from a file where the file contains a log line specific for a particular user.

    Whenever it's called after that, want to call it to get a specific user data setting such as email address or last date contacted, without re-quering the file.

    Here's my code. How far off am I?

    Code:
    #include <stdlib.h>
    #include <string>
    #include <fstream>
    #include <iostream>
    using namespace std;
    //#include <cstdlib>
    //#include <cstring>
    
    class UserSettings
    {
    private:	
    	char username[30];
    	char lastdatecontacted[10];
    	char emailaddr1[50];
    	char emailaddr2[50];
    
    	void GetUserSettings(char currentuser[30])
    		{
    		ifstream fin;
    		fin.open("config.txt", ios::in);    
     		if(fin.fail())
    			{
    			cout << "Error: Unable to open file.\n";
    			return 0;
    			}
    		char detailline[140];
    		while(!fin.fail() && !fin.eof())
    			{
    			fin.getline(detailline, 11, '\n');
    			if (detailline == currentuser)
    				{
    				//hmmm, is substring 0 or 1 based?
    				username = detailline.substring(1,30);
    				lastdatecontacted = detailline.substring(31,10);
    				emailaddr1 = detailline.substring(41,50);
    				emailaddr2 = detailline.substring(91,50);
    				}
    			}
    		fin.close();	
    		}
    
    public:
    	UserSetting(char currentuser) : whatgoeshere(andhere)
    	{
    	//pull from file???
    	GetUserSettings(currentuser)
    	}
    
    	//member functions which do something with toxin
    	int GetLastDateContacted() //return the value of toxin
    	{
    		return lastdatecontacted;
    	}
    
    	void AlterLastDateContacted(int Date_Contacted) //set toxin to Toxin_Value
    	{
    		lastdatecontacted = Date_Contacted;
    	}
    };
    
    void main()
    	{
    	char LastDateContact[10];
    	LastDateContact = UserSettings("bobsmith").GetLastDateContacted;
    	}

  2. #2
    Toaster Zach L.'s Avatar
    Join Date
    Aug 2001
    Posts
    2,686
    Sounds like you need some static member data; that is, member data that is shared between all instances of the class. I recommend digging this info up in whatever book or tutorials you are using, but I'll provide a brief example of the idea:

    Code:
    // In the header file...
    struct UserData
    {
       std::string name;
       // ... pertinent data ...
    };
    
    class UserSettings
    {
       public:
          std::string getName();
       private:
          void readData(std::string);
          static UserData* m_data;
    };
    
    // In the source file...
    // Make sure that all static members are properly declared/defined.
    UserData* UserSettings::m_data = 0;
    
    std::string UserSettings::getName()
    {
       if(!m_data)
          readData("the_file.txt");
       return m_data->name;
    }
    I've assumed here that readData will read in the appropriate data, and allocate/initialize m_data, and that it will do something such as throw an exception on failure (hence, no error checking, pass the exception up).

    *edit*
    And, you really should clean up the pointer. There are a couple of ways to go about it. If this is the correct problem I'm answering, I'll tell you more; otherwise, just read below.

    Cheers
    Last edited by Zach L.; 08-17-2005 at 04:47 PM.
    The word rap as it applies to music is the result of a peculiar phonological rule which has stripped the word of its initial voiceless velar stop.

  3. #3
    Toaster Zach L.'s Avatar
    Join Date
    Aug 2001
    Posts
    2,686
    Okay... I think I answered the wrong question... You want this on a per class basis, I am guessing. This makes it a little easier, but you can use the same strategy, you just don't need anything static. That is, have a little struct that contains the info, and keep a pointer to it. If the pointer is NULL, then read the data from the file, and then just grab the data from the struct. Also, you could just use a boolean to keep track of whether or not things have been initialized. Personally, I prefer the first method.

    *edit*
    And main always returns an int.
    The word rap as it applies to music is the result of a peculiar phonological rule which has stripped the word of its initial voiceless velar stop.

  4. #4
    Registered User
    Join Date
    Oct 2001
    Posts
    2,934
    >Here's my code. How far off am I?
    Pretty far. For example, looking at just your main():

    >void main()
    main returns an int:
    int main()

    > char LastDateContact[10];
    An easier choice would be to use the string class versus a char array:
    string LastDateContact;

    > LastDateContact = UserSettings("bobsmith").GetLastDateContacted;
    I think it would be better to create an instance of the class, then call the function:
    UserSettings user("bobsmith");
    string LastDateContact = user.GetLastDataContacted();

    And I would change all the char arrays in your class to strings.
    Last edited by swoopy; 08-17-2005 at 04:55 PM.

  5. #5
    Registered User
    Join Date
    Aug 2005
    Posts
    128
    Quote Originally Posted by Zach L.
    Okay... I think I answered the wrong question... You want this on a per class basis, I am guessing. This makes it a little easier, but you can use the same strategy, you just don't need anything static. That is, have a little struct that contains the info, and keep a pointer to it. If the pointer is NULL, then read the data from the file, and then just grab the data from the struct. Also, you could just use a boolean to keep track of whether or not things have been initialized. Personally, I prefer the first method.

    *edit*
    And main always returns an int.
    Both of your answers are much appreciated. I'll give this a try and see how it goes. I was reading through my book after I posted this that saw a little of where I was wrong but couldn't figure out how to get what I needed. Thanks again.

  6. #6
    Toaster Zach L.'s Avatar
    Join Date
    Aug 2001
    Posts
    2,686
    Here is a sample of the first solution I provided (fully compilable). It does not read in the values until is needs to (and I have it set to read from cin), and the settings class itself is a singleton.
    Code:
    #include <iostream>
    #include <string>
    
    struct read_exception { };
    
    class settings
    {
       public:
          static settings& instance();
       
          ~settings();
          
          std::string get_value() const;
          
       private:
          settings() { }
          
          // Declare away -- Declare but do not define; attempted use will generate
          // a linker error
          settings(const settings&);
       
          void read_data(std::istream& istr) const;
       
          struct data;
          static data* data_ptr;
          
          static settings instance_obj;
    };
    
    struct settings::data
    {
       std::string value;
    };
    
    settings settings::instance_obj;
    settings::data* settings::data_ptr = 0;
    
    settings& settings::instance()
    {
       return instance_obj;
    }
    
    settings::~settings()
    {
       std::cout << "cleaning up pointer ..." << std::endl;
       delete data_ptr;
       data_ptr = 0;
    }
    
    std::string settings::get_value() const
    {
       std::cout << "getting value ..." << std::endl;
       if(!data_ptr)
       {
          std::cout << "reading stream ..." << std::endl;
          read_data(std::cin);
       }
       return data_ptr->value;
    }
    
    void settings::read_data(std::istream& istr) const
    {
       // Fail if the stream is bad... perhaps do data checking too
       if(!istr)
          throw read_exception();
       
       if(!data_ptr)
       {
          std::cout << "initializing pointer ..." << std::endl;
          data_ptr = new data;
          std::getline(istr, data_ptr->value);
       }
    }
    
    int main()
    {
       settings& usr = settings::instance();
       std::cout << usr.get_value() << std::endl
                 << usr.get_value() << std::endl;
    }
    The word rap as it applies to music is the result of a peculiar phonological rule which has stripped the word of its initial voiceless velar stop.

  7. #7
    Registered User
    Join Date
    Aug 2005
    Posts
    128
    Quote Originally Posted by Zach L.
    Here is a sample of the first solution I provided (fully compilable). It does not read in the values until is needs to (and I have it set to read from cin), and the settings class itself is a singleton.
    I started to post a modified version of your first posting and saw you'd provided a GREAT sample code example.

    I'm now modifying the code and inserting comments so I can remember what everything does.

    I'm also running your raw code and trying to follow the logic.

    A few questions:
    1. What is "settings class itself is a singleton?"

    2. Also, what is instance and instance_obj. I understand an instance on an object, but I thought that was already called by settings& usr = settings::instance();

    better yet, tell me if this is right, regarding the above line...
    1. settings& indicates usr is a pointer to settings
    2. usr is set to an instance of settings.
    3. by why ::instance?

  8. #8
    Registered User
    Join Date
    Aug 2005
    Posts
    128
    Thanks to you, I've got a working version. Yes, I've got some rearranging of code to do for error checking and whatnot, but I ran a file through this and it works.

    Code:
    include <iostream>
    #include <fstream>
    #include <string>
    
    
    using namespace std;
    
    string trim(string const& source, char const* delims = " \t\r\n") {
    	string result(source);
    	string::size_type index = result.find_last_not_of(delims);
    	if(index != string::npos)
    		result.erase(++index);
    
    	index = result.find_first_not_of(delims);
    	if(index != string::npos)
    		result.erase(0, index);
    	else
    		result.erase();
    	return result;
    }
    
    struct read_exception { };
    
    class settings
    {
    	private:
    		settings() { }
          
    		// Declare away -- Declare but do not define; attempted use will generate
    		// a linker error
    		settings(const settings&);
    	
    		void read_data(string& thisuser) const;
       
    		struct data;
    		static data* data_ptr;
          
    		static settings instance_obj;  
    	public:
    
    	   static settings& instance();
    		//deconstruct
    		~settings();
    		//references publc functions stored elsewhere
    		//hmmm could they be incorporated into other classes?
    		string get_username(string thisuser) const;
    		string get_email(string thisuser) const;
    		string get_lastdatecontacted(string thisuser) const;
          
    
    };
    
    //PUBLIC CLASS STRUCT
    struct settings::data
    {
       //string value;
       string username;
       string email;
       string lastdatecontacted;
    };
    //END PUBLIC CLASS STRUCT
    
    settings settings::instance_obj;
    
    settings::data* settings::data_ptr = 0;
    
    //???
    settings& settings::instance()
    {
       return instance_obj;
    }
    
    //PUBLIC CLASS FUNCTIONS
    //deconstruct
    settings::~settings()
    {
       //cout << "cleaning up pointer ..." << endl;
       delete data_ptr;
       data_ptr = 0;
    }
    //end deconstruct
    
    //get values
    string settings::get_username(string thisuser) const
    {
       cout << "getting username ..." << endl;
       if(!data_ptr)
       {
          cout << "reading stream ..." << endl;
          read_data(thisuser);
       }
       return data_ptr->username;
    }
    string settings::get_email(string thisuser) const
    {
       cout << "getting email ..." << endl;
       if(!data_ptr)
       {
          cout << "reading stream ..." << endl;
          read_data(thisuser);
       }
       return data_ptr->email;
    }
    string settings::get_lastdatecontacted(string thisuser) const
    {
       cout << "getting last date contacted ..." << endl;
       if(!data_ptr)
       {
          cout << "reading stream ..." << endl;
          read_data(thisuser);
       }
       return data_ptr->lastdatecontacted;
    }
    //end get values
    
    //read_data
    void settings::read_data(string& thisuser) const
    {
       // Fail if the stream is bad... perhaps do data checking too
       if(!data_ptr)
       {
    		cout << "initializing pointer ..." << endl;
    		data_ptr = new data;
    		ifstream fin;
    		fin.open("test.txt", ios::in);    
     		if(fin.fail())
    			{
    			throw read_exception();
    			}
    		char detailline[140];
    		string sdetailline;
    		while(!fin.fail() && !fin.eof())
    			{
    			
    			fin.getline(detailline, 90, '\n');
    			sdetailline = detailline;
    			if (trim(sdetailline.substr(0,10)) == thisuser)
    				{
    				data_ptr->username = sdetailline.substr(10,10);
    				data_ptr->lastdatecontacted = sdetailline.substr(20,10);
    				data_ptr->email = sdetailline.substr(30,50);
    				}
    			}
    		fin.close();	
    
       }
    }
    //end read_data
    //END PUBLIC CLASS FUNCTIONS
    
    int main()
    {
       settings& usr = settings::instance();
       cout << usr.get_username("bob") << endl
                 << usr.get_lastdatecontacted("bob") << endl
    			 << usr.get_email("bob") << endl;
    
    	cin;
    
       return 1;
    }

  9. #9
    Toaster Zach L.'s Avatar
    Join Date
    Aug 2001
    Posts
    2,686
    Hmm... I managed to get a bit confused, apparently. The class, as it is, will not work exactly as you want it. Since the constructors are private, the only access to the sole instance of this class is through the "instance( )" function.

    What I think you want to do is use the same idea (the pointer to the struct), but without static members, and normally constructible. Granted, then multiple class instances can exist for a given username, and each one will read data if need be. There is a slighly more complicated version of the singleton pattern (the pattern I used to allow only one instance of the class) that can take care of this problem, but you really should understand the singleton pattern first. I am a bit exhausted, so I won't go into detail, but certainly you can find the information on Google.

    Let me know if you are interested in this solution (it involves registering each instance of the class).
    The word rap as it applies to music is the result of a peculiar phonological rule which has stripped the word of its initial voiceless velar stop.

  10. #10
    Registered User
    Join Date
    Aug 2005
    Posts
    128
    Thanks, I'll do a bit of googling first.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Getting a ancestor to save derived class info
    By Emeighty in forum C++ Programming
    Replies: 6
    Last Post: 02-01-2009, 12:55 AM
  2. How to parse the data and retrieve the data.
    By kane_a in forum C Programming
    Replies: 11
    Last Post: 12-08-2005, 09:11 AM
  3. my wndProc is out of scope
    By Raison in forum Windows Programming
    Replies: 35
    Last Post: 06-25-2004, 07:23 AM
  4. Difficulty superclassing EDIT window class
    By cDir in forum Windows Programming
    Replies: 7
    Last Post: 02-21-2002, 05:06 PM
  5. Retrieve specific data from text for edit
    By Unregistered in forum C Programming
    Replies: 2
    Last Post: 01-22-2002, 09:02 AM