• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 249
  • Last Modified:

A Challenge.....

I have the following defined.

Type
     Hdr = Record

     Stat   : Integer;
     Done : Boolean;
     Log    : TDateTime;
     Mem  : Cardinal;

End;

Procedures to work with the record of Hdr.

End.

Now, with that compiled as a dcu unit, i would ike to be able to use it in other programs where i can define another record type containing any structure i need (ie: integers, string[length], etc) and have it "appended" to Hdr so that the procedures in the dcu unit can work with the Hdr record + appended record type as if they were one (ie: all part of Hdr).

Note - the dcu must not "depend" on another record structure to work - ie: if i forget to include a new record structure in a program that uses the dcu unit above, the dcu unit needs to work as if nothing is wrong.

example of record defined in other unit/program at a later date

Type

    Append = Record

  {whatever data types i want here}

end;

1.  Is it possible to do what i am trying to do?
2.  What would i need to change in the Hdr record type definition to allow it to point to the new record structure so it can be included.




0
foxjax
Asked:
foxjax
  • 3
  • 2
  • 2
  • +4
1 Solution
 
LRHGuyCommented:
For example:

procedure ClearDone(var aData);
var
  Data:Hdr absolute aData;
begin
  Data.Done:=False;
end;

myrec=record
  myhdr:hdr;
  mydata:integer;
end;

var
  H:hdr;
  M:myrec;

ClearDone(H);
ClearDone(M);

Is that the idea?

To 'inherite' a record definition, you'll always have to include it in the new record definition, as shown above.
Writing the procedure to NOT check for datatype, as shown, allow you to pass any 'derived' record.
0
 
foxjaxAuthor Commented:
it's the other way around i am trying to do;

ie:

The following is fixed - compiled and just ready to include in a uses clause:

Type
    Hdr = Record

    Stat   : Integer;
    Done : Boolean;
    Log    : TDateTime;
    Mem  : Cardinal;

End;

Procedures to work with the record of Hdr.

End.

What i want to be able to do is create another record type and have Hdr able to include its definition as if it was part of it.

0
 
BlackTigerXCommented:
kinda like "inject" some types to the Hdr type?

you don't want to do something like

TNewRecord = record
  OldHrd:Hrd;
  NewTypes:Integer;
...etc
end

?
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
BlackTigerXCommented:
mistyped...

TNewRecord = record
  OldHrd:Hdr;
  NewTypes:Integer;
...etc
end
0
 
TheRealLokiSenior DeveloperCommented:
I'm not sure I've fully understood you, but perhaps this will be of use.

how about making a class with a virtual method

and in the base class you have

type
TMyClass = class(TOBject)
public
    headerrecord = record ... blah... end;
procedure writeprocedure; virtual;
procedure WriteHeadder;  // just writes the header
end;

procedure TMyClass.writeprocedure;
begin
    write header;    
end;

and in the descendent class
e.g.
type
    TMyDescendedClass = class(TMyClass)
public
   extrarecordstuff: record .. blah...end;
  procedure writeprocedure; override;

end;
procedure TMyDescendedClass.writeprocedure; virtual;
begin
    inherited;  // calls the writeprocedure from the base class (which writes the header)
//  .... write custom record stuff here
//  .... which appends to the file    
end;

so if you simply have an object of TMyClass. then calling
WriteProcedure, the only data written is the header
but if you create an object of TMyDescendedClass, then call th writeproicedure.
it writes the header and the extra stuff you want.

you can typecast it to be generic as well
e.g. (myobject as TMyClass).WriteProcedure; // allowing seamless use

if I've got the wrong end of the stick, let me know :)
0
 
LRHGuyCommented:
> Now, with that compiled as a dcu unit, i would ike to be able to use it in other programs where i can
> define another record type containing any structure i need (ie: integers, string[length], etc) and have
> it "appended" to Hdr so that the procedures in the dcu unit can work with the Hdr record + appended
> record type as if they were one (ie: all part of Hdr).

Doesn't sound like something you can easily do. Maybe a clearer example of the end result you'd expect would help.

Since the HDR unit wouldn't know anything about the "appended' fields, what use is it? What would the procedures do with the extra fields?

You could do something like this:

Hdr=record
  stat:integer;
{$I MOREHDR.INC}
end;

In MOREHDR.INC you would put the additional fields, like so:

extrafield:word;
extrastring:string[12];

1) MOREHDR.INC would have to exist to compile the HDR unit, but it could be empty.
2) The HDR unit will need to be recompiled whenever MOREHDR.INC changes.
0
 
Imthiyaz_phCommented:
How about adding a pointer as a field :

Type
     Hdr = Record

     Stat   : Integer;
     Done : Boolean;
     Log    : TDateTime;
     Mem  : Cardinal;

     Data: Pointer; /////////////////
End;

In this way, u can include any record or object in the Data pointer.

But I dont know how u r going to manipulate the additional data in the record in ur base unit, without knowing its type. I too got with  TheRealLoki's method of using a class instead of a record.
0
 
Wim ten BrinkCommented:
> 1.  Is it possible to do what i am trying to do?
Not if you just use records...
I am working on something similar, where I have a file that contains only object data. I need to be able to read and write pure binary data to this file so I implemented a base class (actually an interface type) as the base type for any object that is stored in this file. Basically, these are just a Read and a Write method, where a parameter to the filestream is passed as parameter.
Now, every unit that has one or more classes that want to be writeable to this file must create the class and support the base class (interface) so the system can access the methods it needs to read and write them. I also register the class with the filesystem with my own register-function. All this function does is add a reference to the class name and a function that will create an instance of this class in some array. When the filesystem reads an object from the file, it first searches in this list for the class name, then creates an object for this type by using the registered create-function and finally it uses this object to call it's read-method which will read the actual object data.
This system works quite well. I might put it online sooner or later... :-)

> 2.  What would i need to change in the Hdr record type definition to allow it to point to the new record structure so it can be included.
Since you can't inherit from records, there isn't much you can do here. Especially since your Hdr unit doesn't know anything about the other record structures, it can't do anything with it. However, you could think about some register function that would create links between data and the record structure and probably some functions that will process it. You could use this structure, for example:
Type
  Hdr = Record
    RecType: Integer;
    Stat   : Integer;
    Done : Boolean;
    Log    : TDateTime;
    Mem  : Cardinal;
  End;
  InheritsFromHdr = record
     InternalHdr = Hdr;
     SomeMore: ShortString;
  end;

The RecType would contain a value that would identify the contents of your record. You could create a dynamic array that links RecType values with their associated processing functions.
InheritsFromHdr shows how you could make other records look like Hdr, but with more data added to it. If you declare the first (not the second or any other field) field as type Hdr then they are near-similar data structures. Yes the new record will require more information.

For the registration, think about some structure like this:

type
  TReadMethod = procedure(var AHdr: Hdr);
  TWriteMethod = procedure(var AHdr: Hdr);
  RegisterHdr = record
    RecName: string;
    RecType: Integer;
    RecSize: Integer;
    ReadMethod: TReadMethod;
    WriteMethod: TWriteMethod;
  end;

var
  RegisteredHdr: array of RegisterHdr;

Now, all you have to do when some unit creates an "inherited" Hdr record is register that new recordtype in the dynamic array.
Your own base unit still won't know how to process the record, though. But because of this registration list it does know the size of the record and which functions to use to process it.
Even funnier, Borland used this technique about 15 years ago within Turbo Pascal version 6 for the Turbo Vision library, but they used objects instead of records.

About the parameter in the ReadMethod method, well... You have to typecast it in your code to whatever recordtype it is supposed to be. Something like:
procedure ReadMethod(var AHdr: Hdr);
var NewHdr: InheritedHdr;
begin
  NewHdr := InheritedHdr(AHdr);
end;

Haven't tested this though. Think you'll even have to use pointers to get it working correctly. Won't be very pretty...
0
 
foxjaxAuthor Commented:
so far i am impressed with the advice and methods offered.  Some very good points made that i never considered.

The HDR unit though, does not need to know the format of the extra record structure - just be able to manipulate it as a block and include it in calculations such as the overall size of the combined data within the record (ie: hdr record + new record type).

I'll leave this question open for a while longer to see if anyone has further comments or ideas.

As i said before - impressive!



0
 
Wim ten BrinkCommented:
If you don't need to know the record structure then just add the recordsize as an additional field to your structure. Thus:

Type
  Hdr = Record
    RecSize: Integer;
    Stat   : Integer;
    Done : Boolean;
    Log    : TDateTime;
    Mem  : Cardinal;
  End;

Now, this will just tell you the size of the block of data that you are manipulating. Windows uses some similar technique internally too at several places. The first field tells the record size, including the size field itself. All other data is whatever you like.
There is no other way to retrieve the recordsize since records are very minimal blocks of data. You can ask how big a record is by using the SizeOf() function but if you're using pointers to records or do typecasting, the recordsize you get might be incorrect. Thus, every time you create a record, set the RecSize field immediately with the SizeOf of the record type used.
0
 
DragonSlayerCommented:
To further elaborate:

maybe something like this?

Type
  Hdr = record
    RecSize: Integer;
    Stat: Integer;
    Done: Boolean;
    Log: TDateTime;
    Mem: Cardinal;
    Buffer: PChar;
  end;

and your methods:

procedure ReadData(const SomeFile: string; var AHdr: Hdr);
var
  F: file;
begin
  AssignFile(F, 'test.dat');
  Reset(F, 1);
  try
    GetMem(AHdr.Buffer, AHdr.RecSize);
    BlockRead(F, AHdr.Buffer^, AHdr.Size);
  finally
    CloseFile(F);
  end;
end;

something like that?

Of course, you would have to manually free the pointer afterwards:

FreeMem(MyHdr.Buffer);
0
 
DragonSlayerCommented:
So, let's say the data you want to use is actually declared as such:

type
  TSomeContent = record
    Int1: Integer;
    Double1: Double;
    Text: string[256];
  end;

I guess you can do something like this:

var
  MyHdr: Hdr;
  MyContent: TSomeContent;

...

MyHdr.RecSize := SizeOf(TSomeContent);
ReadData('test.dat', MyHdr);
Move(MyHdr.Buffer, MyContent, MyHdr.RecSize);
FreeMem(MyHdr.Buffer);

0
 
DragonSlayerCommented:
Oops, some mistake in the AssignFile part of ReadData:

procedure ReadData(const SomeFile: string; var AHdr: Hdr);
var
  F: file;
begin
  AssignFile(F, SomeFile);
  Reset(F, 1);
  try
    GetMem(AHdr.Buffer, AHdr.RecSize);
    BlockRead(F, AHdr.Buffer^, AHdr.Size);
  finally
    CloseFile(F);
  end;
end;
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 3
  • 2
  • 2
  • +4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now