This function was used to convert doubles to strings for insertion into an XML document, which were eventually parsed in an XSLT by the XPath number() function. Most of the time it worked fine, but for really large numbers the number() function failed and return NaN. Why?
This is covered by any halfway-decent C++ book, but I believe it deserves reiteration: Use the RAII idiom. I don’t think I could explain RAII any better than HackCraft does in The RAII Programming Idiom.
Let me demonstrate how to use RAII with a semi-contrived example. Here’s the pre-RAII code:
In this case, the resource wrapped is HMODULE, the resource acquisition function is LoadLibrary(), and the resource release function is FreeLibrary(). Beware of resources which have multiple resource release functions, such as Win32’s HANDLE with FindClose() and CloseHandle(); for these cases you will typically have to write multiple RAII classes. I will call the wrapper class HModule. Here’s how its use will look:
This is a style issue, so there is no right or wrong, but I suggest using a const reference for an input-only paramater to a C++ function and a pointer for an input/output or output-only parameter. This makes it slightly more obvious that the parameter might be modified by the function.
ReturnType Function (
const ParamType& inputParam,
ParamType* inputOutputParam,
ParamType* outputParam
)
{
// ...
}
voidUser()
{
ParamType inputParam;
ParamType inputOutputParam;
ParamType outputParam;
// Note the &s -- this is a bit of a warning something
// might happen to inputOutputParam or outputParam...
ReturnType ret = Function
(
inputParam,
&inputOutputParam,
&outputParam
);
}
If my experience is typical, this is a very common construct:
1
2
3
4
5
6
7
8
9
10
11
12
13
ReturnType Function (
const std::vector<T>& container
)
{
typedef std::vector<T>::const_iterator iterator_t;
for (iterator_t iter = container.begin();
iter != container.end();
++iter)
{
// Work with *iter
}
}
The problem with this construct is that you have forced a container choice upon the user of your function. Slightly better, and basically your only choice when interoping with C, is this:
I’ve seen the following STL construct countless times:
1
2
3
4
std::vector<T> container;
for (int i =0; i < container.size(); ++i) {
// Work with container[i]
}
Unless otherwise necessary, it is better to use an STL iterator because it enables you to more easily change the underlying container. You can isolate the code changes required to one line by using typedef, as in:
Recently I’ve had to write a bit of code which communicates with Microsoft Excel using its object model. Here are a few things I have learned from this experience.
Interaction with the Excel object model seems to use some kind of inter-process communication with an Excel process that is started behind the scenes. If things are not shut down properly, this Excel process will continue to run indefinitely in the background. Be sure to periodically check the Task Manager for any runaway Excel processes — these typically indicate a bug in your code or incomplete shutdown (perhaps because you chose “Stop Debugging” from the debugger).
To give control of the running, hidden Excel process with which you are interacting to the user, use the following code:
1
2
3
4
Excel::_Application app;
// Create and work with app...
app.SetVisible(true);
app.SetUserControl(true);
Even if Excel is not visible to the user, Application::Quit() may pop up a hidden dialog asking if the user wants to save the changes that were made through the dialog box. Since the dialog is not visible, Excel will never shut down. To prevent this dialog, either set Application.DisplayAlerts to false or set Workbook.Saved to true for all modified workbooks. The former is preferred.
Each call using the Excel object model is very, very slow, probably as a result of the use of IPC. This means that the typical way one would think of interacting with cell values in Excel — iterating cell-by-cell within a set of nested for loops — is often too slow to be practical. Instead, I work in selections of nRows rows by nCols columns and use a two-dimensional SAFEARRAY. For example:
1
2
3
4
5
6
7
8
9
10
11
COleSafeArray rawData;
DWORD rawDataDimensions[2];
rawDataDimensions[0] = nRows;
rawDataDimensions[1] = nCols;
rawData.Create(VT_VARIANT, 2, rawDataDimensions);
// Populate the values of rawData...
// Select a range of size nRows x nCols
Excel::Range range = wksheet.GetRange(varUpperLeftCell, varLowerRightCell);
// Set the cells' values in one call to .SetValue()
// instead of setting individual cell values
range.SetValue(rawData);