Data types

Data types

25.55 becomes to 24.5499999 in a field of a calculated paradox-field
Array of const
Create 'dynamic' array
Static Variables
Dynamic Components Solution
Class/Static/Shared Variables
Creating many instances
Difference of TMemoFields
Using integer pointers
MS Binary Format / IEEE conversion
Array of the Image Object
Pass a record problem
Function pointer
Converting Real to a fraction of two integers
Resizing (Dynamic) Arrays
Use Free with records
Create large arrays
Pointer to a function
Pass a function as a parameter
Array of TPoints
Function return type


25.55 becomes to 24.5499999 in a field of a calculated paradox-field

Question


Why 25.55 become 24.499999?

I have a paradox table that contains currency values. I am trying to

calculate totals for some of the fields programmatically with the

following code:



 Table1.Open;

   Table1.first;

   VATTotal := 0;

   SalesIncVAT := 0;

   While not Table1.EOF do

     begin

       VATTotal := VATTotal + Table1VAT.value;

       SalesIncVAT := SalesIncVAT + Table1GrandTotal.value;

       Table1.next;

     end;

     Label2.caption := FloatToStr(SalesIncVAT);

     Label3.caption := FloatToStr(VATTotal);

     Label1.caption := FloatToStr(SalesIncVAT - VATTotal);



Answer


A:

The label captions all show the same type of answer as the subject

line.  Can someone explain what I've done wrong.



You're not to blame for the calculation error!



I've found the same type of problems in an accounting package I'm writing

(=serious!).

Seems to me that Borland has some calculating coding to do.



You can get around the problem using the round function.

SalesIncVAT:=round(SalesIncVAT*100)/100;  {gives you two decimal places}



A:

that's nothing extraodrinary, this is a basic property of floating point

math, which is precise only to a given number of decimal digits. More

specifically float is precise for intermediate range integers and for

fractions which are a sum of components which are powers of 2, any other

number is rounded at the float accuracy (7 digits for single precision, 15

for double, 20 for extended). One has to use round or the str procedure:

var

  s : string;

begin

  str(SalesIncVat:10:2,s); {10 characters altohether (with point) and 2

			    decimal digits}

  Label1.Caption:=s;



A:

In the help it is written that FloatToStr formats the string with 15 decimal

digits - that is why your number is displayed in a fancy fashion, try this

function with a number like 25.5, 25.25, 25.125 or so i.e. one that has a

finite representation in binary notation and you should get it back.



Or use the function FloatToStrF which takes also specification of total

width and number of decimal digits as its arguments.




Array of const

Question


InsertRecord() takes an array of const.  How do I build an array of const

at runtime?

Answer


A:

procedure foo(a : array of const);

implementation

 var

     var1      : longint;

     var2      : pointer;

     var3      : integer;

begin

     var1 := 12345678;

     var2 := @var1;

     var3 := 1234;

     foo([var1, var2, var3]);



Actually an array of const is more correctly called array of tvariant.

Tvariant is a multiple choice kind of variable that can take an number of

types. It has its heritage in Visual Basic. Delphi allows to use either name.



A:

Define a type such as:



TYPE

  NAME1 = Array[1..4,1..10] of Integer;



Then, in your CONST section:



NAME2 : NAME1 = ((1,2,3,4,5,6,7,8,9,10),

                 (1,2,3,4,5,6,7,8,9,10),

                 (1,2,3,4,5,6,7,8,9,10),

                 (1,2,3,4,5,6,7,8,9,10));


Create 'dynamic' array

Question


How do I create a 'dynamic' array?  I need to vary the number of elements

in the array at run-time.

Answer


A:

Assuming you want to store "GIZMOS" in your array, try the following:

CONST

  MaxGIZMOS = $FFFF Div (SizeOf(GIZMOS)) { Or whatever the maximum number of

GIZMOS is going to be...}



TYPE

  pGIZMOArray = ^GIZMOArray;

  GIZMOArray  = Array[1..MaxGIZMOS] of GIZMOS;



VAR

  TheGIZMOS: pGIZMOArray;

  GIZMOcount: integer;

BEGIN

  GetMem(TheGIZMOS,(GIZMOcount+1)*SizeOf(GIZMO)); {Need 1 extra as GetMem

array is zero-based...}

  TheGIZMOS^[index] := Whatever;

etc...



A:

TList is such a dynamic array. Look for details in the Help section. If you

want to do it yourself you have to use GetMem to allocate a pointer to dynamic

memory and later FreeMem to release the space of the dynamic array. Tlist does

all that for you in a painless way.


Static Variables

Question


Does DELPHI allow static variables? Static, in the C sense, a variable declared

in a procedure that does not disappear when the procedure is exited, and is

available when the procedure is next entered.

Answer


A:

Yes, it does. You have declare the variable in the const section,

for example.



procedure P;

const

  MyVariable : Integer = 0;

begin

  Inc(MyVariable);

end;



In this example MyVariable holds the P calls number.



  However, often it is a better to use the field of the object instead

(if possible).




Dynamic Components Solution

Question


How to dynamic component creation?

Answer


A:

Warning to Newbies:

If you simply want to create a component whos type you know at compile time

then you should read the delphi\doc\VB2Delph.wri file to see how this is done,

it also explains control arrays and is just darn good reading anyway. This post

explains how to utilise the RTTI facilities in Delphi.



The first thing that your application needs to do is register all the classes

that you know you are going to need create at some stage in your program. This

can be done using either the RegisterClass(), RegisterClasses() or

RegisterClassAlias() functions.



eg.

    procedure TForm1.FormCreate(Sender: TObject);

    begin

        RegisterClasses([TButton, TEdit, TMemo, TLabel]);

    end;



This may seem like a limitation but then Delphi is a static language. If you

want true dynamic creation of objects in a weakly typed late bound environment

then use a dynamic language like Smalltalk. I have a feeling that Delphi uses

this registration mechanism to register all the components in the DCL when it

starts up, which allows it to create any component at design time.



Creating the components. Use the FindClass() function to return a class

reference to the component that you want to create and call its Create method.

Easy, isn't it? In the example I have typecast SomeComponent to a TControl so

that I can set its parent property (I can do this because I know that all the

classes that I registered were descendants of TControl). You need to set the

parent property of a control to make it appear on the form.



eg.



    procedure TForm1.CreateClick(Sender: TObject);

    begin

        SomeComponent:= TComponentClass(FindClass(ClassName.Text)).Create(Self);

        (SomeComponent as TControl).Parent := Self;

    end;



Now that you have the component, how do you set it's properties without using

the biggest case statement in the universe? Use the GetPropInfo() function to

get the run-time type information (RTTI) structure for the property, and then

use the SetXXXXProp() set of functions to set it's value. (Note: These

functions are not documented in the Delphi help files. OO programming means

reading other peoples code and building on it, not reinventing the proverbial

wheel.) Each SetXXXXProp() function also has an equivalent GetXXXXProp()

function so you can inspect an objects property values.



eg.



    procedure TForm1.SetPropertyClick(Sender: TObject);

    var

        PropType: PTypeInfo;

        PropInfo: PPropInfo;

    begin

        PropInfo := GetPropInfo(SomeComponent.ClassInfo, PropertyName.Text);

        PropType := PropInfo^.PropType;

        case PropType^.Kind of

            tkInteger:

                SetOrdProp(SomeComponent, PropInfo,

StrToInt(PropertyValue.Text));

            tkChar:

                SetOrdProp(SomeComponent, PropInfo, Ord(PropertyValue.Text[1]));

            tkEnumeration:

                SetOrdProp(SomeComponent, PropInfo, GetEnumValue(PropType,

PropertyValue.Text));

            tkFloat:

                SetFloatProp(SomeComponent, PropInfo,

StrToFloat(PropertyValue.Text));

            tkString:

                SetStrProp(SomeComponent, PropInfo, PropertyValue.Text);

        end;

    end;



You can also set the values of Set, Class and Method properties but this can be

a little bit more complicated. I might post how to do that a little bit later.



And that's it. It's pretty amazing what you can find out by reading the VCL

source code.



This is tempting feature, but has potential for mass abuse. There is no

substitute for a good understanding of the other ways of achieving the same

things in Delphi and choosing the technique most appropriate to your design.


Class/Static/Shared Variables

Question


I have been generally pleased with Delphi's level of Object Orientation,

but it does seem to be missing Class Variables.  It would be nice to be

able to have a class/static/shared variable that gets updated every time

the class is instantiated - like so:



type

    TNode = class

    public

        NodeCount : Integer = 0;  {THIS IS NOT ALLOWED}

        constructor Create;

        {

        other stuff

        }

    end;



    TNode.Create;

    begin

        inherited Create;

        Inc(NodeCount);

    end;





It seems the compiler won't allow the typed constant within a class type

declaration.  So the NodeCount variable gets reinitialized for every

instantiation of the TNode class.  It will work by putting the class

variable OUTSIDE of the type statement as a plain-old typed constant.





But then rather than having OOP looking code like:

        SampleNode := TNode.Create;

        x := SampleNode.NodeCount;



You have to resort to semi OOP/procedural code like:

        SampleNode := TNode.Create;

        x := NodeCount;

Answer


A:

It's not a big deal to get equivalent

functionallity by using a class method. You simply declare NodeCount as

a regular typed constant in the implementation section of your file.





type

    TNode = class

    public

        NodeCount : Integer = 0;  {THIS IS NOT ALLOWED}

        constructor Create;

        Class Function GetNodeCount : word;

        {

        other stuff

        }

    end;



implementation

const

     NodeCount : word = 0;



    TNode.Create;

    begin

        inherited Create;

        Inc(NodeCount); 

    end;



    Function TNode.GetNodeCount : word;

    begin

       result := NodeCount;

    end;







So your code will look just as you want:

        SampleNode := TNode.Create;

        x := SampleNode.GetNodeCount;





the follwing form is also legal:

        x := TNode.GetNodeCount;


Creating many instances

Question


How do I create many intances of an object. Sometimes I need to create

10 or so instances and sometime I need 1000 instances. How should I

do it if I have an object named TSSObject.



        TSSObject = class(TObject)

              n1 :Single;

              n2 :Single;

        end;

Answer


A:

list:=Tlist.create;

For i:= 1 to 1000 do

    begin

    SSObject:=TSSObject.create;

    {put the newly created object somewhere - eg on a Tlist}

    list.add(SSObject);

    end;


Difference of TMemoFields

Question


While I'm on the subject can someone please explain to me why this doesn't

work! I'm afraid I just don't understand pointers all the time.



procedure TForm1.Button1Click(Sender: TObject);

var

  Buffer: Pointer;

  MyStrings: TStringList;



begin

  MyStrings := TStringList.Create;

  GetMem(Buffer, Table1Notes.DataSize);

     {After this call Pointer's Addr becomes nil?}



  Table1Notes.GetData(Buffer); {Of course this doesn't work, Pointer is nil }



  FreeMem(Buffer, Table1Notes.DataSize);

  MyStrings.Free;

end;

Answer


A:

First, if the size argument to GetMem is zero, GetMem will set the pointer

to nil (not leave it the way it was, but actually assign nil to it).  Might

want to check the value of DataSize (or getTextLen) in your debugger before

the call.



(Ignore this paragraph if Table1Notes is not a memo.)

Second, if Table1Notes is a memo field, you probably want to use

Table1Notes.getTextLen, not DataSize, since DataSize gives you the length of

the record buffer segment (0-254) while getTextLen gives you the actual size

of the memo.  (For a string field, DataSize will work, but then it's curious

as to why it's zero.)  Also, might want to consider getTextBuf rather than

getData, don't exactly know why, but I played around with them awhile ago

and getTextBuf seemed to work while getData didn't.



A:

Since wordwrapping is your application, you can just replace the #10 (line

feed) and #13 (carriage return) characters with spaces, eg.



   cursor: pchar;



   cursor := your buffer;

   while cursor^ <> #0 do

      if (cursor^ = #13) or (cursor^ = #10) then cursor^ := ' ';



This is easy because we don't have to move any text around, although it does

mean there'll be two blanks at the end of each line, which shouldn't matter

since you're word wrapping anyway.  Alternatively, you can change them to

some special character that your word wrapper recognizes as a word break but

otherwise discards (eg. #8).  If you need to get rid of them, use two

cursors as follows (OTTOMH, not tested):



   out, in: pchar;



   out := your buffer;

   in := out;

   while in^ <> #0 do begin

      if (in^ <> #10) and (in^ <> #13) then begin

          out^ := in^;

          inc (out);

      end;

      inc (in);

   end;

   out^ := #0;



A:

If you instead want to replace every CR-LF pair or a single CR or LF with a

single space you could use this:



  out, inn: PChar;



  out := your byffer;

  inn := out;

  while in^ <> #0 do begin

    if (in^ = #10) then begin

    end

    else if (in^ = #13) then begin

      if (in+1)^



A:

If you instead want to replace every CR-LF pair or a single CR or LF with a

single space you could use this:



  out, inn: PChar;



  out := buf;

  inn := out;

  while inn^ <> #0 do begin

    if (inn^ = #10) or ((inn^ = #13) and ((inn+1)^ <> #10)) then begin

      out^ := ' ';

      Inc(out);

    end

    else if (inn^ = #13) then

      { CR alone - ignore }

    else begin

      out^ := inn^;

      Inc(out);

    end;

    Inc(inn);

  end;

  out^ := #0;

  { buf is now massaged }



Untested is: the effect of shortening (by way of setting the #0 terminator)

this PChar - safest to use compile time arrays or GetMem'ed buffers, what

would happen using StrAlloc/StrDispose?



A:

Here is

the final code after some small changes! For example, at the end, we needed

to tell the pointer to go back to the beginning of its new string.



procedure TForm1.RemoveSpaces(var InBuf: PChar; Size: Word);

var

  Input,

  OutPut,

  Orig: PChar;

begin

  GetMem(Output, Size);

  input := Inbuf;

  Orig := Output;

  while input^ <> #0 do

  begin

    if (input^ <> #10) and (input^ <> #13) then

    begin

      output^ := input^;

      inc (output);

    end;

    inc (input);

  end;

  Output^ := #0;

  Output := Orig;

  InBuf := Output;

end;



I still wonder about that darn GetData thing! I would still like to not have

to use a TMemo! If Anyone out there can solve this problem I'd be very

greatfull! I'd give you a complimetary first hand look at my new printing

rountines! That's what this whole mess is for anyhow! So far I have implemented

text output in any font at any position in inches, and all that basic stuff!

But what I think is really cool, Is my dynamic grid! You can create a grid of

any number of rows and column. Assign text to and cell, setting its Horizontal

AND Vertical Justification, Select border style for each cell and many ways to

manipulate and print these!


Using integer pointers

Question


var

  myptr: PInteger;

begin

  GetMem(myptr, 10);

   (myptr + 1)^ := 1;       { How can I do this? }

  FreeMem(myptr, 10);

end;

Answer


A:

You would have to create the type first.



Type

	Pinteger : ^Integer;



Var

	MyPtr : Pinteger;



Possibly you have used a bad example but it does not seem to make sense to

use a (32 bit) pointer for a 16 bit value or to allocate 10 bytes for the

variable.



Pascal allows you to use NEW and DISPOSE which automatically allocates and

de-allocates the correct size block.



ie NEW(MyPtr) = GetMem(MyPtr, Sizeof(MyPtr)).



Presumably you wish to do a calculation on a variable number of integers.

In that case have a look at TList in the help. Better yet use a linear

array (or a pointer to one if there could be enough elements to make memory

an issue).



A:

For completeness, this should be



  NEW(MyPtr) = GetMem(MyPtr, SizeOf(MyPtr^));



SizeOf(MyPtr) will always be 4 bytes, as 16 bit pointer.



If I understand what you want, a dynamic array of integers, since you know

how many you want at runtime, you could also do



Type

  pIntArr = ^IntArr;

  IntArr  = Array[1..1000] of Integer;

Var

  MyPtr : pIntArr;

Begin



  GetMem(MyPtr, 10); { 10 = SizeOf(Integer) * 5  !!)

{  MyPtr[2]:=1; }

<<<< My turn to be incomplete !! >>>>

  MyPtr[2]^:=1;



  FreeMem(MyPtr,10);

End;



A:

It looks like Delphi makes a special case of pchar.  Best I could figure out

that would let you use the syntax you want is:



type

   intarray = array[0..20000] of integer;



procedure TForm1.Button1Click(Sender: TObject);

var

   xptr:  ^IntArray;

begin

   GetMem(xptr, 10);

   xptr^[idx] := 1;  { where idx is 0 to 4 since we have 10 bytes = 5 integers }

   FreeMem(xptr, 10);

end;



Note that you don't really need to allocate an array of 20,000, but Delphi

range checking won't work unless it is 20,000.  (Pointer users caveat

emptor!)


MS Binary Format / IEEE conversion

Question


Couple of questions:

1.  Does Delphi store real numbers in "Microsoft Binary Format" or "IEEE"?

2.  How can I call a small segment of assembler code within my Delphi code?

Can I embed assembler routines within Delphi?  Please note my assembler

exposure is very limited, but I have also wondered how I can do this, in

both C and now Delphi.

Answer


A:

"whatever the base-level machine uses" is not so straightforward before

Intel's 80x87 numeric coprocessors came along.  I'm not sure if the 80x86

processors had any native instructions to perform floating point

arithmetic.  This could be why Microsoft created their own proprietary

format for floating point numbers; they had to do all the arithmetic

themselves, using their own runtime library.  Today, the 80x87 makes the

arithmetic automatic, and IEEE is now the standard.



Delphi does store the following floating point types in IEEE format:

  Single      4 bytes

  Double      8 bytes

  Extended   10 bytes



Note that Real (6 bytes) is not on this list.  I may be wrong, but I

believe Real is an intrinsic Pascal type; its existence may predate the

80x87.



[Aside:  Delphi's online help says that, by default (via the $N+ compiler

directive), the compiler will generate code to perform ALL floating point

calculations using 80x87 instructions, including Real types.  So either the

compiler will generate calls to a runtime library to handle Real types, or

else I am completely wrong about the above! :) ]



Anyway, in checking Visual Basic's online help, I see that its data types

also include Single and Double, which are also IEEE, and are identical to

Delphi's Single and Double types.  However, there is no mention of

"Microsoft Binary Format".



I then dropped down to DOS and ran QBasic, which is Microsoft's old

QuickBasic interpreter that is now included in DOS.  If you check its

online help, you will see the following:



=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

MKSMBF$ and MKDMBF$ convert IEEE-format numbers to Microsoft-Binary-format

numeric strings that can be stored in FIELD statement string variables.

CVSMBF and CVDMBF convert those strings back to IEEE-format numbers.



MKSMBF$(single-precision-expression!)

MKDMBF$(double-precision-expression#)

CVSMBF (4-byte-numeric-string)

CVDMBF (8-byte-numeric-string)



   Function    Returns

   ========    ============================================================

   MKSMBF$     A 4-byte string containing a Microsoft-Binary-format number

   MKDMBF$     An 8-byte string containing a Microsoft-Binary-format number

   CVSMBF      A single-precision number in IEEE format

   CVDMBF      A double-precision number in IEEE format



   These functions are useful for maintaining data files created with

   older versions of Basic.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=



So, to sum up, if you want to access your "MetaStock" files, I think you

have 3 choices.



1.  Write your program in QBasic/DOS



2.  Find substitutes (hopefully compatible with Delphi) for the conversion

    functions mentioned above.



3.  Write these functions yourself.  You will have to find documentation

    for the bitwise layout of the old "Microsoft Binary Format" Single and

    Double types, perhaps in MS's old Basic manuals.


Array of the Image Object

Question


How do I implement an array of objects...lets say I want 10 image

controls on my form, but don't want a different name for each one...in

VB there was an index property...how do I do it in Delphi?

Answer


You cannot do this visually & straight off, but if you don't mind some coding

this is pretty simple:



type

  TForm1 = class(TForm)

    ...

  public

    images: array [1..10] of TImage;

    ...

  end;



procedure TForm1.FormCreate(...);

var i: integer;

begin

  ...

  for i := 1 to 10 do begin

    images[i] := TImage.Create(self);

    with images[i] do begin

      parent := self;

      tag := i; { makes it easier to detect e.g which 

      ... set other properties as required, e.g:

      OnClick := MyClickEventHndlr;

    end;

  end;

  ...

end;



To make sure you get all "uses" correct you may drop one such dynamic component

on your form, and then either delete it or set visible to false.



A more elaborate way is of course to design your own component to do the same.


Pass a record problem

Question


I have a Delphi application that has about two dozen simple type

declarations similar to the following:



type RecordA = record

  this             : Integer;

  that            : String;

  the_other : Integer;

end;



Each record type can have an essentially random number of fields in it.

I'm looking for a way to write a *generic* function that can be passed

a specific record variable and a 'field number' which will be able

to determine the name of the field, it's type and it's value.



A rough example:



recA    :  RecordA;

.....

recA.that  := 'Steve';

MyFunc(recA, 2);   { Give me details on the 2nd field in recA };



function MyFunc(rec: ????;  field : Integer);

begin

  { Do some magic }



  Label1.Caption := recordname;      { 'recA' }

  Label2.Caption := fieldname;           { 'that' }

  Label3.Caption := _type;                    { 'String' }

  Label4.Caption := value;                    { 'Steve' }

end;

Answer


A:

An idea, may not be what you're looking for, but here it is:



Define a base class, call it allrecs, eg.



   tAllrecs = class

      function getVal (field: integer): string; virtual;

   end;



Then derive a class for each record type, eg.



   recA = class (tAllrecs)

      this             : Integer;

      that            : String;

      the_other : Integer;

      function getVal (field: integer): string; virtual;

   end;



then for each class's function define what it returns:



   function recA.getVal (field: integer); string;

   begin

      case field of

         1: getVal := intToStr (this);

         2: getVal := that;

         3: getVal := intToStr (the_other);

      end;

   end;



Then you can define 



   function myFunc (rec: tAllrecs; field: integer);

   begin

      label2.caption := allrecs.getVal(field);

   end;



and you can then call myFunc with any class derived from tAllrecs, eg.

   myFunc (recA, 2);

   myFunc (recB, 29);



(Rather than a function, getVal will probably have to be a procedure with 3

var parameters so you can return name and type as well as value.)



BTW, please don't use "this" as a variable, it confuses us old C++

programmers :-)

("this" in C++ means the same as "self" in Delphi.)



Also, I did try this out so I have a small working project that does it.



[Sid Gudes, cougar@roadrunner.com]



A:

If you're willing to pass the whole record at a time, then set your

function/procedure to expect an 'array of const' (keeps typechecking

safe, warm & fuzzy). This is identical to 'array of TVarRec'.



See Delphi online help for the system constants defined for TVarRec.


Function pointer

Question


Is The a similar concept to pointers to functions in delphi?

I have a toolbar with speed buttons which can be used by a number of diffrerent

forms in an app. Depending on which form is currently active I would like to

execute a different funtion if a button is pressed. I would hate to do this

using a huge case statement, Ideally what I wanted to do is register a function

with the toolbar button when a form gets focus and then simply call that

function when the speed button is pressed.

Answer


A:

This is what I came up with when building a simple states machine:



This is a very simple example of using function pointers under

Borland Delphi to control program flow.  Just create a simple form with

one button and add the code from Unit1 to the unit created.  Add Unit2

to the project and compile.  Give me a yell if you have any problems.



interface



uses

  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,

  Forms, Dialogs, StdCtrls;



type

  TForm1 = class(TForm)

    Button1: TButton;

    procedure Button1Click(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;



var

  Form1: TForm1;

  CurrProc : LongInt;

  MyVal : LongInt;



implementation



uses Unit2;



{$R *.DFM}



procedure TForm1.Button1Click(Sender: TObject);

var

  NewProc : LongInt;

  MyString : string;

begin

  CurrProc := 2;                { beginning point in proc table }

  MyVal := 0;                   { dummy variable }

  NewProc := 0;                 { return value for next index in proc

table }

  while CurrProc < 6 do

    begin

    { execute the current index in the proc table and get the next proc

}

    NewProc := ProcTable[CurrProc](MyVal);



    { this is just to track the values of NewProc and CurrProc }

    FmtStr(MyString, 'NewProc [%d]  CurrProc [%d]', [NewProc,

CurrProc]);

    MessageDlg(MyString, mtInformation, [mbOK], 0);



    { set the current proc to the returned proc }

    CurrProc := NewProc;

    end;



end;



end.



{ This is a simple example of defining an array of function pointers }



interface



type

  { define Procs as a function }

  Procs = function(var ProcNum : LongInt): LongInt;



var

  { declare the array of function pointers }

  ProcTable : Array [1..5] of Procs;



{ function interface definitions }

function Proc1(var MyVal : LongInt) : LongInt; far;

function Proc2(var MyVal : LongInt) : LongInt; far;

function Proc3(var MyVal : LongInt) : LongInt; far;

function Proc4(var MyVal : LongInt) : LongInt; far;

function Proc5(var MyVal : LongInt) : LongInt; far;





implementation



uses Dialogs;



function Proc1(var MyVal : LongInt) : LongInt;

begin

  MessageDlg('Proc 1', mtInformation, [mbOK], 0);

  Proc1 := 6;

end;



function Proc2(var MyVal : LongInt) : LongInt;

begin

  MessageDlg('Proc 2', mtInformation, [mbOK], 0);

  Proc2 := 3;

end;



function Proc3(var MyVal : LongInt) : LongInt;

begin

  MessageDlg('Proc 3', mtInformation, [mbOK], 0);

  Proc3 := 4;

end;



function Proc4(var MyVal : LongInt) : LongInt;

begin

  MessageDlg('Proc 4', mtInformation, [mbOK], 0);

  Proc4 := 5;

end;



function Proc5(var MyVal : LongInt) : LongInt;

begin

  MessageDlg('Proc 5', mtInformation, [mbOK], 0);

  Proc5 := 1;

end;



initialization



  { initialize the contents of the array of function pointers }

  @ProcTable[1] := @Proc1;

  @ProcTable[2] := @Proc2;

  @ProcTable[3] := @Proc3;

  @ProcTable[4] := @Proc4;

  @ProcTable[5] := @Proc5;



end.



A:

     I think I would do something like this:

     Declare in each form procedures that handle the buttonpresses, like

     procedure CutButtonPressed(Sender:TObject) of Object;

     Then I would simply assign the buttons' OnClick events to these

     procedures in the forms OnActivate event. This would be the oop way to

     do it, but if you don't like it, I think Delphi still has function

     pointers.



A:

Define a base class form with an abstract function declaration for each

of the functions you want to call from your toolbar. Then derive each

of your forms from that base class form, and provide definitions for those

functions.

Eg: (There might be a couple of syntax errors here - I haven't compiled it)

type

  TBaseForm = class(TForm)

  public

    procedure Method1; virtual; abstract;

end;



type

  TDerivedForm1= class(TBaseForm)

  public

    procedure Method1; override;

  end;



  TDerivedForm2= class(TBaseForm)

  public

    procedure Method1; override;

  end;



  procedure TDerivedForm1.Method1;

  begin

    ....

  end;



  procedure TDerivedForm2.Method1;

  begin

    ....

  end;



{To call the function from your toolbar, get

the currently active form and call Method1}

procedure OnButtonClick;

var

  AForm: TBaseForm;

begin

  AForm := ActiveForm as TBaseForm;

  AForm.Method1;

end


Converting Real to a fraction of two integers

Question


I am using a rather slow function (see below) to convert a real

value to a fraction of two integers. Does anybody know of a better and

faster method as the one here presented?

Answer


A:

I have written a program that does just that. It's a dos program. You

call it with the decimal number passed as a parameter. It will print

3 columns, the first will be the continued fractions and the next two

will be the numerator and denominator. You will have to convert it

yourself to a function that you can call from your program, but that

should be to difficult.



To see how it works, I suggest that you try it as follows from the

DOS command line:



CONTFRAC 3.141592654



program contfrac;       { continued fractions }

{$N+}

const

        order   = 20;

var

        y,

        lasterr,

        error,

        x               : extended;



        a               : array[0..order] of longint;

        i,j,

        n               : integer;

        op,

        p,

        q               : longint;



begin

        lasterr := 1e30;

        val(paramstr(1), y, n);

        if n <> 0 then

                halt;

        x := y;

        a[0] := trunc(x);



        writeln;

        writeln(a[0]:20, a[0]:14, 1:14);



{ this is where the smarts are }



        for i := 1 to order do begin

                x := 1.0 / frac(x);

                a[i] := trunc(x);

                p := 1;

                q := a[i];

                for j := pred(i) downto 0 do begin

                        op := p;

                        p := q;

                        q := a[j] * q + op;

                        end;

                error := abs(y - int(q) / int(p));

                if abs(error) >= abs(lasterr) then

                        halt;

                writeln(a[i]:20, q:14, p:14, error:10);

                if error < 1e-18 then

                        halt;

                lasterr := error;

                end;

end.



A:

Here's an idea that I use that seems fast enough.  Here's the scheme:



We'll use the number 23.56.



Take your real number and do integer division by 1.



        23.56 div 1 = 23



Now subtract the result from the number you started with.



        23.56 - 23 = .56



To convert to int value just multiply by 100 if that is needed and recast.



             valA := (val div 100);

             valB := (valA - val); or valB := (valA - val) * 100;



                val = 23.56

                ValA = 23

                ValB = .56 or 56


Resizing (Dynamic) Arrays

Question


Coming from a background of higher level programming languages. I have

not had a lot of experience with array's in C++ or Pascal. I was

wondering if someone could clue me in on dynamic array's in delphi.

Answer


A:

There are several techniques for doing this.  The techniques depend on

whether you have an array of strings or an array of numbers (integer, real,

etc.).



1) If you just want a dynamic one-dimensional array of strings, I suggest

you look at the tStringList component, it takes care of all the management

overhead and is easy to use.



2) If you want a dynamic multi-dimensional array of strings, you can use

tStringList also (as long as the total number of elements is less than the

maximum for tStringList, I believe 16,000).  To do this, you write a

linearizing map function as follows:



Assume you have a three-dimensional array of strings, and the current

dimensions are [12,80,7], and you want to find element [n,m,x].  Then you

can resolve this to an element in a one-dimensional array by using

((n-1)*80*7 + (m-1)*80 + x).  You would then use this as an index into a

tStringList.  To dynamically alter one of the array bounds, use the move

method of tStringList to shuffle things around.  (This involves some

embedded loops but should be pretty quick to execute because tStringList

doesn't move the strings, only pointers to the strings.)



3) If you want a dynamic one-dimensional array of numbers, here's a general

technique, there are many other details.  Declare a pointer to an array type

that has the maximum number of elements for that type (remembering that

Delphi-16 only allows a type to occupy up to 64K), eg.

    type

       bigArray: array[1..32000] of integer;  {or ^double, etc.}

       pMyArray: ^bigArray;



then allocate the array using

   getMem (pMyArray, sizeof(integer) * n);



where n is the number of elements.  Then you can refer to an array element

using, eg.

   pMyArray^[51]



Don't forget to free the array after you're done with it using FreeMem.



To resize the array, define a new pointer, reallocate, and swap, eg.

   pTemp: ^bigArray;



   getMem (pTemp, sizeof(integer) * newnumelements);

   memcopy (pTemp, pMyArray, sizeof(integer)*n);

      {n is number of elements in pMyArray}

   freeMem (pMyArray, sizeof(integer)*n);

   pMyArray := pTemp;



4) To use a multi-dimensional array of numbers, combine the technique in (3)

above with the mapping function in (2) above.



5) If you need more than 64K in your array, you'll need to develop a

cascading list of pointers to chunks of memory, which is beyond what I can

explain here.



A:

I would encapsualte in an object. I use what I call my "Basic String

Object" (BSO), which does dynamic allocation and deallocation for 

strings of any size. Internally it is a PChar pointing to allocated

memory. Externally I have two properties: AsString and AsPChar. I have

various properties and methods allowing various methods of accessing

and manipulating the string.



I wrote my own malloc() calloc() and realloc() using a static private

TString object to track the allocated pieces.  This has worked wonderfully

for any time I need to grab a chunk of memory.



With the two I can allocate memory as needed (in chunks so as not to

waste too much CPU time), and is disposed of (when a certain amount of

slack exists -- again, so as to not waste too much CPU time).



Another idea that I like has already been presented (the open-ended

array). If you need bounds checking and/or dynamic resizing, you may 

be forced to use a method similar to what I've done with the string object

above, and use a default array property to allow for easy access. This 

allows you to use indices and types of any kind.



TMyDynamicObject =

...

 PROPERTY Array[ idx :LONGINT ]:TMyType READ GetArray WRITE PutArray DEFAULT;

...



VAR Mine :TMyDynamicObject;

...

Mine := TMyDynamicObject.Create;

FOR i := 10 TO 20 DO Mine[i] := {whatever}



{MONSTER MEMORY WASTER - unless you get really crazy and use hash tables }

Mine[-100000] := {whatever} 

Mine[+100000] := {whatever}



If you have a sparsely-populated array, using a hash table might

be profitable. I convert index values to strings and let TStrings do

the work when I'm really lazy and don't particularly care about the

overhead to build the conversion to strings.



A:

You can use TList (or TStringList.Objects) to store virtually whatever you

want!  TList.Items stores pointers to objects or records, but it doesn't do

anything with that pointer, so if you want you can typecast that to a longint, 

and not bother with the objects or records at all!  Here is an example of 

storing a list of integers in a TList:



var

  aList: TList;

  I : Integer;

  L : Longint;

begin

  aList := TList.Create;

  L := 93823;

  aList.Add(Pointer(L));

  aList.Add(Pointer(83293));

  for I := 1 to aList.Count do

    L := L + Longint(aList.Items[I-1]);

  aList.Free;

end;



You can have up to 16380 elements in a TList or TStringList.  Now here's an 

example of how to use a TList to store a record (or object):



type

  PMyRec = TMyRec;

  TMyRec = record

    Name: string[40];

    Addr : string[25];

    Comments: string;

    salary: Double;

  end;

var

  aList: TList;

  aRecPtr: PMyRec;

  I : Integer;

begin

  aList := TList.Create;

  New(aRecPtr);

  with aRecPtr^ do

  begin

    Name := 'Danno';

    Addr := 'unknown';

    Comments := 'What a guy!';

    Salary := 999000.00;

  end;

  aList.Add(aRecPtr);

  aList.Add(... );

  ...

  for I := 1 to aList.Count do

  begin

    aRecPtr := PMyRec(aList.Items[I-1]);

    {do something with the record}

  end;



{now dispose of all records, and the list object itself}

  for I := 1 to aList.Count do

    Dispose(PMyRec(aList.Items[I-1]));

  aList.Free;

end;


Use Free with records

Question


If I'm maintaining a TList full of pointers to records (NOT pointers to

objects), do I need to free the records it holds before freeing the TList

itself?  For example:



type

  PMyRecord: ^TMyRecord;

  TMyRecord = record

    MyString: string;

  end;



var

  MyRecord: PMyRecord;

  List: TList;

begin

  List := TList.Create;

  New(MyRecord);

  MyRecord^.MyString := 'Hi There';

  List.Add(MyRecord);

  MyList.Items[0].Free;  { <-- Do I Need This??? }

  List.Free;

end;

Answer


A:

You need to typecast the call to free with the right type, as follows:



var

     i    : integer;



begin

...

     for i := 0 to MyList.Count - 1 do

          dispose(PMyRecord(MyList[i]));

     MyList.Free;

end;



or



begin

     for i := 0 to MyList.Count - 1 do

          dispose(PMyRecord(MyList.items[i]));

     MyList.Free;



end;



Items is the default property, so you don't need to specify it, altough it's

ok if you do.



A:

Don't think of it as a function so much as a reserved word.  In the form:



        var

                p : ^mystruct;

        begin

                new(p);

                ...

                dispose(p);

        end;



the new() and dispose() operate exactly like the getmem() and freemem()

procedures except that the compiler supplies the number of bytes as the size

of the structure pointed to by the pointer variable.  The pointer must be a

typed pointer for this reason, so the following isn't valid:



        var

                p : pointer;

        begin

                new(p);

        end;



because there is no set size for the memory the pointer will point to.  On

the other hand, if you use getmem() and freemem(), you can allocate bytes to

an untyped pointer, as in:



        var

                p : pointer;

        begin

                getmem( p, 32767 );

                ...

                freemem( p, 32767 );

        end;


Create large arrays

Question


To be specific, I want to a 1 MEG array of bytes:

   BigArray : Array[0..999999] of Byte;

How can I do this in Delphi?

Answer


A:

In Delphi 16-bit, you can't do it directly.  In the new 32-bit, you _should_

be able to, but since it won't ship for a couple months I don't know yet.

(There are some beta testers out there who know, can they talk yet?)



In 16-bit Delphi, you need to work in chunks of 32K or 64K and map.  You can

do something like the following:



type

  chunk:     array[0..32767] of byte;

  pchunk:    ^chunk;

var

  BigArray:  array[0..31] of pChunk;



To create the array:



   for i := 0 to high(bigArray) do

      new (bigArray[i]);



To access the n'th byte within the array (n should be a longint):



   bigArray[n shr 15]^[n and $7FFF] := y;

   x := bigArray[n shr 15]^[n and $7fff];



   This will even do range checking if you have range checking set in your

options!

   n must be in the range [0..32*32*1024] = [0..1024*1024] = [0..1048576].



To free the array when you're all done:



   for i := 0 to high(bigArray) do

      dispose (bigArray[i]);


Pointer to a function

Question


I passed a pointer pointing to a procedure to my DLL.  How do I call

the procedure if all I have is the pointer.  I did use makeprocinstance to

get the pointer.

Answer


A:

This is what I normally use to call some functions from a DLL:



1. Declare a type:



type

  TYourDLLFunc = function(Parm1: TParm1; Parm2: TParm2): TParm3;



2. Declare a variable of that type:



var

  YourDllFunc: TYourDLLFunc;



3. Get the DLL handle:



  DLLHandle := LoadLibrary('YourDLL.DLL');



4. Get the address of the function:



  @YourDLLFunc := GetProcAddress(DLLHandle, 'YourDLLFuncName');



5. Use the YourDLLFunc variable as you would normally use the function, e.g.:



  Parm3 := YourDLLFunc(Parm1, Parm2);


Pass a function as a parameter

Question


Does anyone know how can I typecast a function's address into a

LongInt? Or is there a way to pass a function as a parameter?

Here is what I'm trying to do:



 function DllFunction(p: Pointer): Integer;

 far; external 'mydll';



 function foo: integer;

 begin

 result := 1;

 end;



 procedure test;

 var

 l: LongInt;

 begin

 l := Addr(foo);  { Compile Error!!! I tried @foo and }

                  { LongInt(foo), and they won't work neither. }

 { This is what I need. }

 DllFunction(foo);  { Compile Error!!! It can't take a function}

                    { as the parameter. }

 end;

Answer


A:

Sounds like what you need is a procedural type. Assuming that

DllFunction() wants functions that look like what you've described

above, something like the following should work: 



type

  TMyFuncType = function : integer ;



var

  MyFunc : TMyFuncType ;



function foo: integer;

begin

  result := 1;

end;



begin

  MyFunc := foo ;

  DllFunction( longint( MyFunc )) ;



You may also be able to get away with

  DllFunction( longint( @foo )) ;



though I'm not sure about all of the memory issues that may be involved 

with a .DLL calling a routine in another code segment like this, you may 

need to declare foo as far for this to work, or export it from the unit 

or something.



Also, depending on how DllFunction() is written, you might be able 

to declare it thusly, so that it does the typecast implicitly:



function DllFunction( p: TMyFuncType ): Integer; 

  far; external 'mydll';



so you wouldn't need the dummy MyFunc variable or the @ operator.



A:

You can pass functions as parameters in Delphi/Pascal. However you have to

set up a type for the compiler to use. Try the following (I actually

compiled and tested this):



unit Unit1;



interface



uses

  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,

  Forms, Dialogs, StdCtrls;



type

  TForm1 = class(TForm)

    Button1: TButton;

    Button2: TButton;

    procedure Button1Click(Sender: TObject);

    procedure Button2Click(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;



var

  Form1: TForm1;



implementation



{$R *.DFM}



type

	IntFunc = function: integer;



function DllFunction(iFunc: IntFunc): integer; far;

begin

    DllFunction := iFunc; {Notice this is a function call}

end;



function iFoo: integer; far;

begin

	iFoo := 1;

end;



procedure TestIFunc;

var

	i: integer;

begin

	i := DllFunction(iFoo);

end;



procedure TForm1.Button1Click(Sender: TObject);

begin

	TestIFunc;

end;



procedure TForm1.Button2Click(Sender: TObject);

begin

	Close;

end;



end.



A:

Two things you can do.  First, if you want to use longint to pass it, you

can use

   i := longint(@foo).



The other thing you can do is bypass all this longint stuff and call the dll

function with

   DLLfunction (@foo);



Note that if you're planning to call foo from within the DLL, you're

probably going to need a thunk to resolve DS; look at MakeProcInstance for

more info.


Array of TPoints

Question


I need to set up an array of TPoints.  Let's say I have 5 points

and I know their X and Y values.  How do I fill and array with those values

for Tpoints?

Answer


A:

Const

  ptarr : Array[0..4] Of TPoint =

   ((x:0; y:4),

      .

      .

    (x:4; y:4));


Function return type

Question


Is there a way, using Object Pascal, to write a function which

is able to return more than one data type, i.e., returning a

byte or double or record ...

Actually I need some kind of polymorphic function...

Answer


A:

You can do that in C++. You can do it in ObjectPascal too, here's

the code snippet :



// function Chameleon returns different types by raising exceptions



unit Unit1;



interface



uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

  StdCtrls;



type



  MyBoolean = class

  public

    Value : boolean;

  end;



  MyInteger = class

  public

    Value : integer;

  end;



  MyClass = class

  public

    Value : TStrings;

  end;



  TForm1 = class(TForm)

    Button1: TButton;

    procedure Button1Click(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

    procedure MyProc;

    function Chameleon : boolean;

  end;



var

  Form1: TForm1;



implementation



{$R *.DFM}



function TForm1.Chameleon : boolean;

var

  b : MyBoolean;

  i : MyInteger;

  c : MyClass;

  r : integer;

begin

  r := Random(3);

  case r of

  0 :  begin

         b := MyBoolean.Create;

         raise b;

       end;

  1 : begin

        i := MyInteger.Create;

        raise i;

      end;  

  2 : begin

        c := MyClass.Create;

        raise c;

      end;

  end;    

end;



procedure TForm1.MyProc;

begin

  try

    Chameleon;

  except

    on MyBoolean  do ShowMessage('Function returned class MyBoolean');

    on MyInteger do ShowMessage('Function returned class MyInteger');

    on MyClass do ShowMessage('Function returned class MyClass');

  end;

end;



procedure TForm1.Button1Click(Sender: TObject);

begin

  Chameleon;

end;



end.



A:

Take a look at the Variant datatype in D2: this kind of code



function AnyType(const TypeParm: integer): Variant;

begin

  case TypeParm of

    1:  Result := 1;

    2:  Result := 2.0;

    3:  Result := 'Three';

    4:  Result := StrToDate('4/4/1944');

  end;

end;



is crappy programming - but quite legal!



A:

The following code snipped declares 3 procedures all same ingoing

parameters and all different output (as long as the output is physically

the same here it is 4 bytes). I don't think it is possible to fool delphi

to return a string that way. This would crash the heap manager.



Just as you need, call the appropriate function. Every call goes to

MyFuncRetAnything and P1 determines the return type. If you want you can

write another layer which makes the typecasting for you.



3 calls, 1 code.



I know this is not really what you need, but it is another approach.

(you can return strings as PChar which is also 4 bytes). But you must use

some memory allocated by the calling proc (maybe pass the result as P2?).



{my form has 3 labels and 1 button and this code}



var

  MyFuncRetInt : Function (P1, P2 : Integer) : Integer;

  MyFuncRetBool : Function (P1, P2 : Integer) : LongBool;

  MyFuncRetPointer : Function (P1, P2 : Integer) : Pointer;



function MyFuncRetAnything (P1, P2 : Integer) : Integer;

var

  RetPointer : Pointer;

  RetBool : LongBool;

  RetInteger : Integer;

begin

  RetPointer := nil;

  RetBool := False;

  RetInteger := 4711;

  case P1 of

    1 : Result := Integer (RetPointer);

    2 : Result := Integer (RetBool);

    3 : Result := RetInteger;

  end;

end;



procedure TForm1.Button1Click(Sender: TObject);

begin

  if MyFuncRetBool (2, 1900) then

    Label1.Caption := 'True'

  else

    Label1.Caption := 'False';

  Label2.Caption := IntToStr (MyFuncRetInt (3, 1900));

  Label3.Caption := IntToHex (Integer (MyFuncRetPointer (1, 1900)), 16);

end;



initialization

  MyFuncRetInt := @MyFuncRetAnything;

  MyFuncRetBool := @MyFuncRetAnything;

  MyFuncRetPointer := @MyFuncRetAnything;

end.


Close    To Top
  • Prev Article-Programming:
  • Next Article-Programming:
  • Now: Tutorial for Web and Software Design > Programming > delphi > Programming Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction