C++ Implementation





C++ Implementation

The C++ implementation is composed of the following files:

  • CSVtoXMLBasic.cpp: the main routine for the C++ application

  • CSVRowReader.cpp: the class with parse and write methods

  • CSVRowReader.h: the header file for the CSVRowReader class

  • handleCOMError.cpp: the function that displays information about COM errors

  • handleParseError.cpp: the function that displays information about parsing errors

  • BlasterIncludes.h: the header file for the C++ code for including all standard headers and libraries, like importing MSXML

  • BlasterPrototypes.h: the header file with prototypes for handleCOMError and handleParseError

Similar to the discussion of the Java implementation, we'll focus below on the aspects that are unique to C++ and MSXML.

main in CSVToXMLBasic.cpp

Setting up the COM environment is nearly the same as discussed for XMLToCSVBasic.cpp in Chapter 2. There are two differences in the setup, followed by the calls to create the root Element and append it to the Document.

From CSVRowReader main
//  Set up DOM XML environment (dependent on implementation)
//  Create the Output XML Document (dependent on
//    implementation)
//  Create the COM DOM Document
hResult = spDocOutput.CreateInstance(
    __uuidof(DOMDocument40));

if FAILED(hResult)
{
  throw cCreateInstanceError;
}

//  Create the header processing instruction
//    and append it to the Document Node
spPIHeader = spDocOutput->createProcessingInstruction("xml",
  cHeaderText);
spDocOutput->appendChild(spPIHeader);

//  Root Element <- Call Document's createElement method,
//    with tagName of SimpleCSV
spEleRoot = spDocOutput->createElement("SimpleCSV");

//  Document <-  Call Document's appendChild to append Root
//    to Document
spDocOutput->appendChild(spEleRoot);

Note that we have to call only the CreateInstance method and not also the load method. Since we don't call the load method, we also don't have to check for or deal with parsing errors. In the Java implementation, Xerces's XMLSerializer class wrote the header line (formally known as the prolog) of the XML file for us. The following processing instruction line has the XML version and encoding:

<?xml version="1.0" encoding="UTF-8"?>

MSXML didn't do this for us, so we have to explicitly create it. In the strictest sense of the XML Recommendation, the XML prolog is not a processing instruction. However, the only way we can create the prolog with MSXML is to create it as a DOM Processing Instruction Node. The createProcessingInstruction method takes two arguments: (1) a target representing the entity or application for which the instruction is intended, and (2) the text of the instruction. In this case our target is xml and the text is composed of the version and encoding Attributes of the prolog. I've created a char * constant with the instruction text. The calls to create the root Element and append it to the Document come after appending the Processing Instruction header since we want the header to be the first thing in the instance document. These calls are almost identical to the Java calls. The createElement method specifies a _bstr_t for the tagName argument, but the compiler accepts a char * and does the conversion for us.

The other thing to point out in the C++ code is the save operation.

//  Save Output XML Document (dependent on implementation)
//  Save it using Document's save method
hResult = spDocOutput->save(cOutputXMLName);

if FAILED(hResult)
{
  throw cSaveError;
}

MSXML does a very nice, concise implementation of the DOM Level 3 save semantics. A save method is provided for the Document interface, with a single argument of the output. Various types of output are supported, including disk files and another Document object. The save method specifies that the output argument is a VARIANT, but the compiler accepts a file name passed as a char *. We again use a simple homegrown char exception as a way to gracefully exit the main routine's try block.

parse in CSVRowReader.cpp

Similar to the Java implementation, this is straight C++ without any XML involved. You can find the code on the book's Web site.

write in CSVRowReader.cpp

Let's look at how we create the Text Node and append it using MSXML from C++.

//  Text Node <- Call Document's createTextNode method
//    with text from ColumnArray[ColumnNumber]
//  MSXML createTextNode specifies a BSTR for the
//    argument. Visual C++ is usually happy to convert
//    a char * but wanted an explicit cast for this
//    array element.
IXMLDOMTextPtr spTexText = spDocOutput->createTextNode(
  (char *) cColumnArray[iColumnNumber]);

//  Column Element <- Call Column Element's appendChild
//    to add Text Node as child
spEleColumn->appendChild(spTexText);

Again, this is very similar to the Java implementation. A "gotcha" here, as noted in the code comments, is that createTextNode requires that the text argument be a BSTR type. The compiler is usually very happy to handle a char * for us, but in this case it wanted a specific cast as char *. cColumnArray is an array of char * pointers. We allocate memory for them when we read a new column and release it when we've created the Text Node. Visual C++ evidently couldn't handle that level of type conversion.


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows