Hello sdl4fp-users,
As some of you might remember, I wanted to use the SDL also with GNU Pascal in my project AKFQuiz. I tried to fiddle with the source code of SDL4Freepascal. But there was too much code to change just to find out how it works...
So I decided to leave SDL4fp away and implement the few things I need from scratch.
My project now is fully functional with GPC and SDL. It is not yet in the released version, but you can check out the CVS as described on the homepage (see link at the end of this mail).
Now I want to share my findings with you, so probably you can use it for SDL4Freepascal...
First of all, the types for variables are very different. SDL4fp uses "LongInt" for what is "int" in C - because LongInt happens to be 32Bit wide in FPC. But in GPC a LongInt is "at least" :-) 64bit wide! So it doesn't fit.
Older versions of GPC intentionally kept the variable types compatible to their counterparts in C. Newer versions broke with that and introduced speacial types, like CInteger or CLongInt.
Another difference to FPC is, that in GPC you have a more fine grained access to determine attributes, so you have really control about the size of variables.
Here is the code snipplet for the types (from my "qsys.pas"):
----------------------------------------------------------------------- {$IfDef __GPC__}
type Uint8 = Cardinal attribute (Size = 8); Uint16 = Cardinal attribute (Size = 16); Sint16 = Integer attribute (Size = 16); Uint32 = Cardinal attribute (Size = 32); Sint32 = Integer attribute (Size = 32); pByte = ^Uint8;
{$if __GPC_RELEASE__ < 20041218} type CInteger = Integer; {$EndIf} { __GPC_RELEASE__ }
{$Else} { not __GPC__ }
type CInteger = LongInt; { @@@ for 32-Bit systems } CString = PChar; Uint8 = byte; Uint16 = word; Sint16 = SmallInt; Uint32 = cardinal; Sint32 = LongInt; pByte = ^Uint8;
{$EndIf} { not __GPC__ } -----------------------------------------------------------------------
Note that you have to change "LongInt" to "CInteger" and "PChar" to "CString" in SDL4fp.
SDL4fp links to the library by using the name 'SDL' after the "external" modifier. This doesn't work in GPC (yet). In GPC you have to use the compiler directive "{$L SDL}". In FPC there's also a possability to use it in a similar way with "{$LinkLib SDL}"... well it works, but not in every situation. So I decided to keep the name after the external clause, but with "IfDef"s around it. GPC compiles it without the IfDef, but there are lot of warning messages then.
In FPC you must use the modifier "cdecl". In GPC that is the default, so you don't need it there. You can however declare it with "attribute(cdecl)", so you can simply write a macro to modify the "cdecl": "{$Define cdecl attribute(cdecl)}".
C is case sensitive, while Pascal is not. So in GPC all Pascal identifiers are converted to lowercase at an early stage, this includes the procedure names. So you have a problem when your C identifiers need uppercase letters. This can only be solved by using an attribute which mentions the name again with its capitalisation: "name 'Example'". This name isn't converted to lower case, since it's a string-constant. FPC doesn't need that, but it doesn't harm there either.
putting everything together it looks like in this example:
procedure SDL_WM_SetCaption(title, icon: CString); cdecl; external {$IfDef FPC}'SDL'{$EndIf} name 'SDL_WM_SetCaption';
Well I still have a problem with CString parameters. FPC needs a type cast, while GPC doesn't accept a type cast here. I "solved" it again with "IfDef"s. Does anybody know a better solution?
Please have a look at my source code for more information: http://cvs.sv.nongnu.org/viewcvs/akfquiz/srcbin/?root=akfquiz The relevant units are "sdlgrph.pas" and "sdlsnd.pas". The type definitions are in "qsys.pas".
If you use my findings in SDL4fp, it would be nice, if you would mention me there as "AKFoerster".
Related Links:
GNU Pascal: http://www.gnu-pascal.de/ Free Pascal: http://www.freepascal.org/ SDL: http://www.libsdl.org/ SDL4Freepascal: http://sdl4fp.sourceforge.net/ AKFQuiz: http://akfquiz.nongnu.org/
Andreas K. Foerster wrote:
First of all, the types for variables are very different. SDL4fp uses "LongInt" for what is "int" in C - because LongInt happens to be 32Bit wide in FPC. But in GPC a LongInt is "at least" :-) 64bit wide! So it doesn't fit.
Older versions of GPC intentionally kept the variable types compatible to their counterparts in C. Newer versions broke with that and introduced speacial types, like CInteger or CLongInt.
Only CInteger (and its unsigned variants), there's no CLongInt.
(Background: Originally, some 10 years ago, we decided to match GPC's "Integer" to C's "int", for "natural" header conversion, expecting that "int" in C would follow the word size, so just as it changed from 16 bit on 16 bit targets to 32 bit on most 32 bit targets, it would change to 64 bit on 64 bit targets. Later, seeing that this didn't happen, instead C seems to fix "int" at 32 and requiring "long" for 64 bit types on most 64 bit targets, and given that "Integer" has a special status in Pascal, as it's the only predefined integer type in standard Pascal, and the base type of integer subranges, and thus the type in which arithmetic operations are performed etc., we followed a suggestion by Scott Moore, to increase "Integer" to 64 bit on 64 bit targets, which prompted the addition of a new type, "CInteger", which always matches C's "int". We did the same for unsigned types, even though "Cardinal" and "Word" are not in standard Pascal. For other types, C compatibility rules haven't changed, e.g. "MedInt" is and was compatible to C's "long int".)
Note that you have to change "LongInt" to "CInteger" and "PChar" to "CString" in SDL4fp.
BTW, GPC support both "PChar" and "CString" built-in. However, indeed I prefer to write "CString" when I mean a pointer to a "C string" (i.e., Chr(0)-terminated array of chars), instead of a pointer to a single character (which "PChar" is according to a common way of notation).
SDL4fp links to the library by using the name 'SDL' after the "external" modifier. This doesn't work in GPC (yet). In GPC you have to use the compiler directive "{$L SDL}". In FPC there's also a possability to use it in a similar way with "{$LinkLib SDL}"... well it works, but not in every situation. So I decided to keep the name after the external clause, but with "IfDef"s around it. GPC compiles it without the IfDef, but there are lot of warning messages then.
One way to avoid the ifdefs might be a macro that expands to "external 'SDL'" in FPC, and just "external" in GPC (and perhaps taking care of the "cdecl" as well for FPC, to further save typing).
C is case sensitive, while Pascal is not. So in GPC all Pascal identifiers are converted to lowercase at an early stage, this includes the procedure names. So you have a problem when your C identifiers need uppercase letters. This can only be solved by using an attribute which mentions the name again with its capitalisation: "name 'Example'".
Even when they don't need any uppercase letters, it's recommended to always specify the linker name for external declarations.
Well I still have a problem with CString parameters. FPC needs a type cast, while GPC doesn't accept a type cast here. I "solved" it again with "IfDef"s. Does anybody know a better solution?
Perhaps another macro (just a quick idea, as I actually don't like macros too much), that does a type-cast in FPC and nothing in GPC. (Perhaps it could even be a type equal to CString in FPC, though with another name, working as a type-cast by itself, and a nop-macro in GPC.)
Frank
Am Mittwoch, dem 01. Nov 2006 schrieb Frank Heckenbach:
Only CInteger (and its unsigned variants), there's no CLongInt.
Okay, my fault.
Note that you have to change "LongInt" to "CInteger" and "PChar" to "CString" in SDL4fp.
BTW, GPC support both "PChar" and "CString" built-in. However, indeed I prefer to write "CString" when I mean a pointer to a "C string" (i.e., Chr(0)-terminated array of chars), instead of a pointer to a single character (which "PChar" is according to a common way of notation).
I knew that there is PChar in GPC, but I also prefer CString because of it's name.
One way to avoid the ifdefs might be a macro that expands to "external 'SDL'" in FPC, and just "external" in GPC (and perhaps taking care of the "cdecl" as well for FPC,
That's a very good idea. I've done it this way now:
{$IfDef __GPC__} {$L SDL} {$DEFINE libSDL external name} {$EndIf}
{$IfDef FPC} {$MACRO ON} {$DEFINE libSDL:=cdecl; external 'SDL' name} {$EndIf}
[...]
function SDL_Init(flags: Uint32): CInteger; libSDL 'SDL_Init';
to further save typing).
It's not for the typing, but the result is much more readable.
C is case sensitive, while Pascal is not. So in GPC all Pascal identifiers are converted to lowercase at an early stage, this includes the procedure names. So you have a problem when your C identifiers need uppercase letters. This can only be solved by using an attribute which mentions the name again with its capitalisation: "name 'Example'".
Even when they don't need any uppercase letters, it's recommended to always specify the linker name for external declarations.
I see that there are reasons for the compiler developers. But it is much more convenient for the programmer, if the name would simply be deduced from the Pascal name with its capitalisation, as it is in Free Pascal.
Well I still have a problem with CString parameters. FPC needs a type cast, while GPC doesn't accept a type cast here. I "solved" it again with "IfDef"s. Does anybody know a better solution?
Perhaps another macro (just a quick idea, as I actually don't like macros too much), that does a type-cast in FPC and nothing in GPC. (Perhaps it could even be a type equal to CString in FPC, though with another name, working as a type-cast by itself, and a nop-macro in GPC.)
Can you tell me more about "nop-macros" in GPC?
Another macro would even be dirtier than the ifdef. Fortunately there are just few (only one?) situation, where that's needed in the SDL - so that doesn't harm much in my program.
Andreas K. Foerster wrote:
to further save typing).
It's not for the typing, but the result is much more readable.
Of course. :-)
C is case sensitive, while Pascal is not. So in GPC all Pascal identifiers are converted to lowercase at an early stage, this includes the procedure names. So you have a problem when your C identifiers need uppercase letters. This can only be solved by using an attribute which mentions the name again with its capitalisation: "name 'Example'".
Even when they don't need any uppercase letters, it's recommended to always specify the linker name for external declarations.
I see that there are reasons for the compiler developers. But it is much more convenient for the programmer, if the name would simply be deduced from the Pascal name with its capitalisation, as it is in Free Pascal.
We've discussed this, but using the capitalization from the Pascal identifier would, unfortunately, be wrong because Pascal by definition is case-insensitive, i.e.
procedure Foo; external;
and
procedure foo; external;
are equivalent, i.e. must yield the same result. Of course, FPC doesn't care about standard Pascal, but GPC does (and also applies them, as much as reasonable, to non-standard extensions such as "external").
Well I still have a problem with CString parameters. FPC needs a type cast, while GPC doesn't accept a type cast here. I "solved" it again with "IfDef"s. Does anybody know a better solution?
Perhaps another macro (just a quick idea, as I actually don't like macros too much), that does a type-cast in FPC and nothing in GPC. (Perhaps it could even be a type equal to CString in FPC, though with another name, working as a type-cast by itself, and a nop-macro in GPC.)
Can you tell me more about "nop-macros" in GPC?
{$define foo(X) (X)}
Another macro would even be dirtier than the ifdef.
Somehow yes. But if ifdefs are otherwise required in several places, while a macro has to be defined only once, it might be worth it ...
Frank
Frank Heckenbach schrieb:
We've discussed this, but using the capitalization from the Pascal identifier would, unfortunately, be wrong because Pascal by definition is case-insensitive, i.e.
procedure Foo; external;
and
procedure foo; external;
are equivalent, i.e. must yield the same result. Of course, FPC doesn't care about standard Pascal, but GPC does (and also applies them, as much as reasonable, to non-standard extensions such as "external").
external is something to interface to external libraries which aren't necessarily written in pascal so we've to give up some pascal principles here when deriving the external name of the procedure :) BTW: Where is it written in any standard that you always lower case identifiers internally? FPC uppercases by default everything internally.
Florian Klaempfl wrote:
Frank Heckenbach schrieb:
We've discussed this, but using the capitalization from the Pascal identifier would, unfortunately, be wrong because Pascal by definition is case-insensitive, i.e.
procedure Foo; external;
and
procedure foo; external;
are equivalent, i.e. must yield the same result. Of course, FPC doesn't care about standard Pascal, but GPC does (and also applies them, as much as reasonable, to non-standard extensions such as "external").
external is something to interface to external libraries which aren't necessarily written in pascal so we've to give up some pascal principles here when deriving the external name of the procedure :) BTW: Where is it written in any standard that you always lower case identifiers internally? FPC uppercases by default everything internally.
This is not written in the standard. It just says that both forms are equivalent. We could just have a different convention (and in fact, we did until some time ago, when the first character was capitalized, and the rest lower-cased), as long as it's case-insensitive, as far as Pascal identifiers are concerned (as Andreas already pointed out, the explicit name specifications are syntactically string constants, and thus case-sensitive).
We now choose all-lowercase rather arbitrarily (all-uppercase would work just as well), perhaps just for some kind of consistency with default unit file names (i.e., in absence of an "in 'foo.pas'" specification), which is also derived from the unit name, lower-cased, for more practical reasons (on case-sensitive file systems, lower-case file names are much more common and easier to type; on others, it doesn't matter).
procedure Foo; external;
and
procedure foo; external;
are equivalent, i.e. must yield the same result. Of course, FPC doesn't care about standard Pascal
The above is also equivalent in FPC. Only if additionally "cdecl" (or C in MacPas) is added, then the capitalization of the identifier is kept for obvious reasons.
"Obvious" is, of course, only your opinion. I find it obvious, that in a case-insensitive language, programs that differ only in the case of an identifier must yield the same result.
(I may add that GPC has an optional warning for differing identifier case, but this doesn't change the statement. If the program compiles at all, i.e. one does not enable this warning and abort on warnings ("-Werror"), the resulting program is the same.)
Frank
On 01 Nov 2006, at 20:29, Frank Heckenbach wrote:
I find it obvious, that in a case-insensitive language, programs that differ only in the case of an identifier must yield the same result.
I (sorry for not adding an imho in my previous post) do not find that obvious if the programmers specifies that the identifier refers to an entity that should follow the conventions of a case-sensitive language and its compilers.
Jonas
Jonas Maebe wrote:
On 01 Nov 2006, at 20:29, Frank Heckenbach wrote:
I find it obvious, that in a case-insensitive language, programs that differ only in the case of an identifier must yield the same result.
I (sorry for not adding an imho in my previous post) do not find that obvious if the programmers specifies that the identifier refers to an entity that should follow the conventions of a case-sensitive language and its compilers.
The entity is external, but (IMHO) this shouldn't imply that the syntactic rules change to that of another language, same as we (both) also still use Pascal types, rather than C types ("int") and parameter syntax in external declarations. -- Of course, it would be possible otherwise, letting the programmer write C types, and the compiler substitute equivalent Pascal types; in fact, it would (at least at first sight) ease header translation, yet it doesn't seem to "fit" nicely in a Pascal program. (AFAIK, no Pascal compiler has seriously considered this option.) It would be more like an embedded foreign-language section (such as 'extern "C"' in C++, which is of course simplified by the fact that the syntax of C and a subset of C++ is very similar), but IMHO "external;" is not like this.
Frank
Florian Klaempfl wrote:
Frank Heckenbach schrieb:
syntactic rules change to that of another language,
It's not about syntax but abi.
Identifier case-insensitivity is part of the Pascal syntax.
Any ABI that assigns different meanings to the same identifier with different case is not consistent with this syntax. (Of course, FPC never claimed to follow Pascal syntax, we're all aware of this, but GPC strives to.)
Frank
Frank Heckenbach schrieb:
Florian Klaempfl wrote:
Frank Heckenbach schrieb:
syntactic rules change to that of another language,
It's not about syntax but abi.
Identifier case-insensitivity is part of the Pascal syntax.
Any ABI that assigns different meanings to the same identifier with different case is not consistent with this syntax.
I've the strange feeling that you didn't get how it works in FPC.
On 01 Nov 2006, at 20:46, Frank Heckenbach wrote:
It would be more like an embedded foreign-language section (such as 'extern "C"' in C++, which is of course simplified by the fact that the syntax of C and a subset of C++ is very similar), but IMHO "external;" is not like this.
You're misunderstanding what I'm trying to say. The following:
procedure t; external;
and
procedure T; external;
are also equivalent (as I literally said in my original post). Just like it is in GPC. And I also said that they only become different if you add cdecl (or C in MacPas) mode, i.e.
procedure t; cdecl; external;
keeps the name 't' and
procedure T; cdecl; external;
keeps the name 'T'. But, and here's where the misunderstanding comes in, you can't have both with the same parameter list in one compilation unit because when calling them from Pascal code, the Pascal casing rules are applied (i.e., whether you write t or T later on, both resolve to one and the same identifier). Therefore this program:
*** Program test;
procedure t; cdecl; external; procedure T; cdecl; external;
begin end. ***
generates the error:
tt.pp(4,11) Error: overloaded functions have the same parameter list
I.o.w, the cdecl keeping the identifier casing only applies to the name used for external linkage, and not to the name(s) usable in the Pascal code.
Jonas
Jonas Maebe wrote:
On 01 Nov 2006, at 20:46, Frank Heckenbach wrote:
It would be more like an embedded foreign-language section (such as 'extern "C"' in C++, which is of course simplified by the fact that the syntax of C and a subset of C++ is very similar), but IMHO "external;" is not like this.
You're misunderstanding what I'm trying to say. The following:
procedure t; external;
and
procedure T; external;
are also equivalent (as I literally said in my original post). Just like it is in GPC. And I also said that they only become different if you add cdecl (or C in MacPas) mode, i.e.
procedure t; cdecl; external;
keeps the name 't' and
procedure T; cdecl; external;
keeps the name 'T'.
Yes, OK, so it's with "cdecl", but it doesn't change the point.
But, and here's where the misunderstanding comes in, you can't have both with the same parameter list in one compilation unit because when calling them from Pascal code, the Pascal casing rules are applied (i.e., whether you write t or T later on, both resolve to one and the same identifier).
No misunderstanding, I didn't actually assume they could both be used in the same scope (more precise than compilation unit -- at least in GPC, you could have them in different scopes in the same compilation unit). Actually it didn't even occur to me that they could possibly (and I'm glad they can't in FPC).
But Pascal's case-insensitivity rule is stricter than just disallowing, e.g., "procedure foo;" and "procedure Foo;" in the same scope. It says that the two declarations are equivalent for all purposes.
Florian Klaempfl wrote:
Frank Heckenbach schrieb:
Florian Klaempfl wrote:
Frank Heckenbach schrieb:
syntactic rules change to that of another language,
It's not about syntax but abi.
Identifier case-insensitivity is part of the Pascal syntax.
Any ABI that assigns different meanings to the same identifier with different case is not consistent with this syntax.
I've the strange feeling that you didn't get how it works in FPC.
I think I do. As Jonas explained:
procedure t; cdecl; external;
gets the [linker-level] name 't' and
procedure T; cdecl; external;
gets the [linker-level] name 'T'. If there's more to it, feel free to explain.
However, I don't doubt that FPC works this way. I just pointed out that this doesn't conform to Pascal syntax rules (and that this doesn't matter for FPC), because whatever the compiler does internally, or which ABI it follows, according to Pascal rules, the identifier "t" is the same as "T", thus the two declarations are exactly the same, and thus any well-defined (in the mathematical sense) behaviour must be the same for both.
Frank
Frank Heckenbach schrieb:
However, I don't doubt that FPC works this way. I just pointed out that this doesn't conform to Pascal syntax rules (and that this doesn't matter for FPC),
It does, a standard isn't defined only by some bureaucrats but also by what people use and I guess 99 per cent of the pascal langauage family programmers use Delphi styled Object Pascal so this narrows it down.
because whatever the compiler does internally, or which ABI it follows, according to Pascal rules, the identifier "t" is the same as "T", thus the two declarations are exactly the same,
They don't exist anyways in the pascal sense because pascal doesn't support external to my knowledge.
and thus any well-defined (in the mathematical sense)
Only a closed system can follow mathematical rules and with the cdecl; external; and using C code in your program you open your pascal closed system.
behaviour must be the same for both.
Florian Klaempfl wrote:
Frank Heckenbach schrieb:
However, I don't doubt that FPC works this way. I just pointed out that this doesn't conform to Pascal syntax rules (and that this doesn't matter for FPC),
It does, a standard isn't defined only by some bureaucrats but also by what people use
Yawn, not that again. The language Pascal was invented by Wirth and standardized very close to his language (contrary to common myth, which you seem to allude to -- the few differences between Wirth's Pascal and ISO 7185 are to clarify ambiguities and such; ISO 10206 is a different question, but that's OT here, since the case-insensitivity rule is the same there).
Many people (including Wirth himself, later), have created other languages, more or less similar to Pascal, and this doesn't change what Pascal is.
and I guess 99 per cent of the pascal langauage family programmers use Delphi styled Object Pascal so this narrows it down.
I guess your guess is wrong. Now what?
And FWIW, even Borland doesn't claim, AFAIK, that Delphi is Pascal, but intentionally calls its language Delphi. (Looking at their main Delphi index page, as returned by Google, it doesn't even contain the word "Pascal".)
and thus any well-defined (in the mathematical sense)
Only a closed system can follow mathematical rules
Anything in computer software (or algorithmic digital information processing) can be described mathematically, of course.
Frank
On 01 Nov 2006, at 12:46, Frank Heckenbach wrote:
We've discussed this, but using the capitalization from the Pascal identifier would, unfortunately, be wrong because Pascal by definition is case-insensitive, i.e.
procedure Foo; external;
and
procedure foo; external;
are equivalent, i.e. must yield the same result. Of course, FPC doesn't care about standard Pascal
The above is also equivalent in FPC. Only if additionally "cdecl" (or C in MacPas) is added, then the capitalization of the identifier is kept for obvious reasons.
Jonas