Q
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 |
Microsoft Visual C++ version 6 and subsequent versions support C++ templates.
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.
A Here's a simple example of C++ template programming applied to a common MDL task.
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.
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.
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);
};
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.