C++Builder and the VCL - Part 02
Is to forget the right way And forget that the going is easy. This
program declares this string as an AnsiString,
and then passes it to a procedure that can break it down into a series of
words. These words are passed back one at a time and added into an array. The
array is then displayed in a series of one word lines in a memo control. The
program appears in Listings 3.1 and 3.2. Listing 3.1. The header for the
ChuangTzu program. //--------------------------------------------------------------------------
#ifndef MainH #define MainH //--------------------------------------------------------------------------
#include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> //--------------------------------------------------------------------------
class TChuangTzu { private: int FCount; AnsiString
FWords[40]; AnsiString
GetWords(int Index); void
SetWords(int Index, AnsiString S); public:
TChuangTzu(){ FCount = 0; } __property
AnsiString Words[int Index] = {read=GetWords, write=SetWords}; __property
int Count={read=FCount, write=FCount}; }; class TForm1 : public TForm { published: TButton
*Button1; TMemo
*Memo1; void
__fastcall Button1Click( TObject
*Sender); private: public: virtual
__fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------
extern TForm1 *Form1; //--------------------------------------------------------------------------
#endif Listing 3.2. The main module of
the ChuangTzu program. #include <vcl.h> #pragma hdrstop #include "Main.h" #include "codebox.h" #pragma resource "*.dfm" TForm1 *Form1; /////////////////////////////////////// // ChuangTzu ////////////////////////// /////////////////////////////////////// AnsiString TChuangTzu::GetWords(int i) { return
FWords[i]; } void TChuangTzu::SetWords(int i,const AnsiString s) { FCount++;
FWords[i]=s; } AnsiString StripWords(AnsiString &S, char Token)
{ AnsiString
TokenStr(Token); AnsiString
Temp1; AnsiString
Temp2 = S + ` `; Temp1 =
strtok(S.c_str(), TokenStr.c_str()); Temp2 = strrev(Temp2.c_str());
Temp2.SetLength(Temp2.Length() - (Temp1.Length() + 1)); S =
strrev(Temp2.c_str()); return
Temp1; } /////////////////////////////////////// // Form1 ////////////////////////////// /////////////////////////////////////// __fastcall TForm1::TForm1(TComponent* Owner) :
TForm(Owner) { } void __fastcall TForm1::Button1Click( TObject
*Sender) { TChuangTzu
C; AnsiString
S1; int i = 0; AnsiString S
= "Easy
is right. Begin right "; "And
you are easy. "
"Continue easy and you are right. " "The
right way to go easy " "Is
to forget the right way " "And
forget that the going is easy."; do { S1 = StripWords(S,
` `); C.Words[i]
= S1; i++; } while (S1
!= ""); for (i = 0;
i < C.Count; i++) {
Memo1->Lines->Add(C.Words[i]); } } The core
of this program is the ChuangTzu class definition: class TChuangTzu { private: int FCount; AnsiString
FWords[40]; AnsiString
GetWords(int Index); void
SetWords(int Index, AnsiString S); public:
TChuangTzu(){ FCount = 0; } __property
AnsiString Words[int Index] = {read=GetWords, write=SetWords}; __property int Count={read=FCount,
write=FCount}; }; AnsiString FWords[40]; You can
access the second member of this array with code that looks like this: FWords[1] = "Let some sad trumpeter stand on
the empty streets at dawn." AnsiString S = FWords[1]; The ChuangTzu object provides an array property that gives
you access to this private data. There is, of course, no reason why you can't
declare an array of AnsiStrings on their own without a
supporting object. However, I thought it might be helpful to see how you can
use an array in your program without breaking the rule about keeping the raw
data of your objects private. Here is
the declaration for the array property: __property AnsiString Words[int Index] =
{read=GetWords, write=SetWords}; As you
can see, it returns an AnsiString and can be indexed with an
integer value. (There is nothing to keep you from declaring array properties that are indexed with AnsiStrings!) Here is
the get method for the array: AnsiString TChuangTzu::GetWords(int i) { return
FWords[i]; } Notice
that it takes a single integer as a parameter and returns an AnsiString. Here is
the set method for the array: void TChuangTzu::SetWords(int i,const AnsiString s) { FCount++; FWords[i]=s;
} This
function takes both an integer and a string as parameters. In order to call
this method, you write ChuangTzu.Words[i] = "peace proclaims olives of
endless age." Here you
can see the integer that is passed to SetWords, as well as a quote from Shakespeare's 107 sonnet that is passed in
the second parameter. The FCount variable is used to track the
number of entries in the array. Note that properties provide an improvement
over get and set methods by letting you use simple array syntax. Here is
an example of how to declare a large AnsiString that extends over several lines: AnsiString S = "Easy
is right. Begin right "; "And
you are easy. "
"Continue easy and you are right. " "The
right way to go easy " "Is
to forget the right way " "And
forget that the going is easy."; The StripWords function is used to peel off words from this
quote one at a time, starting at the front: AnsiString StripWords(AnsiString &S, char Token)
{ AnsiString
TokenStr(Token); AnsiString
Temp1; AnsiString
Temp2 = S + ` `; Temp1 =
strtok(S.c_str(), TokenStr.c_str()); Temp2 =
strrev(Temp2.c_str());
Temp2.SetLength(Temp2.Length() - (Temp1.Length() + 1)); S =
strrev(Temp2.c_str()); return
Temp1; } There
are two interesting things about this function. The first shows how to
concatenate strings with the plus operator, and the second shows how to use standard
C string functions on an AnsiString. Notice
the bit of syntax that adds a space onto the end of the string passed to the
function: AnsiString Temp2 = S + ` `; You can
always use the plus operator to append or prefix information to an AnsiString. This eliminates any need for you to use the
strcat function, though strcat is a likely candidate for the routine inside
the AnsiString implementation that actually
performs the concatenation. NOTE: The reason
I append a space onto the end of this string is to ensure that the AnsiString object that underlies this C string makes a
unique copy of it. If I simply assign the two strings without changing them,
the call to strrev or strtok will affect both strings! I do,
however, use standard C functions to tokenize and reverse the strings: Temp1 = strtok(S.c_str(), TokenStr.c_str()); Temp2 = strrev(Temp2.c_str()); Temp2.SetLength(Temp2.Length() - (Temp1.Length() +
1)); What the
function does here is not important. Just notice that I can get at the
underlying null-terminated string with the c_str() method of the AnsiString class. Once I have access to the underlying string, I can pass it to
any of the standard C functions such as strrev, strcat, strtok, and so on. To get
the length of a string, call the Length method
of the AnsiString class: int I = MyString.Length(); I have gone
on at some length about the AnsiString class because it is used
extensively throughout this book and throughout most BCB programs. The key
fact to remember is that AnsiStrings are compatible with the VCL. For
instance, you can assign an AnsiString directly to a VCL control: AnsiString S("Doubting the filching age will
steal his treasure"); Edit1->Text = S; You can
also assign a regular C string to a VCL control, but that is because the VCL
string is represented on the C++ side as an AnsiString, which can have its constructor called by a null-terminated string: char S[] = {"Till either gorge be stuffed or
prey be gone"}; Memo1->Text
= S;
NOTE: This
time the quote is from Shakespeare's Venus and Adonis, with this seemingly
gruesome line appearing in the following context: In fact,
you can take advantage of these constructors and assign an integer directly
to a VCL control: Memo1->Text = 2; This is
considerably more freedom than the strongly typed Object Pascal language
would ever give you liberty to pursue. Exploring
SYSDEFS.H This
chapter is largely about the places where C++ and the VCL are woven together.
Most of the code that is used to accomplish this end is found in SYSDEFS.H. SYSDEFS.H is
the place where the developers put most of the core system code needed to
allow BCB to use the VCL native types. In particular, if there was some
element of Object Pascal not supported by C++ that played a role in the VCL,
solutions to that problem were mapped out in this file. The only major
exception to this rule is DSTRINGS.H, where the AnsiString class is declared. NOTE: SYSDEFS.H is to some degree a testament to
the power of the C++ language. It features classes and templates that emulate
features of Object Pascal that The
following classes appear in SYSDEFS.H: class __declspec(delphireturn) Currency : public
CurrencyBase class __declspec(delphireturn) TDateTime : public TDateTimeBase
class __declspec(delphiclass) TObject class TMetaClass class __declspec(delphireturn) Set class TVarArray class TVarData class TVarRec template<class T> class OpenArray class __declspec(delphireturn) Variant: public
TVarData class AutoCmd class NamedParm class Procedure: public AutoCmd class Function: public AutoCmd class PropertySet: public AutoCmd class PropertyGet: public AutoCmd typedef class TMemoryManager *PMemoryManager; class THeapStatus; Some of these
classes, such as TObject and TMetaClass, have already been discussed at length. Other classes such as Procedure, Function, PropertyGet, and PropertySet are part of the guts of the BCB-VCL interface, and are not worth
discussing in this context. But a few of these classes, notably Set, Currency, TDataTime, Variant, and TMemoryManager play an important role in standard BCB
programming, and will therefore be covered in the next few pages. I will also
take a look at TVarRec and OpenArray, because they play an important supporting role in the object model. Formatting
Text In the
next three sections, called TDateTime, TCurrency, and OpenArrays, I explore some of the ways you
can format text before presenting it to the user. You should read through all
three sections to get a feeling for some of the options supplied by BCB. Many
tools for formatting text have been contributed by third-party VCL vendors.
There are many sites on the Web that provide links to third-party toolmakers.
To get started, visit the Link section of my Web site: users.aol.com/charliecal, or go to www.borland.com. TDateTime The VCL
comes equipped with a number of functions for working with the current date
or time, as shown by this excerpt from the online help:
There is
also a TDateTimeField that is used in database
programming. Many of
these functions work with an object type called TDateTime that is found in the SYSDEFS.H unit. Most of the rest of this
section is dedicated to exploring the uses of the TDateTime type. This type exists in order to provide compatibility with the VCL
routines listed at the beginning of this section. The
following method shows two different ways of outputting the current date: void __fastcall TForm1::CurrentDate1Click(TObject
*Sender) { TDateTime
Date1, Date2 = Now(); AnsiString
S;
DateTimeToString(S, "dddd, mmmm dd, yyyy",
Date1.CurrentDate()); S +=
"\r" + Date2.FormatString("dddd, mmmm dd, yyyy");
ShowMessage(S); } The code
shown in the CurrentDate1Click method outputs the following text
inside a RichEdit control: Tuesday,
January 07, 1997 Tuesday,
January 07, 1997
NOTE: The TRichEdit control from the Win95 page of the Component
Palette provides supports for the RTF format. There is no set limit on the
amount of text you can display in a TRichEdit
control, and it supports the use of multiple fonts and colors inside a single
control. There are also routines for saving the loading and saving the
contents of the control, and for printing. I
initialize the first of the two TDateTime
objects with the default constructor, which automatically sets the date to
Saturday, December 30, 1899. Then set asks this object to retrieve the
current date (not the default date), which it passes to me in the form of a TDateTime object: static TDateTime __fastcall CurrentDate(); A raw TDateTime object is obviously not something I can show
to the user, so I use the VCL DateTimeToString routine to convert it into a
string: DateTimeToString(S, "dddd, mmmm dd, yyyy",
Date1.CurrentDate()); This
routine takes a string in the first parameter, a format string in the second
parameter, and a TDateTime record in the third parameter. NOTE: If
you look in SYSDEFS.H, you will see that TDateTime is declared delphireturn. That is because this object needs to
conform to the expectations of VCL routines such as DateTimeToString. In other words, this is a classic example
of what use the BCB team made of delphireturn. The
second TDateTime object shown in this example is
set equal to the current date during its construction: TDateTime Date2(Now()); Notice
that I pass the VCL routine called Now to the TDateTime constructor. Now returns a variable of type TDateTime that
is initialized to the current date and time. You can then use the FormatString method of the TDateTime object to retrieve the current string: S += "\n" + Date2.FormatString("dddd,
mmmm dd, yyyy"); The FormatString method takes the same type of format string
passed to DateTimeToString. Format
strings are documented in the online help, but the basic idea behind them is
that if you pass in two letters, you get a number back; pass in three
letters, and you get an abbreviation back; and pass in four letters, and you
get a whole word back: dd => 01 ddd => Sun dddd => Sunday The
following method displays the current time in a specified format: void __fastcall TForm1::CurrentTime1Click(TObject
*Sender) { TDateTime
Date(Now());
RichEdit1->Text = Date.FormatString("h:nn:ss am/PM"); } The
result will look something like this: 12:27:56 PM In this
case, the PM is in large letters because I wrote
it that way in the format specifier. Had I written am/pm, the resulting string would have looked like this: 12:27:56 pm. The rest of the format specifiers works
like this: h => No leading zero hh => Prefix a leading zero In
short, if you pass in one letter, you get back a string with no leading zero;
if you pass in two, a leading zero will be added automatically. Notice that
you pass in an n to specify the format for
minutes. This is because m has already been used for months.
Here is
a table listing the various specifiers you can use:
The various
date and time formats shown in the last items will be explained in just one
moment. The DateTimeString., DateString,
and TimeString methods of the TDateTime object do not require that you provide
format specifiers. Here, for instance, is an example of how to use the DateTimeString method: void __fastcall TForm1::DateandTime1Click(TObject
*Sender) { TDateTime
Date(Now());
RichEdit1->Lines->Add(Date.DateTimeString()); } On my
system, the code shown here produces the following result: 1/7/97 12:31:24 PM The
format used in this case is global to the system. You can change these
settings by working with a series of global variables found in SYSUTILS.HPP: extern System::AnsiString CurrencyString; extern unsigned char CurrencyFormat; extern unsigned char NegCurrFormat; extern char ThousandSeparator; extern char DecimalSeparator; extern unsigned char CurrencyDecimals; extern char DateSeparator; extern System::AnsiString ShortDateFormat; extern System::AnsiString LongDateFormat; extern char TimeSeparator; extern System::AnsiString TimeAMString; extern System::AnsiString TimePMString; extern System::AnsiString ShortTimeFormat; extern System::AnsiString LongTimeFormat; extern System::AnsiString ShortMonthNames[12]; extern System::AnsiString LongMonthNames[12]; extern System::AnsiString ShortDayNames[7]; extern System::AnsiString LongDayNames[7]; On my
system, these values are preset as follows: CurrencyString: $ CurrencyFormat: 0 NegCurrFormat: 0 ThousandSeparator: , DecimalSeparator: . CurrencyDecimals: 2 DateSeparator: / ShortDateFormat: M/d/yy LongDateFormat: dddd, MMMM dd, yyyy TimeSeparator: : TimeAMString: AM TimePMString: PM ShortTimeFormat: h:mm AMPM LongTimeFormat: h:mm:ss AMPM ShortMonthNames: Jan ShortMonthNames: Feb ShortMonthNames: Mar ShortMonthNames: Apr ShortMonthNames: May ShortMonthNames: Jun ShortMonthNames: Jul ShortMonthNames: Aug ShortMonthNames: Sep ShortMonthNames: Oct ShortMonthNames: Nov ShortMonthNames: Dec LongMonthNames: January LongMonthNames: February LongMonthNames: March LongMonthNames: April LongMonthNames: May LongMonthNames: June LongMonthNames: July LongMonthNames: August LongMonthNames: September LongMonthNames: October LongMonthNames: November LongMonthNames: December ShortDayNames: Sun ShortDayNames: Mon ShortDayNames: Tue ShortDayNames: Wed ShortDayNames: Thu ShortDayNames: Fri ShortDayNames: Sat LongDayNames: Sunday LongDayNames: Monday LongDayNames: Tuesday LongDayNames: Wednesday LongDayNames: Thursday LongDayNames: Friday LongDayNames: Saturday For instance,
the following code changes the nature of the current ShortDateFormatString: void __fastcall
TForm1::SetShortDateFormattoMMMMDDDDYYYY1Click(TObject *Sender) {
RichEdit1->Text = Now();
ShortDateFormat = "MMMM/DDDD/YYYY";
RichEdit1->Lines->Add(Now()); } The text
displayed by calling this function is as follows: 1/7/97 1:30:21 PM January/Tuesday/1997 1:30:21 PM The
first block of text shows the default behavior, and the second block shows what
happened after I made a few subtle changes to the system. The
system initializes the date and time strings to the choices you make in
Windows. You can see the current Windows settings by calling GetLocaleInfo. Changes you make are written to the system
registry if you send a WM_INICHANGE message to your own nonconsole
mode application. The
following lines of code show how you can compare two times using the greater
than operator: void __fastcall TForm1::SetTimeOne1Click(TObject
*Sender) { FTimeOne =
Now();
RichEdit1->Text = FTimeOne.TimeString(); } void __fastcall TForm1::SetTimeTwo1Click(TObject
*Sender) { FTimeTwo =
Now();
RichEdit1->Lines->Add(FTimeTwo.TimeString()); } void __fastcall TForm1::CompareOnetoTwo1Click(TObject
*Sender) { AnsiString
S; if (FTimeOne
> FTimeTwo) S =
FTimeOne.TimeString() + " > " + FTimeTwo.TimeString(); else S =
FTimeTwo.TimeString() + " > " + FTimeOne.TimeString();
RichEdit1->Lines->Add(S); } The
first method shown here sets a global object called TimeOne to the current time. The second method sets a second global object to
the current time. After calling the two functions at a two-second interval, you
can end up with the objects initialized to two different times: 2:10:29 PM 2:10:31 PM You can
then use the third function to compare them using the > operator: 2:10:31 PM > 2:10:29 PM You can
also use the ++ operator on the current time to
increment the day by one: void __fastcall
TForm1::CompareTwotoOne1Click(TObject *Sender) { int i;
RichEdit1->Lines->Add(FTimeOne.DateTimeString()); FTimeOne++;
RichEdit1->Lines->Add(FTimeOne.DateTimeString()); } Here is the
output in the RichEdit control after a run off all four
functions: 2:13:03 PM 2:13:05 PM 2:13:05 PM > 2:13:03 PM // First call to compare 1/7/97 2:13:03 PM 1/8/97 2:13:03 PM 2:13:03 PM > 2:13:05 PM // Second call to compare As you
can see, calling the ++ operator incremented the day by
one. After incrementing the values, a second call to the compare function
showed that FTimeOne is now larger than FTimeTwo. Even though it is still two seconds smaller
than date two, it is now a day later, and hence larger. This
should give you some sense of what you can do with the TDateTime type. This has not been an exhaustive
investigation of what can be done, but if you now open up SysDefs.h, you should have no trouble following the
drift of the declaration for the other methods in the TDateTime object. The code samples shown in this
section are from a program found on disk called, in a flight of poetic fancy,
DateTime1. TCurrency I will
now spend a few minutes looking at TCurrency.
After the rather lengthy investigation of TDateTime that you just saw, there should not be much need to examine this
object in-depth, because it follows the same general patterns of logic you
saw in the previous section. When
following this discussion, remember that the primary reason the type exists
is for compatibility with the VCL. In particular, the TCurrency type, like the TDateTime type, is used in database programming. For instance, there is a TCurrencyField and a TDateTimeField available for database programmers. The
principles behind the field types are examined in detail throughout the
lengthy database section of this book, Chapters 8 through 18, and
particularly in Chapter 11, "Working with Field Objects." The Currency type is a wrapper around the built in __int64 type. The compiler probably will not support
assigning large numbers directly to a variable of type Currency: Currency C = 5000000000000; // This probably won't
work You can,
however, pass in a string that holds a large number. Consider the following
method: Currency C = void __fastcall
TForm1::Button1Click(TObject *Sender) { Currency
C("500000000000000"); AnsiString S
= C;
Edit1->Text = S;
Edit2->Text = Format("%m", OPENARRAY(TVarRec, (C))); } This
method initializes a variable of type currency to a large number with a
string. Most of the time large numbers will be entered by the user in an edit
control, so this is a reasonable way to proceed. Once you
have an instance of a Currency type, you can display it directly
to the user or assign it to a TCurrencyField from a database table. Notice
that you can assign a string directly to a Currency field, and vice versa. When translating a currency type to a string,
the result does not produce a formatted result string. Instead, you see a
plain number: 500000000000000 If you
want to display the string with proper formatting, use the Format function, as shown in the Button1Click method: $500,000,000,000,000.00 I will
describe the Format function, and OpenArrays, in the next section of this chapter,
called "Working with OpenArrays." Note that some of the database
controls will automatically display a currency type with formatting,
depending in part on their attributes and the contents of the Data
Dictionary. The
currency type will handle numbers in the following range: -922337203685477.5808..922337203685477.5807 That is,
it can handle between 19 and 20 digits. There
are many operators in the Currency class for performing mathematical
operations. For instance, the following syntax is valid: Currency C1 = 25000, C2 = 5000, C3; C1 += C2; C1 = C2 / 9; C3 = (C1 % C2); C3 = (C1 + C2) / C1 + (C1 % C2); It's
interesting to note that these operations are performed on objects, and not
on simple types. This is an excellent example of the power of C++ and the
amazing things you can do with operator overloading. If you are not used to
C++, you should spend a few moments contemplating the fact that C1, C2, and C3 are objects, with methods and functions, and
not just simple built-in types like integers or floats. You should open up SysDefs.h to see a list of all the supported syntax
used by the Currency object. MaskEdits and
Other Issues The
native VCL TMaskEdit control deserves at least a few
sentences of comment during this discussion of formatting strings. This
component is a descendant of TEdit that enables you to control the
kinds of characters that are entered into the control. The key
to the TMaskEdit component is the EditMask property, which enables you to enter a
string that uses several special characters to define what the user can
legally enter into the edit area. There is
a property editor associated with the EditMask property. In this editor, you can find a number of default masks, as
shown in Figure 3.1. However, on many occasions, none of these masks will
suit your purpose. Figure 3.1. The property editor for the EditMask property. I could,
for instance, enter the following string as an EditMask: ######;0;_ The purpose
of this string is to ensure that the user can enter only numeric values. In
particular, these numeric values represent the number of widgets that the
user wants to sell. To
understand the EditMask property, you need to study the entry
associated with it in the online help. Here, you see all the characters that
can be used to define a mask, along with a description of the effects
associated with each character. I quote most of this list later in this
section. In the
online help for the EditMask property, you will find that a
pound sign (#) enables you to enter only
numbers, spaces, and plus and minus characters. Notice, however, that there
is also a 0 character, which requires that
you enter a number in each location marked with that character. The question,
then, is which special character should you use: # or 0? In some
cases, the 0 character is not what you want,
because it forces the user to enter values. For instance, the following code
forces the user to enter a six-digit number, one for each of the zeroes you
place in the mask: 000000;0;_ The
preceding mask would enable you to enter the number 123456, but it would reject 123. If you want to give the user the
freedom to enter a number of any size up to 999,999, you should use the # character rather than the 0 character. One way to sum this matter up is
as follows: The 0 character means that the user has
to fill in that place with a number, while the # character enables the user to fill in that space with either a number
or a blank space. The zero
that appears after the semicolon in the previous string specifies that the
mask entered is not saved as part of the data. If I had placed a 1 here, the mask would have been saved. In
this particular case, there are no mask characters to either save or discard,
so it doesn't matter what I placed in this location. The phone mask, shown
previously in Figure 3.1, contains two parentheses that would be affected by
this value. Finally,
the very end of the previous mask includes an underscore. This value is used
to specify what will be used to designate the space character. Notice
that each field of the mask is separated from the last by a semicolon. Each
mask consists of three fields, which were described earlier. The MaskEdit component is available from the Additional
page of the Component palette. Notice that you can click a button in the EditMask dialog found in the EditMask property editor in order to load country-specific strings. NOTE: The
following lists format specifiers you can use in the TMaskEdit control: Remember
that many of the database objects such as the TDateTime field provide automatic formatting for you. This is
also an area where you might consider turning to third-party products. For
instance, Turbo Power Software has an excellent library called Orpheus that
provides many fine controls and routines for automatically formatting
strings. You should, however, explore the market thoroughly, because there
are many fine products out there for handling this type of problem. In particular,
many controls are found on the Web. You can visit my Web site to find links
to sites that specialize in components. (users.aol.com/charliecal). Working with
OpenArrays The VCL
supports a concept called an array of const. This type will enable you to pass in a variable number of
parameters. Think for a moment of the sprintf function: int sprintf(char *buffer, const char *format[,
argument, ...]); The last
argument of this function can contain multiple parameters. OpenArrays provide the same functionality in Object
Pascal. In short, they enable you to pass a variable number of parameters to
a function. Here is
an Object Pascal code segment declared to accept an array of const: function Format(const Format: string; const Args:
array of const): string; The Format function acts almost exactly like sprintf. Its first parameter contains a string with
various format specifiers in it, and its second parameter consists of an
array containing multiple values to be formatted. The format specifiers used
by Format are essentially the same as those
you would use in sprintf. After all, the whole point of
adding the function to the Object Pascal language was simply to duplicate the
functionality of sprintf inside Object Pascal. Here is the
way a call to Format looks in Object Pascal: var S, Name:
string; Age:
Integer; begin Name =
GetName; Age =
GetAge; S := Format("My name is %s and I am %d
years old", [Name, Age]); end; All this
is good and well, but you might object that C++ already has support for this
kind of syntax through sprintf. That is correct, but you will
find many VCL database routines that use arrays of const for various purposes. There are no standard C++ equivalents of these
routines, as they exist only in the VCL. In other words, the C++ and Object
Pascal implementations of this functionality differ in their details, even
though the interface to the user is similar. As a result, special C++ classes
need to be created to duplicate this built-in Object Pascal type. While
you may or may not find the Format routine itself useful, you will
definitely need to know about arrays of const if you want to work with the VCL, and particularly if you want to do any
database programming. For instance, the following database routines all use
arrays of const: FinkKey, FindNearest, SetRange, AddRecord, SetKeyFields, AppendRecord, InsertRecord, SetFields, AssignValue, and DBErrorFmt. Clearly, this is a subject that you need to understand. Here is
the declaration for the Format function as it appears in SYSUTILS.HPP: extern System::AnsiString __fastcall Format(const
System::AnsiString Format, const
System::TVarRec *Args, const int Args_Size); As you
can see, this function takes not two, but three parameters. You will not,
however, pass all three parameters into this function, nor will you pass them
into any of the database routines shown previously. Instead, you use a macro
called OPENARRAY, which enables you to call VCL
functions that use arrays of const with a reasonable degree of ease.
NOTE: As I
am sure you have noticed already, this particular subject is not exactly the
garden spot of C++Builder. We VMS Desenvolvimentos
Diversas Dicas, Apostilas, Arquivos Fontes,
Tutoriais, Vídeo Aula, Download de Arquivos Relacionado a Programação em C++
Builder.
|