Lazarus

Free Pascal => General => Topic started by: soerensen3 on November 11, 2018, 05:53:26 pm

Title: redeclare procedure from another unit
Post by: soerensen3 on November 11, 2018, 05:53:26 pm
Hi!

I want a header unit that redeclares types and functions of a lot of other units so that you only have to add the header to the uses section instead of a bunch of smaller files. For types this is simple:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2. interface
  3.  
  4. uses Unit2;
  5.  
  6. type
  7.   TTestClass = Unit2.TTestClass;
  8.  
  9. ...
  10.  

Is there some equivalent for procedures and functions? I only know of the external keyword but I think this is for compiled libraries (*.DLL, *.SO) only.

It might be the same question like this but the discussion confuses me a bit so I'm not sure:
https://stackoverflow.com/questions/25893908/how-to-reference-an-external-function-from-inside-a-unit
Title: Re: redeclare procedure from another unit
Post by: Thaddy on November 11, 2018, 06:07:04 pm
Hi!

I want a header unit that redeclares types and functions of a lot of other units so that you only have to add the header to the uses section instead of a bunch of smaller files. For types this is simple:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2. interface
  3.  
  4. uses Unit2;
  5.  
  6. type
  7.   TTestClass = Unit2.TTestClass;
  8.  
  9. ...
  10.  

Is there some equivalent for procedures and functions? I only know of the external keyword but I think this is for compiled libraries (*.DLL, *.SO) only.

It might be the same question like this but the discussion confuses me a bit so I'm not sure:
https://stackoverflow.com/questions/25893908/how-to-reference-an-external-function-from-inside-a-unit
As long as your unit order is correct this will simply work. No external needed. Forget about that.
Title: Re: redeclare procedure from another unit
Post by: creaothceann on November 11, 2018, 07:40:07 pm
Unit A declares T:

Code: [Select]
unit A;

interface

type T = byte;

implementation

end.


Unit B declares P:

Code: [Select]
unit B;

interface

procedure P;

implementation

procedure P;
begin
WriteLn('P');
end;

end.


Unit C includes A and B:

Code: [Select]
unit C;

interface
uses A;

// T is already available here

implementation
uses B;

// T and P are available here

end.
Title: Re: redeclare procedure from another unit
Post by: jamie on November 11, 2018, 07:55:42 pm


Ok, I suppose in the type section

TFORM = Forms.Tform;

Or
TOldObject = Objects.Tobject;

I suppose one could have that in a INC file
{$I FileName}

Title: Re: redeclare procedure from another unit
Post by: soerensen3 on November 11, 2018, 09:58:22 pm
I guess I have to explain it a bit more, I thought when I say header everyone understands what I mean :P

Ok for example in my game engine I have many classes which I don't want to have in one file like TMesh, TScene or TLight and a lot more.

I have a namespace graphics and several subunits:

Code: Pascal  [Select][+][-]
  1. unit graphics.mesh;
  2. interface
  3. type
  4.   TMesh = class
  5. ....
  6.  

Code: Pascal  [Select][+][-]
  1. unit graphics.light;
  2. interface
  3. type
  4.   TLight = class
  5. ....
  6.  

and in graphics.pas I do this:

Code: Pascal  [Select][+][-]
  1. unit graphics;
  2. interface
  3. uses
  4.   graphics.light,
  5.   graphics.mesh;
  6. type
  7.   TLight = graphics.light.TLight;
  8.   TMesh = graphics.light.TMesh;
  9. ....
  10.  

And if I want to use the game engine in a project I would simply do this:

Code: Pascal  [Select][+][-]
  1. unit SomeGameUnit;
  2. interface
  3. uses
  4.   audio,
  5.   graphics,
  6.   input;
  7. ....
  8. implementation
  9. var
  10.   Light: TLight;
  11. ...
  12.  

This should work perfectly unless I want to define a function/procedure in one unit (not a method).

Code: Pascal  [Select][+][-]
  1. unit graphics.core;
  2. interface
  3.   procedure ClearScreen( ClearColor: TColor4 );
  4. ...
  5.  
If I include this unit in graphics I can use the ClearScreen function there. But if I want to use this procedure in "SomeGameUnit.pas", the only way I can think of is putting my function into includes instead of separate units. At the moment I'm using includes for all my subsystems because I don't want to have one large file. This however can be a annoying in the IDE because for example whenever I use code completion for a new type it would append it to the wrong includes and so on and units look cleaner somehow.
Title: Re: redeclare procedure from another unit
Post by: Martin_fr on November 11, 2018, 10:02:44 pm
You can write a inline wrapper / not perfect, but compiled with optimization, it *should* be no extra code in the exe.
Code: Pascal  [Select][+][-]
  1. interface
  2.   uses UnitFoo;
  3.   procedure Foo; inline;
  4. implementation
  5.   procedure Foo;
  6.   begin
  7.     UnitFoo.Foo;
  8.   end;
  9.  
Title: Re: redeclare procedure from another unit
Post by: ASerge on November 11, 2018, 10:28:40 pm
You can write a inline wrapper / not perfect, but compiled with optimization, it *should* be no extra code in the exe.
Or use procedure type variable:
Code: Pascal  [Select][+][-]
  1. unit graphics;
  2.  
  3. interface
  4.  
  5. var ClearScreen: procedure(ClearColor: TColor4) = @graphics.core.ClearScreen;
Title: Re: redeclare procedure from another unit
Post by: creaothceann on November 11, 2018, 10:31:24 pm
If you want to call a procedure/function in such a unit you could just add it to your uses list. Or put all of these procedures/functions into a record/class as static class procedures/functions.

@Martin_fr:
Does it matter where the inline is added? I always put it after the actual procedure/function signature...

@ASerge:
That would add a pointer indirection.
Title: Re: redeclare procedure from another unit
Post by: lucamar on November 11, 2018, 10:47:13 pm
Ok for example in my game engine I have many classes which I don't want to have in one file like TMesh, TScene or TLight and a lot more.
[. . .]
and in graphics.pas I do this:
[... some code ]
And if I want to use the game engine in a project I would simply do this:
[... some code ...]

This should work perfectly unless I want to define a function/procedure in one unit (not a method).
[... some code ...]

If I include this unit in graphics I can use the ClearScreen function there. But if I want to use this procedure in "SomeGameUnit.pas", the only way I can think of is putting my function into includes instead of separate units.

What you pretend is to have all the (suppossed) advantages of one big unit without paying the (very real) cost of having one big unit: it can be done, more or less, for classes but for separate procedures/functions you have basically the solutions proposed by ASerge and Martin_fr ... but both will make you end up with a big (although smaller) unit again.

Instead of that use the power of units to the hilt. There's no need for you to use include files nor, indeed, a mega-include-all-classes graphics.pas; you can simply add your small "one-class" units to the uses clause of SomeGameUnit.pas. It also allows the IDE features to work better, since unit is, let's say, their basic building block.
Title: Re: redeclare procedure from another unit
Post by: Martin_fr on November 11, 2018, 11:13:12 pm
There is one more way.

In the unit where it is implemented (afaik,only implementation is needed):
Code: Pascal  [Select][+][-]
  1. function Foo: Boolean; alias : 'AFoo';
  2. begin
  3. end;
  4.  

In the big unit
Code: Pascal  [Select][+][-]
  1. function Foo: Boolean;   external name 'AFoo';
  2.  

The compiler will not match them, so you get no error, if they have different declarations. If they do not match, you get a crash when you run your app.

It is the linker, that will connect them together.
Title: Re: redeclare procedure from another unit
Post by: soerensen3 on November 11, 2018, 11:37:07 pm
Thanks for all the answers!

There is one more way.

In the unit where it is implemented (afaik,only implementation is needed):
Code: Pascal  [Select][+][-]
  1. function Foo: Boolean; alias : 'AFoo';
  2. begin
  3. end;
  4.  

In the big unit
Code: Pascal  [Select][+][-]
  1. function Foo: Boolean;   external name 'AFoo';
  2.  

The compiler will not match them, so you get no error, if they have different declarations. If they do not match, you get a crash when you run your app.

It is the linker, that will connect them together.
Just for the implications: Does it mean I can only have one function of that name in the package, in the scope or for the whole executable?

Ok for example in my game engine I have many classes which I don't want to have in one file like TMesh, TScene or TLight and a lot more.
[. . .]
and in graphics.pas I do this:
[... some code ]
And if I want to use the game engine in a project I would simply do this:
[... some code ...]

This should work perfectly unless I want to define a function/procedure in one unit (not a method).
[... some code ...]

If I include this unit in graphics I can use the ClearScreen function there. But if I want to use this procedure in "SomeGameUnit.pas", the only way I can think of is putting my function into includes instead of separate units.

What you pretend is to have all the (suppossed) advantages of one big unit without paying the (very real) cost of having one big unit: it can be done, more or less, for classes but for separate procedures/functions you have basically the solutions proposed by ASerge and Martin_fr ... but both will make you end up with a big (although smaller) unit again.

Instead of that use the power of units to the hilt. There's no need for you to use include files nor, indeed, a mega-include-all-classes graphics.pas; you can simply add your small "one-class" units to the uses clause of SomeGameUnit.pas. It also allows the IDE features to work better, since unit is, let's say, their basic building block.
Maybe you're right but I also tend to have very small files with only a few classes and functions. I don't want to burden any user having to remember which file is in which small unit. But I suppose maybe I find a solution in between because I don't want too many "hacks" in the code.
Title: Re: redeclare procedure from another unit
Post by: Martin_fr on November 12, 2018, 12:13:44 am
Just for the implications: Does it mean I can only have one function of that name in the package, in the scope or for the whole executable?
It is the alias that must be unique. And yes the alias must be unique across the entire app. I am not sure what the maximum length for the alias is (there probably is a restriction).

If they are not unique the linker will probably fail (You can test that).

----
The point is, in your big unit, you do not even need to add the other unit to the "uses".
But you need to include it somewhere in your app, so the exports will be there (or you compile the small units separate, and make sure the linker finds the .o files)

For fpc, it is external, it does not care where it comes from. It will be found by the linker. (same as if it was linked in from some 3 party object files)
TinyPortal © 2005-2018