Lesson 3: General Programming Practices

Overview

This lesson covers some general topics of interest to developers working with the GeoCalc tool kit.  These topics include:

 

Memory Management

As of GeoCalc 6.3, the memory management scheme has changed slightly.  In previous versions, one would use the "delete" keyword to free the memory associated with an object that resides on the heap.  Now, there is a new object called Disposal, which takes care of freeing memory.  When you no longer need to use an object that resides on the heap, you call the static Disposal::Dispose method, and it will free the memory associated with that object.

Let's look at an example of how to use the Dispose method.  We will create some objects on the heap, and then free the memory associated with them:

DataSource * dataSource = DataSource::CreateDataSource();

dataSource->LoadFile(L"C:\\bmg\\GeoCalcPBW\\data\\geocalc.xml",false,L"c:\\bmg\\GeoCalcPBW\\data\custom.xml");

GeodeticCoordSys * cs = dataSource->GetGeodeticCoordSys(L"BMG", L"WGS84_coordinate_system");

wchar_t * wktString = dataSource->ExportCoordSysToString(*cs);

Disposal::Dispose(cs);

Disposal::Dispose(wktString);

Disposal::Dispose(dataSource);

The Dispose method should be used with the following objects:

 

Strings

In GeoCalc, strings are represented by wide character strings, or wchar_t*s.  The wchar_t data type is defined like this in GeoCalc:

typedef unsigned short wchar_t;

In Microsoft Visual C++ version 7 and higher, there is a compiler option that allows wchar_t to be defined as a native type.  Projects that use GeoCalc must not use this compiler option.  See Lesson 2 for instructions on how to set this compiler option.  When this compiler option is turned off, it is still possible to use the native wchar_t type by using the __wchar_t keyword.

GeoCalc provides the header file TConvert.h, which defines two objects used to make copies of char*s and wchar_t*s and to convert between these two types.  The _tochar object is used to produce a char* that is a copy of an existing char* or wchar_t*.  The _towchar object is used to produce a wchar_t* that is a copy of an existing char* or wchar_t*.  For example:

char * aString = "This is a string";

wchar_t * aWideString = _towchar(aString, false);

char * anotherString = _tochar(aWideString, false);

In order to express a wide character string literal, one can use the "L" macro.  For example:

wchar_t * aWideString = L"This is a wide string";

 

Identifiers

All Serializable objects that are stored in the GeoCalc DataSource must have at least one identifier, and many objects have multiple identifiers.  An identifier, combined with an object type, is enough information to uniquely identify any object in the DataSource.  An identifier consists of a pair of strings referred to as the issuer and the code.  For example, there is a ProjectedCoordSys defined in geocalc.xml that has an identifier where issuer = "BMG" and code = "UTM-19N".  This object is uniquely identified in the DataSource by this information:

Object Type:

ProjectedCoordSys

Issuer:

BMG

Code:

UTM-19N

The issuer of an identifier is the source that provided the definition for the object.  Many objects in the supplied data source files have an identifier where the issuer is equal to "BMG", which indicates that Blue Marble Geographics has provided the definition.  Some other objects have an identifier where the issuer is equal to "EPSG", which indicates that the definition came from the EPSG database.  All objects in the GeoCalc DataSource must have an identifier where the issuer is equal to "GC".

The code of an identifier is a value that distinguishes an object from other objects of the same type defined by the same issuer.

The identifiers for a Serializable object are stored in the IdentifierCollection that can be accessed through the Serializable::get_Identifiers method.  The IdentifierCollection returned by this method provides the functionality to get the identifiers and also to add, remove, and change the identifiers.

 

Exception Handling

When an unexpected or problematic condition occurs within GeoCalc during runtime, a GeoCalcException will be thrown.  When an exception is thrown by some method, the exception will travel down the call stack until it is either caught by a try-catch statement or it reaches the bottom of the call stack.  If an exception reaches the bottom of the call stack without being caught, it will cause the application to crash.  Therefore, it is a good practice to catch all exceptions.

The methods on the GeoCalcException object will provide some information about why the exception was thrown.  The most useful of these is the get_ErrorCode method, which returns a member of the GeoCalcException::Code enumeration.  This value will indicate the general nature of the condition that caused the exception to be thrown.  The get_FullMessage method can then be used to get a string that gives a verbose description of the exception.

For example, the DataSource::GetProjectedCoordSys method will throw a GeoCalcException if the specified identifier does not exist in the DataSource.  Here is an example of how to catch this exception:

DataSource * dataSource = 0;

dataSource = DataSource::CreateDataSource();

dataSource->LoadFile(L"C:\\bmg\\GeoCalcPBW\\data\\geocalc.xml", false, L"c:\\bmg\\GeoCalcPBW\\data\custom.xml");

 

ProjectedCoordSys * pcs = 0;

try

{

pcs = dataSource->GetProjectedCoordSys(L"NOT_A_VALID_ISSUER", L"NOT_A_VALID_CODE");

}

catch(GeoCalcException & ex)

{

if(ex.get_ErrorCode() == GeoCalcException::Code::IdentifierNotFound)

{

const wchar_t * desc = ex.get_FullMessage();

MessageBox(0, _tochar(desc).c_str(), "GeoCalcException", 0);

}

}

 

if(pcs) Disposal::Dispose(pcs);

if(dataSource) Disposal::Dispose(dataSource);