Q

C++ Templates

A  C++ templates have been part of the C++ language for over a decade, and are supported by all C++ compilers these days. Templates let you specify function parameter types and class types at compile time — a sort of static polymorphism. I'm not going to attempt to describe templates here; there are plenty of authoritative sources of information in printed and electronic form. For example …

C++ Templates - The Complete Guide The C++ Programming Language
C++ Templates - The Complete Guide The C++ Programming Language

Microsoft Visual C++ version 6 and subsequent versions support C++ templates.

Boost

Boost is the group of C++ heroes who add enormous value to the C++ Standard Library. The code they develop sometimes makes its way into the Standard Library. Read more about Boost.

Simple Template Example

A  Here's a simple example of C++ template programming applied to a common MDL task.

Load & Save User Preferences

In a MicroStation application, we often want to restore and save user preferences as a user loads and unloads our application. User preferences are stored as binary data in the user's user preference file (UPF). A UPF is named after the user current workspace with a .upf file extension.

Our user preferences, which are plain old data (POD), are typically stored in a struct. When we save preferences, we write the data from our struct to the UPF, using the MDL resource manager API. When we load preferences, we copy the data from the UPF to our struct, using the MDL resource manager API.

We have to be sure that MicroStation's resource manager knows the size of the chunk of data we plan to save, and that it copies to us the right amount of data when we load our preferences. We also need to take care the first time we save data to the UPF. The MDL code is simple, but requires care because of those resource and memory-management issues.

Traditional MDL Load/Save Preferences

Here's the traditional, hand-crafted way, of loading our preferences …

bool	LoadUserPreferences	(YOUR_STRUCT*	data)
{
  bool			rc		(false);
  RscFileHandle   userPrefsH = 0;
  YOUR_STRUCT*	pUserPrefs = NULL;

  if (SUCCESS == mdlDialog_userPrefFileOpen (&userPrefsH, RSC_READ))
  {
    pUserPrefs = (YOUR_STRUCT*)mdlResource_loadByAlias (userPrefsH, YourResourceTypeId, YourResourceId, mdlSystem_getCurrTaskID ()));
    if (NULL == pUserPrefs)
    {
	  //	Deal with errors
    }
    else
    {  //	Copy data to our struct
      *data = *pUserPrefs;
      mdlResource_free (pUserPrefs);
      rc = true;
    }
  	mdlResource_closeFile (userPrefsH);
  }
  return rc;
}

Note the various pointers and casts to the YOUR_STRUCT data type. This lends itself to template programming.

Templated Class used to Load/Save Preferences

Here is the same function as a member of a CUserPreferences class …

template <typename T>
bool	CUserPreferences<T>::Load	(T*	data)
{
  bool            rc        (false);
  RscFileHandle   userPrefsH = 0;
  T*              pUserPrefs = NULL;

  if (SUCCESS == mdlDialog_userPrefFileOpen (&userPrefsH, RSC_READ))
  {
    pUserPrefs = (T*)mdlResource_loadByAlias (userPrefsH, typeId_, rscId_, const_cast<char*>(alias_.c_str ()));
    if (NULL == pUserPrefs)
    {
       //	Deal with errors
    }
    else
    {  //	Copy data to our struct
       *data = *pUserPrefs;
       mdlResource_free (pUserPrefs);
       rc = true;
    }
    mdlResource_closeFile (userPrefsH);
    return rc;
  }
}

Note that we don't reference our own struct any more: its type is substituted by the template parameter T. We're also using class member variables to pass the resource ID, type ID, and task ID.

The CUserPreferences<T> class is simple …

template <typename T>
class CUserPreferences
{
  UInt32        typeId_;    //	Resource type ID from your header
  UInt32        rscId_;     //	Resource ID from your header
  std::string   alias_;     //	Usually app. name

public:
  //////////////////////////////////////////////////////////////////
  //	Implementation
  bool          Save (T const*	data);
  bool          Load (T*	data);
  //////////////////////////////////////////////////////////////////
  //	Construction
  CUserPreferences  (UInt32        typeId,
                     UInt32        rscId,
                     const char*   alias  = mdlSystem_getCurrTaskID ());

private:
  //	Copying and assignment not allowed
  CUserPreferences   (const CUserPreferences<T>&            CopyConstructorNotUsed);
  CUserPreferences&   operator=(const CUserPreferences<T>&  AssignmentNotAllowed);
};

Templated Class Usage

Instantiate a variable of this class, passing YOUR_STRUCT as the template parameter. Your resource type and ID should be in one of your header files …

	CUserPreferences<YOUR_STRUCT>	userprefs (yourRscTypeId, yourRscId);

Now you can load and save preferences easily and type-safely …

	YOUR_STRUCT	prefs;
	userprefs.Load (&prefs);
	…
	userprefs.Save (&prefs);

The CUserPreferences class and its definition are provided in a single file UserPreferences.hpp (file is in a ZIP archive). Add this to your C++ project, and #include it in an implementation (.cpp) file.