Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2005-05

type definitions etc

16 messages · 8 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Hans Maartensson

For quite some time I have been using FreePascal for occasional programming (windows programs); it works fine, so I don't post so many stupid questions to this list any more.

But now I have downloaded the new version 2, driven by the enthusiasm in the announcement, and trying to compile some of my old programs with the new version rose a few questions.

In a program I had a recursive definition like:

type sometype = record
   n: longint;
   p: ^sometype
   end;

This worked in version 1.0, but in version 2.0 the compiler complains that the type is not fully defined. Is there a good reason why that is not allowed any more.? It was practical for making chains of data structures.

Also the following call to a win32 API function is not allowed any more:

invalidaterect(windows, 0, true);

The error is that the 2nd parameter should be of type 'rect'. I know that careful type checking is a fundamental part of pascal philosophy, and this is good for preventing many programming errors. But on the other hand some windows API functions make use of the rule that substituting null for some type makes the function use a default value. Is there a general way to get around this problem? The 2nd parameter in this example must be of 'var' type, which means that it is the pointer to the parameter that is transferred to the function. Transferring a nil pointer is in pascal language the same as leaving the parameter undefined. But that is not possible in FreePascal, is it?

With the installation of version 2, I had the problem that the figures are missing from the html-documentation.
I downloaded the zipped file from the special link and unpacked it to D:\FPC\2.0.0\ and it went into the already existing doc directory. But some (or may be all) of the figures are missing from the text.

Finally I would like to ask about the following.
The compiler does not allow a procedure definition like:

procedure someproc(p: ^double);

whereas the following is perfectly possible:

type Tpdouble = ^double;
procedure f(p: Tpdouble);

Is there a good reason for this? Why isn't ^double a valid type everywhere?

Hans Maartensson

Vinzent Hoefler
to Hans Maartensson
Hans Maartensson wrote 6 lines
In a program I had a recursive definition like: type sometype = record n: longint; p: ^sometype end;

This was never allowed in Pascal, AFAIK. The standard solution is to define the pointer type first:

|type
| ptr_sometype = ^sometype;
|type
| some_type =
| record
| p : ptr_sometype;
| ...
| end;

Show quoted text 6 lines
Also the following call to a win32 API function is not allowed any more: invalidaterect(windows, 0, true); The error is that the 2nd parameter should be of type 'rect'.

Have you actually tried "InvalidateRect (windows, NIL, True);"?

Show quoted text 5 lines
The 2nd parameter in this example must be of 'var' type, which means that it is the pointer to the parameter that is transferred to the function. Transferring a nil pointer is in pascal language the same as leaving the parameter undefined. But that is not possible in FreePascal, is it?

It is, because of function overloading:

function InvalidateRect(hWnd:HWND; var lpRect:RECT;
bErase:WINBOOL):WINBOOL; external 'user32' name 'InvalidateRect';
function InvalidateRect(hWnd:HWND;lpRect:LPRECT;
bErase:WINBOOL):WINBOOL; external 'user32' name 'InvalidateRect';

So the second form takes a pointer, thus passing NIL should be perfectly allowed, AFAICS.

Show quoted text 12 lines
Finally I would like to ask about the following. The compiler does not allow a procedure definition like: procedure someproc(p: ^double); whereas the following is perfectly possible: type Tpdouble = ^double; procedure f(p: Tpdouble); Is there a good reason for this? Why isn't ^double a valid type everywhere?

Because

type1 = ^double;
type2 = ^double;

should be considered different types. So if you define the type in the procedure declaration, you couldn't call it, just because you can't declare a variable of the same type for the argument. The only allowed argument would be NIL, which I think is a little bit pointless.

BTW, why don't you use "procedure f(const/var/out p : double);" instead to get rid of as much pointers as possible and use the right parameter modes which also have the side effect of being more self documenting?

Vinzent.

Hans Maartensson
to Vinzent Hoefler
you wrote 7 lines
> ..... > type sometype = record > n: longint; > p: ^sometype > end; This was never allowed in Pascal, AFAIK.

FPC version 1.0.12 compiled it OK

Show quoted text 11 lines
The standard solution is to define the pointer type first: |type | ptr_sometype = ^sometype; |type | some_type = | record | p : ptr_sometype; | ... | end;

OK, I didn't try that, so it is allowed to define a type as a pointer to a type that is defined later.

Show quoted text 6 lines
> Also the following call to a win32 API function is not allowed any > more: > > invalidaterect(windows, 0, true); Have you actually tried "InvalidateRect (windows, NIL, True);"?

...

I have overlooked that, you are right, it works, due to function overloading, as you explain. So in the old version it was 0, now it is nil, OK.

Show quoted text 22 lines
> Finally I would like to ask about the following. > The compiler does not allow a procedure definition like: > > procedure someproc(p: ^double); > > whereas the following is perfectly possible: > > type Tpdouble = ^double; > procedure f(p: Tpdouble); > > Is there a good reason for this? Why isn't ^double a valid type > everywhere? Because type1 = ^double; type2 = ^double; should be considered different types. So if you define the type in the procedure declaration, you couldn't call it, just because you can't declare a variable of the same type for the argument. The only allowed argument would be NIL, which I think is a little bit pointless.

OK that makes sense.

BTW, why don't you use "procedure f(const/var/out p : double);" instead to get rid of as much pointers as possible and use the right parameter modes which also have the side effect of being more self documenting?

I do avoid pointers when I can, but sometimes it is not known, how many variables are needed before the program is running. So you let the program allocate some memory, put the values in there, and then you need pointers to keep track of then.

On the other hand, I just read in the documentation that the new version of FPC (I used v.1.0.12 before) has something called dynamic array. Those arrays will fix that kind of problem, I really like that new feature.

Thank you very much for the help.

Hans Maartensson

Vinzent Hoefler
to Hans Maartensson
Hans Maartensson wrote 10 lines
At 14:50 26-05-2005, you wrote: > > ..... > > type sometype = record > > n: longint; > > p: ^sometype > > end; > >This was never allowed in Pascal, AFAIK. FPC version 1.0.12 compiled it OK

Then it looks more like a bug.

OK, I didn't try that, so it is allowed to define a type as a pointer to a type that is defined later.

Yes, but _only_ if the type declaration comes _immediately_ after the pointer type declaration. So you can't just declare a bunch of pointer types and defer the actual type-declaration to a later point, that won't work.

I do avoid pointers when I can, but sometimes it is not known, how many variables are needed before the program is running. So you let the program allocate some memory, put the values in there, and then you need pointers to keep track of then.

Yes. But even then you can dereference your pointers early and not carry them through the whole program.

I mean, a "procedure Foo (var V : Double);" can still be called with a pointer variable like "Foo (pd^)" and then used as normal variable in subsequent routines. This can even eliminate lots of (unnecessary) NIL checks. (I recently converted a lot of pointer stuff that way, the original was more or less a one-to-one translation from C-code, so you can perhaps imagine...).

On the other hand, I just read in the documentation that the new version of FPC (I used v.1.0.12 before) has something called dynamic array. Those arrays will fix that kind of problem, I really like that new feature.

That might be another option. I haven't tried it yet, so I can't say anything about it, but if it works properly, it looks even better.

Vinzent.

Jeff Pohlmeyer
Show quoted text 7 lines
> OK, I didn't try that, so it is allowed to define a type > as a pointer to a type that is defined later. Yes, but _only_ if the type declaration comes _immediately_ after the pointer type declaration. So you can't just declare a bunch of pointer types and defer the actual type-declaration to a later point, that won't work.

To clarify, you can actually have many additional types between the pointer type and the record definition, as long as you don't introduce another section keyword that "turns off" the current "type" section.

For instance, this will work:

  type
    pMyRec = ^tMyRec; // forward declaration
    MyInt = integer;
    MyArray = array[0..255] of char;
    { Hundreds more typedef's could go here }
    tMyRec = Record // actual definition
      data:pointer;
      next:pMyRec;
    end;

But the next example will NOT work, because the
additional "type" keyword separates the two types:

  type  pMyRec = ^tMyRec;
  type  tMyRec = Record
      data:pointer;
      next:pMyRec;
    end;
listmember
to Jeff Pohlmeyer
Jeff Pohlmeyer wrote 30 lines
To clarify, you can actually have many additional types between the pointer type and the record definition, as long as you don't introduce another section keyword that "turns off" the current "type" section. For instance, this will work: type pMyRec = ^tMyRec; // forward declaration MyInt = integer; MyArray = array[0..255] of char; { Hundreds more typedef's could go here } tMyRec = Record // actual definition data:pointer; next:pMyRec; end; But the next example will NOT work, because the additional "type" keyword separates the two types: type pMyRec = ^tMyRec; type tMyRec = Record data:pointer; next:pMyRec; end;

I can see what you mean, but I fail see the wisdom behind the restriction.

Why should it matter if we used 2 'type' keywords
instead of one --we are, after all, in the same
declaration sestion of the same unit... Why should it matter?

Cheers,
Ray

Florian Klaempfl
to listmember
listmember wrote 40 lines
Jeff Pohlmeyer wrote: > To clarify, you can actually have many additional types between > the pointer type and the record definition, as long as you > don't introduce another section keyword that "turns off" > the current "type" section. > For instance, this will work: > > type > > pMyRec = ^tMyRec; // forward declaration > > MyInt = integer; > MyArray = array[0..255] of char; > > { Hundreds more typedef's could go here } > > tMyRec = Record // actual definition > data:pointer; > next:pMyRec; > end; > > > But the next example will NOT work, because the additional "type" > keyword separates the two types: > > type pMyRec = ^tMyRec; > type tMyRec = Record > data:pointer; > next:pMyRec; > end; I can see what you mean, but I fail see the wisdom behind the restriction. Why should it matter if we used 2 'type' keywords instead of one --we are, after all, in the same declaration sestion of the same unit... Why should it matter?

type starts a new section without this restriction you could say why doesn't work:

type
  precord = ^trecord
var
  r : trecord;
type
  trecord = record
  end;

It's simply the philosophy of pascal: things which need not to work don't work. The extra type gives no gain, so why should be allowed? It makes
- reading the code harder
- work for the compiler harder => slower and more error prone compiler

Show quoted text 7 lines
Cheers, Ray _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
listmember
to Florian Klaempfl
Florian Klaempfl wrote
It's simply the philosophy of pascal: things which need not to work don't work. The extra type gives no gain, so why should be allowed?

I am not sure it is one of those things that need not work.

You could look at it from a different perspective: It does not add any ambiguity to then disallow it.

I mean, is there really a difference between this:

type
  pMyRec = ^tMyRec;
type
   tMyRec = Record
     data:pointer;
     next:pMyRec;
   end;

and this:

type
   pMyRec = ^tMyRec;
   tMyRec = Record
     data:pointer;
     next:pMyRec;
   end;

especially since there isn't a different kind of declaration between the first 'type' and the second --and even if there were.

It makes - reading the code harder - work for the compiler harder => slower and more error prone compiler

Interesting... this little bit of flexiblity would make life that hard for the compiler?

So, these 2 different forms are theated differently by the compiler too?

This:

const SOME_NUMBER1 = 1;
const SOME_NUMBER2 = 2;
const SOME_NUMBER3 = 3;
const SOME_NUMBER4 = 4;

and this:

const
   SOME_NUMBER1 = 1;
   SOME_NUMBER2 = 2;
   SOME_NUMBER3 = 3;
   SOME_NUMBER4 = 4;

Cheers,
Ray

Matthias Hryniszak
to listmember

----- Original Message -----

Earlier message 47 lines
From: "listmember" To: "FPC-Pascal users discussions" Sent: Sunday, May 29, 2005 12:48 PM Subject: [fpc-pascal] Re: > Florian Klaempfl wrote: > >> It's simply the philosophy of pascal: things which need not to work >> don't work. The extra type gives no gain, so why should be allowed? > > I am not sure it is one of those things that need not work. > > You could look at it from a different perspective: It does not > add any ambiguity to then disallow it. > > I mean, is there really a difference between this: > > type > pMyRec = ^tMyRec; > > type > tMyRec = Record > data:pointer; > next:pMyRec; > end; > > and this: > > type > pMyRec = ^tMyRec; > > tMyRec = Record > data:pointer; > next:pMyRec; > end; AFAIK the declaration type PMyRecord = ^TMyRecord is only a forward declaration, that means the TMyRecord type has not been fully defined yet. Therefore it must be defined in the same section (at least that's what my good old teacher used to say). Matthias.
Florian Klaempfl
to listmember: I am not sure it is one of those things that need not work. You could look at it from a…
listmember wrote 7 lines
Florian Klaempfl wrote: > It's simply the philosophy of pascal: things which need not to work > don't work. The extra type gives no gain, so why should be allowed? I am not sure it is one of those things that need not work.

What would be the gain?

You could look at it from a different perspective: It does not add any ambiguity to then disallow it.

See my reasons I gave earlier in this thread but there is no reason to allow it.

Show quoted text 33 lines
I mean, is there really a difference between this: type pMyRec = ^tMyRec; type tMyRec = Record data:pointer; next:pMyRec; end; and this: type pMyRec = ^tMyRec; tMyRec = Record data:pointer; next:pMyRec; end; especially since there isn't a different kind of declaration between the first 'type' and the second --and even if there were. > It makes > - reading the code harder > - work for the compiler harder => slower and more error prone compiler Interesting... this little bit of flexiblity would make life that hard for the compiler?

Well, don't forget that adding more flexibility could also be abused, if you example worked, why shouldn't

type
 pMyRec = ^tMyRec;
var
  i :longint;
type
  tMyRec = Record
    data:pointer;
    next:pMyRec;
  end;

work. There is simply a limit in flexibility where it gives no extra gain anymore.

Show quoted text 18 lines
So, these 2 different forms are theated differently by the compiler too? This: const SOME_NUMBER1 = 1; const SOME_NUMBER2 = 2; const SOME_NUMBER3 = 3; const SOME_NUMBER4 = 4; and this: const SOME_NUMBER1 = 1; SOME_NUMBER2 = 2; SOME_NUMBER3 = 3; SOME_NUMBER4 = 4;

Internally of course, the second is compiled faster, you won't notice it though in this example ;)

listmember
to Florian Klaempfl
Florian Klaempfl wrote
>I am not sure it is one of those things that need not work. What would be the gain?
Unless the compiler generates some sort of hint, or warning;
the loss (i.e. negative gain) is a needlesly confused developer.
Show quoted text 19 lines
>Interesting... this little bit of flexiblity would make >life that hard for the compiler? Well, don't forget that adding more flexibility could also be abused, if you example worked, why shouldn't type pMyRec = ^tMyRec; var i :longint; type tMyRec = Record data:pointer; next:pMyRec; end; work.

IMHO it should work too :-)

After all, all these declarations are in the same unit, so we do not have any namespace conflicts to worry about.

Is there any reason that forward declarations be immediately above or below what they refer to?

There is simply a limit in flexibility where it gives no extra gain anymore.

Should I understand this as something to do with the current compiler being written from ground up; i.e. some features will be added as the codebase matures?

Show quoted text 20 lines
>So, these 2 different forms are theated differently by the >compiler too? > >This: > >const SOME_NUMBER1 = 1; >const SOME_NUMBER2 = 2; >const SOME_NUMBER3 = 3; >const SOME_NUMBER4 = 4; > >and this: > >const > SOME_NUMBER1 = 1; > SOME_NUMBER2 = 2; > SOME_NUMBER3 = 3; > SOME_NUMBER4 = 4; Internally of course, the second is compiled faster, you won't notice it though in this example ;)

Yes, but both are allowed..

Cheers,
Ray

Florian Klaempfl
to listmember
listmember wrote 10 lines
Florian Klaempfl wrote: >> I am not sure it is one of those things that need not work. > > > What would be the gain? Unless the compiler generates some sort of hint, or warning; the loss (i.e. negative gain) is a needlesly confused developer.

No, he should simply know the language he uses. Understanding the concept of forward declarations is harder than to understand that they must be in the same type block.

Anyways, it's how pascal works and there is absolutely no need to support it. Or do you think british people are confused by the fact that they have to use program instead of programme?

Show quoted text 32 lines
>> Interesting... this little bit of flexiblity would make >> life that hard for the compiler? > > > Well, don't forget that adding more flexibility could also be > abused, if you example worked, why shouldn't > > type > pMyRec = ^tMyRec; > > var > i :longint; > > type > tMyRec = Record > data:pointer; > next:pMyRec; > end; > > work. IMHO it should work too :-) After all, all these declarations are in the same unit, so we do not have any namespace conflicts to worry about. Is there any reason that forward declarations be immediately above or below what they refer to?

So this is ok as well:

 type
  pMyRec = ^tMyRec;
 var
   i :tMyRec;
 type
   tMyRec = Record
     data:pointer;
     next:pMyRec;
   end;

I see no reason why it should work ;)
and:

 type
  pMyRec = ^tMyRec;
 var
   i :tMyRec;
 procedure p;
   begin
     i.data:=nil;
   end;
 type
   tMyRec = Record
     data:pointer;
     next:pMyRec;
   end;

is ok as well? It could be valid with the same arguments because if it doesn't work people could get confused.

Show quoted text 40 lines
> There is simply a limit in flexibility where it gives no extra > gain anymore. Should I understand this as something to do with the current compiler being written from ground up; i.e. some features will be added as the codebase matures? >> So, these 2 different forms are theated differently by the >> compiler too? >> >> This: >> >> const SOME_NUMBER1 = 1; >> const SOME_NUMBER2 = 2; >> const SOME_NUMBER3 = 3; >> const SOME_NUMBER4 = 4; >> >> and this: >> >> const >> SOME_NUMBER1 = 1; >> SOME_NUMBER2 = 2; >> SOME_NUMBER3 = 3; >> SOME_NUMBER4 = 4; > > > Internally of course, the second is compiled faster, you won't > notice it though in this example ;) Yes, but both are allowed.. Cheers, Ray _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Ragnar Schroder
to Florian Klaempfl
... they must be in the same type block. Anyways, it's how pascal works

I'd like to echo that. Let Pascal stay Pascal. Simple syntax and fast compiler. I for one really appreciate the Pascal way, even though I've mostly used other languages in the past.

Greetings, Ragnar Schroder
listmember
to Florian Klaempfl: No, he should simply know the language he uses. Understanding the concept of forward…
Show quoted text 15 lines
So this is ok as well: type pMyRec = ^tMyRec; var i :tMyRec; type tMyRec = Record data:pointer; next:pMyRec; end; I see no reason why it should work ;)

You're taking the analogy too far now :-)

The compiler should get back with a message saying "Error: tMyRec is not fully defined yet" at the point where the code has

   var
     i :tMyRec;
Show quoted text 20 lines
and: type pMyRec = ^tMyRec; var i :tMyRec; procedure p; begin i.data:=nil; end; type tMyRec = Record data:pointer; next:pMyRec; end; is ok as well?

No, not OK.

Here, when the compiler meets

  var
    i :tMyRec;

it should emit a 'not fully defined yet' error and stop.

Once it stops, it never reaches 'procedure p' anyway, so we need not discuss that case.

In short, as long as the compiler catches 'not fully defined yet' stuff, I have no issues with where and how far apart they are forward declared.

It could be valid with the same arguments because if it doesn't work people could get confused.

AFAIK, there is no restriction wrt vicinity of forward declarations --as long as they are in (or belong to) the same unit (namespace).

And, yes, of course, you're not supposed to declare/use a variable unless it is fully defined.

But, here we are talking about variable decalarations, not type decalarations.

listmember
to Ragnar Schroder: I'd like to echo that. Let Pascal stay Pascal. Simple syntax and fast compiler. I for…
Ragnar Schroder wrote 7 lines
> ... they must be in the same type block. > Anyways, it's how pascal works I'd like to echo that. Let Pascal stay Pascal. Simple syntax and fast compiler. I for one really appreciate the Pascal way, even though I've mostly used other languages in the past.

:-)

Thing is, not only do I love Pascal, but --here is the difference-- I have always used Pascal, all my life :-)