You are here:

C++/Native C++ XML

Advertisement


Question
Hello Ralph!

I'm trying to develop a class that needs to store some informations about paths to some data. I was wondering what would be the best solution for this. I need to store something like a table with 2 columns of strings and some rows. Something like:
TEMP_DATA C:\temp\
MY_DATA C:\my_folder\
etc.

What about to store it in XML file? But, I'm not able to handle XML with Native C++ in VS. I was looking for some classes, but I can't find anything useful. Is there something in Native C++ to write/read XML files? If not, what could be the other good solution to handle my problem? I'm not sure if binary data file or pure text file are ok for this.

Thank you a lot for your answer and for your time!

Peter.

Answer
Hello Peter,

You seem to be asking at least two separate questions: how to represent your data in memory using C++ classes and how to represent your data persistently in files.

It seems that you are mostly concerned with the storage - sorry if I guess wrong.

So, XML and C++ - you are correct in that (ISO) C++ standard library has no such support. In fact it has no support for any specific data formats other than formatting of basic data types as characters. That is up to the developer to decide. However there are several libraries and components that support XML for C++ - mostly from the parsing (reading) side, such as Xerces C++ Parser (http://xerces.apache.org/xerces-c/), Expat (http://expat.sourceforge.net/), libxml (http://xmlsoft.org/) or even the Microsoft XML COM component (as you mention VS - which I take to mean Microsoft Visual Studio) (http://msdn.microsoft.com/library/ms763742.aspx). You might like to also look at Arabica (http://www.jezuk.co.uk/cgi-bin/view/arabica).

However it looks to me like you need to just store a name coupled with a path, so although you could use an XML format to store the information doing so might be a little like taking a sledgehammer to crack a nut. Your question does not include enough detail for me to say if even this complexity is required - if you only have a few known paths then maybe just the paths need to be stored and read into their respective objects (possibly as specific members of some class?).

Assuming however that what you are trying to achieve is a set of key, value pairs so that when the file is read and processed you can ask for path "TEMP_DATA" by name and receive the associated path then what you require in your program is a map (or dictionary). The term map is more relevant for C++ as the standard library _does_ include a map type - in fact it is a generic map class template.

So internally you require a class that contains a map. You could use a naked map but that would probably expose too much detail and would probably be easier to use if wrapped in a class that provided just the operations required. The basic operations would such things as add, get and set values to the collection of named paths. An implementation might look like so:

   // File pathvariable.h (for example)

   #include <string>   // for std::string
   #include <map>      // for std::map
   #include <utility>  // for std::pair

   class PathVariables
   {
       typedef std::map<std::string, std::string>  PathVarMap;

   public:
       bool Add( std::string const & var, std::string const & pathValue )
       {
       // insert returns a pair containing the iterator position and
       // a bool value indicating whether the insertion was
       // successful - it fails if the map already contains an entry
       // for the key in question.
         return map_.insert( std::make_pair(var, pathValue) ).second;
       }

       bool Get( std::string const & var, std::string & pathValue ) const
       {
         PathVarMap::const_iterator pos = map_.find(var);
         bool found( pos != map_.end() );
         if ( found )
         {
         pathValue = pos->second;
         }
         return found;
       }

       bool Set( std::string const & var, std::string const & pathValue )
       {
         PathVarMap::iterator pos = map_.find(var);
         bool found( pos != map_.end() );
         if ( found )
         {
         pos->second = pathValue;
         }
         return found;
       }

       void SetAndAdd( std::string const & var, std::string const & pathValue  )
       {
         map_[ var ] = pathValue;
       }

   private:
       PathVarMap map_;
   };

[note: all code shown here is for example use only and therefore is not up to production quality. It may contain typos etc. - for which  I apologise]

The class PathVariables wraps a std::map of string variable name keys and string path name values. The operations are implemented in terms of the interface to a this map. They are:

   Add         :   Adds a new path variable to the collection. Fails if a variable of that name
         already exists.
   Get         :   Retrieves the path value for a given variable name. Fails if the variable
         cannot be found.
   Set         :   Changes the value of an existing variable. Fails if the variable cannot be
         found.
   SetAndAdd   :   A combination of Set and Add operations. If the variable exists then sets its
         path value to the provided value. If the variable does not exist then adds it
         with a value of the provided path value.
         
Failure is indicated by a false return from Add, Get and Set. Note that un-checked for errors may cause exceptions to be raised. If you are unfamiliar with std::map, std::string, std::pair etc. then please refer to a decent C++ standard library reference such as "The C++ Standard Library A Tutorial and Reference" by Nicolai M. Josuttis. There should be some information in the Microsoft Visual C++ documentation in the MSDN library, although it is more likely to be of a brief reference nature.

Of course it would also be nice if we could write and read a set of PathVariables to or from a file (or other place). So we could add Write and Read member functions:

   class PathVariables
   {

   // ...

   public:

   // ...

       int  Write( BasePathVarWriter & writer );
       int  Read( BasePathVarReader & reader );

   // ...

   };

The Read and Write operations take an object to read or write values to where ever, however. These objects are passed as references to a base interface type - quite possibly an abstract base class. They return status information as an integer rather than a simple bool as often such I/O operations have a richer set of failure modes than the other operations of the PathVariables class. another option (with all the operations in fact) would be to return nothing (void) and handle errors via raising exception.

The idea is simple - to abstract how and where the values are read and written to. The basic operations of a reader or writer is to read or write one variable name, value pair. Thus the basic interface to BasePathVarWriter would be something like:

   class BasePathVarWriter
   {
   public:
       virtual int Write( std::string const & var, std::string const & pathValue ) = 0;
       
       virtual ~BasePathVarWriter() {}
   };

And similarly that of BasePathVarReader:

   class BasePathVarReader
   {
   public:
       virtual int Read( std::string & var, std::string & pathValue ) = 0;
       
       virtual ~BasePathVarReader() {}
   };

The way that such concrete objects of the BasePathVarWriter type are used by PathVariables::Write would be to iterate through all items in the map passing the variable name and the path value to the object's Write operation until all items have been written or an error occurs. It could look like so:

   int  PathVariables::Write( BasePathVarWriter & writer )
   {
       PathVarMap::iterator it(map_.begin());
       PathVarMap::iterator finish( map_.end() );
       int status( StatusOK );
       for (; it!=finish && StatusOK==status; ++it )
       {
         status = writer.Write( it->first, it->second );
       }

       return status;
   }

Objects of BasePathVarReader are used by PathVariables::Read by reading values from the reader and adding them to the PathVariables::map_ member until either an error or end of data indicator is returned (e.g. an end of file indicator). Note that you can decide what to do about repeated reads of the same variable - should second and subsequent values be an error, ignored or replace the previous value. The first and last of these are handled by Add and SetAndAdd. Ignoring duplicates would take a bit of extra logic. One way to do this, using Add, could look like so:

   int  PathVariables::Read( BasePathVarReader & reader )
   {
       int status( StatusOK );

       while ( StatusOK==status )
       {
         std::string var;
         std::string pathValue;
         status = reader.Read( var, pathValue );
         if ( StatusOK==status )
         {
         Add( var, pathValue );
         }
       }

       return status;
   }

Here is an example class definition for a simple text writer class:

   // simplepathvarwriter.h

   #include "pathvariable.h"
   #include <fstream>      // for std::ofstream

   class SimplePathVarTextWriter : public BasePathVarWriter
   {
   public:
       bool    Open( std::string const & pathName )
         {
         stream_.open( pathName.c_str() );
         return !!stream_;
         }
         
       void    Close()
         {
         stream_.close();
         }
         
       ~SimplePathVarTextWriter();
      
       virtual int Write( std::string const & var, std::string const & pathValue );
         

   private: // data
       std::ofstream          stream_;
   };

   // simplepathvarwriter.cpp

   #include "simplepathvarwriter.h"
   
   SimplePathVarTextWriter::~SimplePathVarTextWriter()
   {
       Close();
   }
   
   int SimplePathVarTextWriter::Write( std::string const & var, std::string const & pathValue )
   {
       if ( stream_.good() )
       {
         stream_ << var << '\t' << pathValue << "\n";
       }
       
       return stream_.good() ? StatusOK : StatusWriteError;
   }
   

Here is an example class definition for a simple text reader class:

   // simplepathvarreader.h

   #include "pathvariable.h"
   #include <fstream>      // for std::ifstream

   class SimplePathVarTextReader : public BasePathVarReader
   {
   public:
       bool    Open( std::string const & pathName )
         {
         stream_.open( pathName.c_str() );
         return !!stream_;
         }
         
       void    Close()
         {
         stream_.close();
         }
         
       ~SimplePathVarTextReader();
      
       virtual int Read( std::string & var, std::string & pathValue );
         
   private: // data
       std::ifstream          stream_;
   };

   // simplepathvarreader.cpp
   #include "simplepathvarreader.h"
   #include <string> // for std::getline

   SimplePathVarTextReader::~SimplePathVarTextReader()
   {
       Close();
   }
   
   int SimplePathVarTextReader::Read( std::string & var, std::string & pathValue )
   {
       if ( stream_.good() )
       {
         stream_ >> var;
         
         stream_.ignore(); // '\t' separator - fragile code! Rework for non-example code.         
         
         std::getline( stream_, pathValue );
       }
       
       return stream_.good() ? StatusOK    :
         stream_.eof()  ? StatusEOF   : StatusReadError;
   }
   
OK so the idea for these simple writer and reader types is just to wrap file streams for writing and reading. It is the creator of SimplePathVarTextWriter and SimplePathVarTextReader objects' responsibility to open and close the files the use - although the files are closed when the objects are destroyed. As can be seen the SimplePathVarTextXXX classes derive from the BasePathVarXXX abstract base classes and override and implement the Writer or Read pure virtual functions.

The writing and reading are performed as simply as possible. Write just writes the variable name string followed by a tab character and then the path string value followed by a newline.

The Read function is a little more complex and should be more so as it is fragile at the moment. It first reads the variable name string (this implies the variable name has no whitespace characters in it), then it ignores the following tab character - it is here that the logic is fragile as it assumes the file is laid out exactly as written by SimplePathVarTextWriter::Write which may well be the case - but does not cater for human manual editing where additional spaces and/or tab characters might be used - however it is good enough for this example. Finally the rest of the line is read into the pathValue string using the non-member std::getline function declared in <string>. The error checking could also be more robust here.

The error value returned by Write and Read are made up and I have not shown them defined anywhere- again this is only example code. One way to define them would be as an enum:

   enum StatusTtype { StatusOK, StatusEOF, StatusWriteError, StatusReadError };

In which case you might like to consider using the enum type as the status type instead of int. If you think such types may change then use a typedef alias for the status type and then you only have to redefine the type alias. If such types may change for different uses of PathVariables and reader and writer types then consider parameterising the classes or functions using templates.

The scheme above, when put together (and any typos have been removed - much of the code has in fact been compiled using Visual C++ 2005 so hopefully there are not too many) should allow you to read (and write) files of the form:

   TEMP_DATA   C:\temp\
   MY_DATA C:\my_folder\

Where the gap between the variable names and the path values is a tab character.

We could create such a file like so (with no error checking for brevity):

   char const * PathVarFileName("file_of_path_variables.dat");
   char const * TempDataPathVar("TEMP_DATA");
   char const * UserDataPathVar("MY_DATA");

   // ...

   PathVariables  paths;

   paths.Add(TempDataPathVar, "C:\\temp\\");
   paths.Add(UserDataPathVar, "C:\\my_folder\\");

   SimplePathVarTextWriter writer;

   writer.Open(PathVarFileName;

   paths.Write(writer);


Then we could read the path variable and access the paths by name like so (again less error checking for brevity):

   PathVariables  paths;
   SimplePathVarTextReader reader;

   reader.Open(PathVarFileName);

   paths.Read(reader);

   std::string tempFilePath;
   paths.Get(TempDataPathVar, tempFilePath);

   std::cout << "Variable " << TempDataPathVar << " has value " << tempFilePath << '\n';

Of course by writing different reader and writer classes you can support such data stored using different formats (e.g. XML <g>) and written and read using different I/O types - e.g. network sockets, memory (e.g. string stream classes) etc.

This of course is just one possible way to do it - and of course assumes this is in fact suitable for your problem which once again I do not have enough detail to be sure of.

In any case I hope al least some of this is of use.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.