What I'm trying to do should be pretty self-explanatory from the code. Unfortunately, the code is extensive. Thanks in advance to anyone who's willing to give it a look.
jake.settings.cpp
Code:
/******************************************************************************/
/* The Settings Class, by Jake Meiergerd
/* ~ C++ Implementation
/* AUTHOR: Jake Meiergerd
/* VERSION: 1.0.0.1
/*
/* FILE: jake.settings.h
/* ~ Settings Class Implementation File
/* ~ A Member of the jake library.
/*
/* A useful class that encapsulates storing data (settings) between executions
/* of a program. The class stores the data in a file within the same
/* directory as the program (by default), and provides intermediate
/* functions for reading from and writing to the file.
/* The format is fairly simple, from the coder's perspective. Each setting
/* stored has a name which only need by worried about by the coder,
/* specified by a string, and a block of straight, raw data, whose size
/* needs to be specificly argued by the coder. Once the setting block is in
/* place within the file, the coder can tell the Settings Object to go to
/* the beginning of a particular setting and can then begin reading and
/* writing upon the file.
/******************************************************************************/
#ifdef __cplusplus
// Module Header File
#include "jake.settings.h"
Settings::Settings(string filename)
{
cout << "CHECKPOINT: Constructing Settings object...\n";
// We first open the file in output mode only, to ensure that it exists. Also,
// Append mode needs ot be specified, otherwise the old contents of the file
// will be erased.
cout << "CHECKPOINT: Testing open of settings file...\n";
settings_file.open(filename.c_str(), ios_base::out | ios_base::app);
if(settings_file.is_open())
{
cout << "CHECKPOINT: Success.\n";
settings_file.close();
}
else
cout << "CHECKPOINT: Failure.\n";
// Open the file for real, in IN/OUT binary mode
cout << "CHECKPOINT: Opening settings file...\n";
settings_file.open(filename.c_str(),
ios_base::in | ios_base::out | ios_base::binary);
if(settings_file.is_open())
cout << "CHECKPOINT: Success.\n";
else
cout << "CHECKPOINT: Failure.\n";
}
Settings::Settings(char* filename)
{
cout << "CHECKPOINT: Constructing Settings object...\n";
// We first open the file in output mode only, to ensure that it exists. Also,
// Append mode needs ot be specified, otherwise the old contents of the file
// will be erased.
cout << "CHECKPOINT: Testing open of settings file...\n";
settings_file.open(filename, ios_base::out | ios_base::app);
if(settings_file.is_open())
{
cout << "CHECKPOINT: Success.\n";
settings_file.close();
}
else
cout << "CHECKPOINT: Failure.\n";
// Open the file for real, in IN/OUT binary mode
cout << "CHECKPOINT: Opening settings file...\n";
settings_file.open(filename,
ios_base::in | ios_base::out | ios_base::binary);
if(settings_file.is_open())
cout << "CHECKPOINT: Success.\n";
else
cout << "CHECKPOINT: Failure.\n";
}
Settings::~Settings(void)
{
cout << "CHECKPOINT: Destroying Settings object...\n";
// Make sure the file is open, then close it.
cout << "CHECKPOINT: Closing settings file...\n";
if(settings_file.is_open())
settings_file.close();
if(!settings_file.is_open())
cout << "CHECKPOINT: Success.\n";
else
cout << "CHECKPOINT: Failure.\n";
}
bool Settings::GotoSetting(string setting_name)
{
cout << "CHECKPOINT: Executing GotoSetting()...\n";
// Make sure the file is open...
if(!settings_file.is_open())
{
cout << "CHECKPOINT: ERROR: File not open. Exiting.\n";
return false;
}
// Go to the beginning of the file.
cout << "CHECKPOINT: Moving to beginning of file...\n";
settings_file.seekg(0);
if(!settings_file.fail())
cout << "CHECKPOINT: Success.\n";
else
cout << "CHECKPOINT: Failure.\n";
// Start at the beginning of setting_name
uint name_index = 0;
// For logic progression purposes...
bool matched_name = false;
uint null_count = 0;
cout << "CHECKPOINT: Entering Search loop...\n";
while(1)
{
// As long as we don't reach the end of the file...
settings_file.peek();
if(settings_file.eof())
{
cout << "CHECKPOINT: End of file.\n";
break;
}
// If we've already matched the entire name
if(matched_name)
{
cout << "CHECKPOINT: Setting Name Matched.\n";
// Record another Null-Terminator, if the next character is one.
if(settings_file.get() == 0)
{
++null_count;
cout << "CHECKPOINT: Null-Terminator #" << null_count << ".\n";
}
// If we didn't get a Null-Terminator, we need to start over
else
{
cout << "CHECKPOINT: No Null-Terminator. Resetting loop.\n";
matched_name = false;
null_count = 0;
}
// If we found two Null-Terminators after the name, we found it
if(null_count == 2)
{
cout << "CHECKPOINT: Second Null-Terminator.\n";
cout << "CHECKPOINT: Setting found!\n";
// Synchronize the input position to the output position, which is the
// beginning of the setting's data
settings_file.seekp(settings_file.tellg());
// Return Success
return true;
}
}
else
{
// If we matched a character within the name
if(settings_file.get() == setting_name[name_index])
{
cout << "CHECKPOINT: setting_name[" << name_index << "] Matched.\n";
// If it was the final character of the name, indicate that we've
// matched the entire name
if(name_index == (setting_name.size() - 1))
{
cout << "CHECKPOINT: Full Name Matched.\n";
matched_name = true;
}
// If we haven't matched the entire name yet, go to the next character
// of the name
else
++name_index;
}
// If the current character didn't match, start over at the beginning of
// setting_name.
else
{
cout << "CHECKPOINT: No character match. Resetting loop.\n";
name_index = 0;
}
}
}
// If we made it this far, that means the loop ran through the entire file,
// and didn't find what we were looking for. So, return false
cout << "CHECKPOINT: Failure to find Setting.\n";
return false;
}
bool Settings::GotoSetting(char* setting_name)
{
// Make a C++ style-string out of the setting's name
string string_setting_name = setting_name;
// Pass the work off to the other version of GotoSetting()
return GotoSetting(string_setting_name);
}
bool Settings::AddSetting(string setting_name, uint size)
{
cout << "CHECKPOINT: Executing AddSetting()...\n";
// If the file isn't open, we can't do anything
if(!settings_file.is_open())
{
cout << "CHECKPOINT: ERROR: File not open. Exiting.\n";
return false;
}
// Make sure there isn't already a setting with the given name
if(GotoSetting(setting_name))
{
cout << "CHECKPOINT: Setting " << setting_name << " already exists.\n";
return false;
}
// Go to the end of the file
cout << "CHECKPOINT: Going to end of file...\n";
settings_file.seekp(0, ios_base::end);
if(!settings_file.fail())
cout << "CHECKPOINT: Success.\n";
else
cout << "CHECKPOINT: Failure.\n";
cout << "CHECKPOINT: Checking file positions...\n";
cout << "CHECKPOINT: Put pointer = " << settings_file.tellp() << "\n";
cout << "CHECKPOINT: Get pointer = " << settings_file.tellg() << "\n";
if(!settings_file.fail())
cout << "CHECKPOINT: Success.\n";
else
cout << "CHECKPOINT: Failure.\n";
// Add the prefixer, which is the setting's name, followed by two
// Null-Terminators.
cout << "CHECKPOINT: Adding prefixer for setting " << setting_name << "...\n";
settings_file << setting_name << (byte)0 << (byte)0;
if(!settings_file.fail())
cout << "CHECKPOINT: Success.\n";
else
cout << "CHECKPOINT: Failure.\n";
// Remember this position, because the coder expects this function to leave
// the file at the beginning of the setting's data block
cout << "CHECKPOINT: Retrieving file position...\n";
uint position = settings_file.tellp();
if(!settings_file.fail())
cout << "CHECKPOINT: Success.\n";
else
cout << "CHECKPOINT: Failure.\n";
// Add [size] number of bytes to the file, making space for the setting's
// data.
cout << "CHECKPOINT: Adding empty filespace...\n";
for(uint i = 0; i < size; ++i)
settings_file << (byte)0;
if(!settings_file.fail())
cout << "CHECKPOINT: Success.\n";
else
cout << "CHECKPOINT: Failure.\n";
// Set the file's position to the one we stored, the beginning of the
// setting's data.
cout << "CHECKPOINT: Setting File positions...\n";
settings_file.seekp(position);
settings_file.seekg(position);
if(!settings_file.fail())
cout << "CHECKPOINT: Success.\n";
else
cout << "CHECKPOINT: Failure.\n";
// We made it here, so the setting was added.
return true;
}
bool Settings::AddSetting(char* setting_name, uint size)
{
// Make a C++ style-string out of the setting's name
string string_setting_name = setting_name;
// Pass the work off to the other version of AddSetting()
return AddSetting(string_setting_name, size);
}
#endif
jake.settings.h
Code:
/******************************************************************************/
/* The Settings Class, by Jake Meiergerd
/* ~ C++ Implementation
/* AUTHOR: Jake Meiergerd
/* VERSION: 1.0.0.1
/*
/* FILE: jake.settings.h
/* ~ Settings Class Header File
/* ~ A Member of the jake library.
/*
/* A useful class that encapsulates storing data (settings) between executions
/* of a program. The class stores the data in a file within the same
/* directory as the program (by default), and provides intermediate
/* functions for reading from and writing to the file.
/* The format is fairly simple, from the coder's perspective. Each setting
/* stored has a name which only need by worried about by the coder,
/* specified by a string, and a block of straight, raw data, whose size
/* needs to be specificly argued by the coder. Once the setting block is in
/* place within the file, the coder can tell the Settings Object to go to
/* the beginning of a particular setting and can then begin reading and
/* writing upon the file.
/******************************************************************************/
#ifndef JAKE_SETTINGS_H
#define JAKE_SETTINGS_H
#ifdef __cplusplus
// Global header for the library
#include "jake.lib.h"
/**************************************/
/********* Class Declaration **********/
/**************************************/
class Settings
{
public:
// FUNCTION: Settings() - Class Constructor
// ARGUMENTS: filename: The name of the file in which the settings data
// will be stored.
// PRE: None
// POST: The class is initialized for the given filename, and the
// file is created, if it didn't already exist.
// BRIEF: Initializes the Settings module for a given filename.
Settings(string filename);
Settings(char* filename);
// FUNCTION: ~Settings() - Class Destructor
// PRE: None
// POST: The class object is ready to be deleted.
// BRIEF: Basically, it just closes the fstream object.
~Settings(void);
// FUNCTION: GotoSetting()
// ARGUMENTS: setting_name: The name of the setting to search for
// RETURN: true: The function was successful.
// false: The setting could not be found: i.e. there is no
// setting named setting_name in the file, or the file
// wasn't open.
// PRE: None
// POST: The current position of the settings file is the beginning
// of the data block of the specified setting
// BRIEF: This function searches the settings file for a setting named
// setting_name and sets the current position of the file, for both
// reading and writing, to the beginning of the data block for that
// setting.
bool GotoSetting(string setting_name);
bool GotoSetting(char* setting_name);
// FUNCTION: AddSetting()
// ARGUMENTS: setting_name: The name of the setting you wish to add
// size: The number of bytes required for the setting's
// data
// RETURN: true: The function was successful
// false: The function failed: i.e. the settings file was not
// opened, or it already contains a setting with the
// given name.
// PRE: None
// POST: The settings file now contains a block of space for the new
// setting, and the current position within the file, for
// both reading and writing, is the beginning of the new
// setting's data block.
// BRIEF: This function will add a block of space to the settings file to
// store data for the setting with the given name. This new block will
// be prefixed with the setting's name and a double Null-Terminator
// so it can be found by GotoSetting.
bool AddSetting(string setting_name, uint size);
bool AddSetting(char* setting_name, uint size);
private:
// VAR: settings_file
// BRIEF: file in which the setting data is stored.
fstream settings_file;
};
#endif
#endif
test.cpp
Code:
#include "jake.lib.h"
int main(void)
{
cout << " ~ Testing the jake library, main Modules ~\n\n";
// Test the Settings Module
class Settings ProgramSettings("test_file.set");
ProgramSettings.AddSetting("TEST_SETTING", 16);
// ProgramSettings.AddSetting("TEST_SETTING_2", 32);
// End of Tests
cout << "\n\n";
cout << "Program Complete\n\n";
return 0;
}
All necessary #includes and namespace stuff are taken care of in "jake.lib.h". Program compiles and runs, and gives the following output, whether the file "test_file.set" exists previously or not.
Code:
-bash-3.2$ test.exe
~ Testing the jake library, main Modules ~
CHECKPOINT: Constructing Settings object...
CHECKPOINT: Testing open of settings file...
CHECKPOINT: Success.
CHECKPOINT: Opening settings file...
CHECKPOINT: Success.
CHECKPOINT: Executing AddSetting()...
CHECKPOINT: Executing GotoSetting()...
CHECKPOINT: Moving to beginning of file...
CHECKPOINT: Success.
CHECKPOINT: Entering Search loop...
CHECKPOINT: End of file.
CHECKPOINT: Failure to find Setting.
CHECKPOINT: Going to end of file...
CHECKPOINT: Success.
CHECKPOINT: Checking file positions...
CHECKPOINT: Put pointer = 0
CHECKPOINT: Get pointer = 0
CHECKPOINT: Success.
CHECKPOINT: Adding prefixer for setting TEST_SETTING...
CHECKPOINT: Failure.
CHECKPOINT: Retrieving file position...
CHECKPOINT: Failure.
CHECKPOINT: Adding empty filespace...
CHECKPOINT: Failure.
CHECKPOINT: Setting File positions...
CHECKPOINT: Failure.
Program Complete
CHECKPOINT: Destroying Settings object...
CHECKPOINT: Closing settings file...
CHECKPOINT: Success.