Using a Class to Manage Conversions
Go Up to Converting Measurements
You can always use conversion functions to register a conversion unit. There are times, however, when this requires you to create an unnecessarily large number of functions that all do essentially the same thing.
If you can write a set of conversion functions that differ only in the value of a parameter or variable, you can create a class to handle those conversions. For example, there is a set standard techniques for converting between the various European currencies since the introduction of the Euro. Even though the conversion factors remain constant (unlike the conversion factor between, say, dollars and Euros), you cannot use a simple conversion factor approach to properly convert between European currencies for two reasons:
- The conversion must round to a currency-specific number of digits.
- The conversion factor approach uses an inverse factor to the one specified by the standard Euro conversions.
However, this can all be handled by the conversion functions such as the following:
Delphi:
function FromEuro(const AValue: Double, Factor; FRound: TRoundToRange): Double;
begin
Result := RoundTo(AValue * Factor, FRound);
end;
function ToEuro(const AValue: Double, Factor): Double;
begin
Result := AValue / Factor;
end;
C++:
double __fastcall FromEuro(const double AValue, const double Factor,
TRoundToRange FRound) {
return(RoundTo(AValue * Factor, FRound));
}
double __fastcall ToEuro(const double AValue, const double Factor) {
return (AValue / Factor);
}
The problem is, this approach requires extra parameters on the conversion function, which means you can't simply register the same function with every European currency. In order to avoid having to write two new conversion functions for every European currency, you can make use of the same two functions by making them the members of a class.
Contents
Creating the conversion class
The class must be a descendant of TConvTypeFactor. TConvTypeFactor defines two methods, ToCommon and FromCommon, for converting to and from the base units of a conversion family (in this case, to and from Euros). Just as with the functions you use directly when registering a conversion unit, these methods have no extra parameters, so you must supply the number of digits to round off and the conversion factor as private members of your conversion class. This is shown in the EuroConv example in the demos\ConvertIt directory (see euroconv.pas):
Delphi:
type
TConvTypeEuroFactor = class(TConvTypeFactor)
private
FRound: TRoundToRange;
public
constructor Create(const AConvFamily: TConvFamily;
const ADescription: string; const AFactor: Double;
const ARound: TRoundToRange);
function ToCommon(const AValue: Double): Double; override;
function FromCommon(const AValue: Double): Double; override;
end;
end;
C++:
class PASCALIMPLEMENTATION TConvTypeEuroFactor
: public Convutils::TConvTypeFactor {
private:
TRoundToRange FRound;
public:
__fastcall TConvTypeEuroFactor(const TConvFamily AConvFamily,
const AnsiString ADescription, const double AFactor,
const TRoundToRange ARound);
TConvTypeFactor(AConvFamily, ADescription, AFactor);
virtual double ToCommon(const double AValue);
virtual double FromCommon(const double AValue);
}
The constructor assigns values to those private members:
Delphi:
constructor TConvTypeEuroFactor.Create(const AConvFamily: TConvFamily; const ADescription: string; const AFactor: Double; const ARound: TRoundToRange);
begin
inherited Create(AConvFamily, ADescription, AFactor);
FRound := ARound;
end;
C++:
__fastcall TConvTypeEuroFactor::TConvTypeEuroFactor
(const TConvFamily AConvFamily, const AnsiString ADescription,
const double AFactor, const TRoundToRange ARound)
: TConvTypeFactor(AConvFamily, ADescription, AFactor); {
FRound = ARound;
}
The two conversion functions simply use these private members:
Delphi:
function TConvTypeEuroFactor.FromCommon(const AValue: Double): Double;
begin
Result := RoundTo(AValue * Factor, FRound);
end;
function TConvTypeEuroFactor.ToCommon(const AValue: Double): Double;
begin
Result := AValue / Factor;
end;
C++:
virtual double TConvTypeEuroFactor::ToCommon(const double AValue) {
return (RoundTo(AValue * Factor, FRound));
}
virtual double TConvTypeEuroFactor::ToCommon(const double AValue) {
return (AValue / Factor);
}
Declare variables
Now that you have a conversion class, begin as with any other conversion family, by declaring identifiers:
Delphi:
var
euEUR: TConvType; { EU euro }
euBEF: TConvType; { Belgian francs }
euDEM: TConvType; { German marks }
euGRD: TConvType; { Greek drachmas }
euESP: TConvType; { Spanish pesetas }
euFFR: TConvType; { French francs }
euIEP: TConvType; { Irish pounds }
euITL: TConvType; { Italian lire }
euLUF: TConvType; { Luxembourg francs }
euNLG: TConvType; { Dutch guilders }
euATS: TConvType; { Austrian schillings }
euPTE: TConvType; { Portuguese escudos }
euFIM: TConvType; { Finnish marks }
cbEuro: TConvFamily;
C++:
TConvFamily cbEuro;
TConvType euEUR; // EU euro
TConvType euBEF; // Belgian francs
TConvType euDEM; // German marks
TConvType euGRD; // Greek drachmas
TConvType euESP; // Spanish pesetas
TConvType euFFR; // French francs
TConvType euIEP; // Irish pounds
TConvType euITL; // Italian lire
TConvType euLUF; // Luxembourg francs
TConvType euNLG; // Dutch guilders
TConvType euATS; // Austrian schillings
TConvType euPTE; // Protuguese escudos
TConvType euFIM; // Finnish marks
Register the conversion family and the other units
Now you are ready to register the conversion family and the European monetary units, using your new conversion class. Register the conversion family the same way you registered the other conversion families:
Delphi:
cbEuro := RegisterConversionFamily ('European currency');
C++:
cbEuro = RegisterConversionFamily("European currency");
To register each conversion type, create an instance of the conversion class that reflects the factor and rounding properties of that currency, and call the RegisterConversionType method:
Delphi:
var
LInfo: TConvTypeInfo;
begin
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'EUEuro', 1.0, -2);
if not RegisterConversionType(LInfo, euEUR) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'BelgianFrancs', 40.3399, 0);
if not RegisterConversionType(LInfo, euBEF) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'GermanMarks', 1.95583, -2);
if not RegisterConversionType(LInfo, euDEM) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'GreekDrachmas', 340.75, 0);
if not RegisterConversionType(LInfo, euGRD) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'SpanishPesetas', 166.386, 0);
if not RegisterConversionType(LInfo, euESP) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'FrenchFrancs', 6.55957, -2);
if not RegisterConversionType(LInfo, euFFR) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'IrishPounds', 0.787564, -2);
if not RegisterConversionType(LInfo, euIEP) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'ItalianLire', 1936.27, 0);
if not RegisterConversionType(LInfo, euITL) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'LuxembourgFrancs', 40.3399, -2);
if not RegisterConversionType(LInfo, euLUF) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'DutchGuilders', 2.20371, -2);
if not RegisterConversionType(LInfo, euNLG) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'AustrianSchillings', 13.7603, -2);
if not RegisterConversionType(LInfo, euATS) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'PortugueseEscudos', 200.482, -2);
if not RegisterConversionType(LInfo, euPTE) then
LInfo.Free;
LInfo := TConvTypeEuroFactor.Create(cbEuro, 'FinnishMarks', 5.94573, 0);
if not RegisterConversionType(LInfo, euFIM) then
LInfo.Free;
end;
C++:
TConvTypeInfo *pInfo = new TConvTypeEuroFactor(cbEuro, " EUEuro ", 1.0, -2);
if (!RegisterConversionType(pInfo, euEUR))
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " BelgianFrancs ", 40.3399, 0);
if (!RegisterConversionType(pInfo, euBEF))
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " GermanMarks ", 1.95583, -2);
if (!RegisterConversionType(pInfo, euDEM))
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " GreekDrachmas ", 340.75, 0);
if (!RegisterConversionType(pInfo, euGRD) delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " SpanishPesetas ", 166.386, 0);
if (!RegisterConversionType(pInfo, euESP) delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " FrenchFrancs ", 6.55957, -2);
if (!RegisterConversionType(pInfo, euFFR) delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " IrishPounds ", 0.787564, -2);
if (!RegisterConversionType(pInfo, euIEP) delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " ItalianLire ", 1936.27, 0);
if (!RegisterConversionType(pInfo, euITL) delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " LuxembourgFrancs ", 40.3399, -2);
if (!RegisterConversionType(pInfo, euLUF) delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " DutchGuilders ", 2.20371, -2);
if (!RegisterConversionType(pInfo, euNLG) delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " AutstrianSchillings ", 13.7603, -2);
if (!RegisterConversionType(pInfo, euATS) delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " PortugueseEscudos ", 200.482, -2);
if (!RegisterConversionType(pInfo, euPTE) delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, " FinnishMarks ", 5.94573, 0);
if (!RegisterConversionType(pInfo, euFIM) delete pInfo;
Note: Click Start | Programs | Embarcadero RAD Studio Alexandria | Samples to find the ConvertIt sample (see
Object Pascal\RTL\ConvertIt
and\CPP\RTL\ConvertIt
). The ConvertIt sample provides an expanded version of this example that includes other currencies that do not have fixed conversion rates and with more error checking.
Use the new units
You can now use the newly registered units to perform conversions in your applications. The global Convert function can convert between any of the European currencies you have registered with the new cbEuro family. For example, the following code converts a value from Italian Lire to German Marks:
C++:
Edit2->Text = FloatToStr(Convert(StrToFloat(Edit1->Text), euITL, euDEM));