August 1997

All set?

by Kent Reisdorph

As you probably know by now, the VCL that comes with C++Builder is written in Pascal. While this isn't a problem, the situation does raise certain issues for the C++Builder programmer--some features found in Pascal don't exist in C++, and vice versa. One Pascal feature that C++ doesn't have is the set. In this article, we'll explore sets and how C++Builder handles them. Let's begin with a little background information.

Sets in Pascal

In Pascal, a set is "a collection of like objects." Hmm... that definition doesn't say much, does it? An example that comes to mind is VCL's Font.Style property. This property can have one or more of the following values: fsBold, fsItalic, fsUnderline, or fsStrikeout. Let's detour for a moment and explain where those values come from.

Typically, the individual elements you use with a set come from an enumeration. For instance, the font styles we just mentioned come from the TFontStyle enumeration. The TFontStyles set, then, is created as a set of TFontStyle values. The VCL source declaring these objects is as follows:

TFontStyle = (fsBold, fsItalic, fsUnderline, fsStrikeOut);
TFontStyles = set of TFontStyle;
Most of the sets you'll use in VCL follow this same architecture--you have an enumeration that declares a set, which can contain values of that enumeration.

Typically, you'll turn the font's individual Style values on or off at design time. Sometimes, however, you need to set the font Style property at runtime. In that case, you'd use code like the following:

Font.Style := []; 
Font.Style := [fsBold, fsItalic];
The first line clears the Style set by assigning an empty set (designated by empty brackets) to the Style property. The next line adds the fsBold and fsItalic values to the set. When you use the font, your text will appear in bold italics. (Note that the first line of code in this example isn't really necessary. I included it to show you how to clear a set.)

Now, let's say that you want the font to be bold and underlined, but not italic. You need to remove the italic style and add the underline style, while leaving the bold style in the set:

Font.Style := Font.Style - [fsItalic];
Font.Style := Font.Style + [fsUnderline];
And that, my friends, is how you add elements to--and remove them from--a set in Pascal.

Besides changing set elements, you may want to see if a particular item is in a set. Let's say you want to know whether the font is currently bold. You can determine if the fsBold style is in the set as follows:

if fsBold in Font.Style then
Font.Style := Font.Style - [fsBold];
The in keyword returns true if the particular value is in the set and false if it isn't in the set.

Sets in C++

The C++ language includes no built-in support for sets. C++ programmers have typically used bitfields where Pascal programmers use sets. For example, the MessageBox() function has a flags parameter that specifies which buttons and icons will appear on the message box. The following example illustrates the use and manipulation of a bitfield:
// the initial bitfield with two styles
unsigned int flags = MB_OK | MB_ICONEXCLAMATION;
// remove the icon style
flags &= ~MB_ICONEXCLAMATION;
// add a help button to the message box
flags |= MB_HELP;
// show the message box
::MessageBox(Handle, "Test Message", 
"Message", flags);
This mechanism works fine, but it isn't very useful when you're dealing with VCL, because VCL uses Pascal sets. For this reason, Borland had to come up with a way to emulate sets in C++Builder. As an added benefit, the concept of sets is easier for beginning C++ programmers to understand than is the concept of bitfields. (C++Builder is all about rapid application development, and part of RAD is getting programmers up to speed as quickly as possible.)

The Set template

While there are several ways to solve this particular riddle in C++, Borland decided to implement sets in the form of a template class called, predictably, Set. Fortunately, you don't need to know the nitty-gritty details of templates to use the Set template.

Let's return to our earlier examples of how to add and remove elements from a set in Pascal:

{start with an empty set}
Font.Style := []; 
{add the bold and italic styles}
Font.Style := [fsBold, fsItalic];
{remove the italic style}
Font.Style := Font.Style - [fsItalic];
{add the underline style}
Font.Style := Font.Style + [fsUnderline];
Now, let's see how the corresponding code looks in C++Builder.
// start with an empty set
Font-$gtStyle.Clear(); 
// add the bold and italic styles
Font-$gtStyle << fsBold << fsItalic;
// remove the italic style
Font-$gtStyle $gt$gt fsItalic;
// add the underline style
Font-$gtStyle << fsUnderline;
First, notice that you can use the Clear() function--a member of the Set template--to remove all elements from the set. Next, notice how you can add elements to the set using the insertion operator (<<). If you have C++ experience, then you've seen similar syntax in the C++ streaming classes. The insertion operator adds the item to the right of the operator to the set; you can chain insertion operators to add several elements to a set at one time.

Finally, the extraction operator ($gt$gt) removes elements from the set. You can also chain extraction operators to remove several elements from the set simultaneously.

Who's in there?

In the section on Pascal sets, we talked about checking a set to see which elements it contains. We illustrated the technique with this code:
if fsBold in Font.Style then
Font.Style := Font.Style - [fsBold];
The code checks to see if the bold style is set and, if so, removes it. However, C++ doesn't have the in keyword. Instead, the C++ Set template has a function called Contains(), which serves the same purpose. The C++ equivalent of the previous code is as follows:
if (Font-$gtStyle.Contains(fsBold))
Font-$gtStyle $gt$gt $gt$gt;

Sets on the fly

Sometimes you need to create a set on the fly. The VCL MessageDlg() function provides a good example. (Note that this function isn't documented in the C++Builder help files.) This function expects a set of TMsgButtons as its third parameter; the set tells VCL which buttons to put in the dialog box.

Once again, let's look first at how to create the set in Pascal:

MessageDlg("An error has occurred...", 
mtError, [mbOK, mbCancel], 0);
Now, look at the C++ code for the same set:
MessageDlg("An error has occurred...", 
$lt$lt mbOK $lt$lt mbCancel, 0);
In this case, the Pascal code is a bit more elegant than the C++ implementation. You create the Pascal temporary set simply by using the square brackets with the elements you wish to include in the set. In C++, you create a temporary set, then add the elements using the insertion operator. That part of the C++ code is as follows:
TMsgDlgButtons() << mbOK << <&lte$gt$gt $gt$gtn
use this simple syntax any time you need a temporary set.

Conclusion

Sets are a way of life in VCL and in C++Builder programming. The C++Builder Set template is your route to VCL sets. The extraction and insertion operators, while odd-looking at first, provide a simple method for you to add and remove set elements.

Kent Reisdorph is a editor of the C++Builder Developer's Journal as well as director of systems and services at TurboPower Software Company, and a member of TeamB, Borland's volunteer online support group. He's the author of Teach Yourself C++Builder in 21 Days and Teach Yourself C++Builder in 14 Days. You can contact Kent at editor@bridgespublishing.com.