Using a Class to Manage Conversions

From RAD Studio
Jump to: navigation, search

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.

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 10 Seattle | 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));

See Also