Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: Otto - Febbraio 17, 2020, 02:06:14 pm

Titolo: Split Unit
Inserito da: Otto - Febbraio 17, 2020, 02:06:14 pm
Salve a tutti.

Avrei bisogno di sapere se esistesse un modo per separare una “Unit” in almeno due file diversi; in pratica avrei bisogno di replicare un metodo analogo a quello utilizzato in c# con le “partial class”.
Se non fosse possibile potrei cercare di implementare un piccolo merging tools, ma questa soluzione non sarebbe certo molto comoda.

Saluti,
Otto.
Titolo: Re:Split Unit
Inserito da: nomorelogic - Febbraio 17, 2020, 04:02:17 pm
non so se mi sono perso qualcosa ma con free pascal mi pare proprio che non si possa fare

quando mi si è presentata la necessità di fare qualcosa del genere ho usato la direttiva $I ($INCLUDE)
non so se è applicabile a quello che devi fare tu

in alternativa puoi splittare la classe i più classi che hanno compiti diversi per poi "iniettare" le sottoclassi in qualche property di quella principale
Titolo: Re:Split Unit
Inserito da: Stilgar - Febbraio 17, 2020, 04:16:08 pm
Ciao Otto.

Cosa sono le partial class?

Lo chiedo perché forse mi è venuta un'idea.


Stilgar
Titolo: Re:Split Unit
Inserito da: nomorelogic - Febbraio 17, 2020, 04:36:10 pm
dai un'occhiata qua

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods

assomigliano proprio a delle classi nidificate
Titolo: Re:Split Unit
Inserito da: Stilgar - Febbraio 17, 2020, 04:38:23 pm
Ni.
Forse assomigliano alle classi helper :)
Lo so è una soluzione "malvagia dentro", ma forse è da indagare in questa direzione ;)
Volevo solo una conferma.
Stilgar
Titolo: Re:Split Unit
Inserito da: Otto - Febbraio 17, 2020, 06:48:37 pm
non so se mi sono perso qualcosa ma con free pascal mi pare proprio che non si possa fare

quando mi si è presentata la necessità di fare qualcosa del genere ho usato la direttiva $I ($INCLUDE)
non so se è applicabile a quello che devi fare tu

in alternativa puoi splittare la classe i più classi che hanno compiti diversi per poi "iniettare" le sottoclassi in qualche property di quella principale

Grazie nomorelogic, ho controllato la guida https://wiki.lazarus.freepascal.org/$include e credo che la tua soluzione ($INCLUDE) possa andare bene.
Anche l’alternativa da te proposta sembra interessante, solo che avrei bisogno di un piccolo esempio per velocizzare l’implementazione.

Saluti,
Otto.
Titolo: Re:Split Unit
Inserito da: Otto - Febbraio 17, 2020, 07:00:53 pm
Ni.
Forse assomigliano alle classi helper :)
Lo so è una soluzione "malvagia dentro", ma forse è da indagare in questa direzione ;)
Volevo solo una conferma.
Stilgar

Grazie Stilgar, potresti avere ragione.
Sapresti indirizzarmi su qualche buon esempio di un corretto utilizzo delle helper class?

Saluti,
Otto.
Titolo: Re:Split Unit
Inserito da: Stilgar - Febbraio 17, 2020, 07:17:31 pm
https://github.com/paxtibi/lazarus-batis-utility/blob/master/src/lzbatis.dom.aspects.pas

Qui ho simulato il comportamento di jquery sul dom fp.

Stilgar
Titolo: Re:Split Unit
Inserito da: Otto - Febbraio 18, 2020, 08:30:09 am
Grazie Stilgar, il tuo progetto è molto interessante. Lo studierò attentamente: mi sarà certamente utile in futuro.

Saluti,
Otto.
Titolo: Re:Split Unit
Inserito da: Stilgar - Febbraio 18, 2020, 09:05:35 am
Ciao Otto.

Ne sto progettando uno più compatibile con mybatis.
Manca di le annotation/atttribute in freepascal devo riadattare la logica interna
Ma l'obiettivo è sempre generare delle classi pronte all'uso.
Devo produrre altri package di supporto  per la gestione dei null/nil.

Intanto buon studio

Stilgar
Titolo: Re:Split Unit
Inserito da: Otto - Febbraio 18, 2020, 11:55:00 am
Molto bene Stilgar, tienici informati sullo sviluppo del nuovo progetto.

Per quanto riguarda le “Class helpers” ho letto la discussione:
https://forum.lazarus.freepascal.org/index.php/topic,45253.0.html (https://forum.lazarus.freepascal.org/index.php/topic,45253.0.html)
che mi ha fatto propendere a pensare che le “Class helpers” del FPC siano sovrapponibili solo in alcuni aspetti con le “partial class” del c#.
Sembrerebbero utilissime qualora si volesse aggiungere del codice a delle classi alle quali non si avesse, o non si volesse avere, accesso diretto.
The purpose of helpers is to be able to add code to classes that you don't have direct access to.
Per le mie modeste conoscenze le “Class helpers” del FPC sembrerebbero più vicine alle “Extension Method” del C# (https://www.dotnetperls.com/extension) senza però essere equivalenti.
In questo articolo https://delphisorcery.blogspot.com/2013/04/why-no-extension-methods-in-delphi.html vengono messe in luce alcune differenze.

Saluti,
Otto.
Titolo: Re:Split Unit
Inserito da: Stilgar - Febbraio 18, 2020, 01:37:12 pm
:)
Mi sfugge la tua necessità di usare le classi parziali.
Non mi è chiaro il concetto evidentemente di classi parziali.
:)
Stilgar
Titolo: Re:Split Unit
Inserito da: Otto - Febbraio 18, 2020, 02:45:20 pm
La possibilità di utilizzare le “classi parziali” mi permetterebbe la semplificazione del processo di conversione di progetti già realizzati in Lazarus in modo tale che molti dei controlli vengano creati in fase di run time. Quando i progetti sono pochi e relativamente semplici non è certo un problema effettuare tale conversione, ma quando i progetti sono molti e con interfacce complesse il tempo necessario potrebbe essere considerevole.
Per velocizzare il processo ho utilizzato uno strumento in grado di convertire  i “Lazarus Form file” (*.lfm)  in codice sorgente pascal (*.pas)
 lazarus/examples/pascalstream/CopyAsPasPkg/copyformaspascaldemopkg.lpk.
Tale tools mi è stato suggerito da wp nella discussione: https://forum.lazarus.freepascal.org/index.php/topic,48160.0.html
- Il tools copyformaspascaldemopkg da buoni risultati purché i controlli non contengano immagini, altrimenti queste avrebbero una rappresentazione incompatibile con la sintassi Pascal. Credo che per risolvere il problema si possa adoperare un riferimento ai dati delle immagini, magari salvandoli all’interno di un file di risorse. Immagino che, in qualche modo, sia possibile adattare i dati immagine provenienti dal RAD in un formato compatibile ai file di risorse -.

Utilizzando le “classi parziali” potrei salvare i dati prodotti dal tools direttamente in un nuovo file (chiamandolo ad esempio Unit1_lfm.pas), in questo modo avrei il file della Unit del form (chiamata Unit1.pas) sostanzialmente invariato ed il nuovo file relazionato con tale Unit che andrebbe a sostituire il file “Lazarus Form file”. Questo processo potrebbe essere reso automatico.

Spero di non essere stato troppo confusionario.
Otto.
Titolo: Re:Split Unit
Inserito da: Stilgar - Febbraio 18, 2020, 02:52:16 pm
In soldoni stai cercando di pare un porting del codice.

Fare un parser che legga i file cs e li converta in pascalese?

La butto lì.



Stilgar
Titolo: Re:Split Unit
Inserito da: Stilgar - Febbraio 18, 2020, 03:01:21 pm
Leggendo il 3d in inglese ho capito che è lazarus su Lazarus che stai cercando di "convertire".
Corretto?
Stilgar
Titolo: Re:Split Unit
Inserito da: Stilgar - Febbraio 18, 2020, 03:10:45 pm
Ho controllato il codice che viene prodotto da quel plugin ... abbastanza orrido :)
Stilgar
Titolo: Re:Split Unit
Inserito da: Otto - Febbraio 18, 2020, 03:13:17 pm
Esatto, Stilgar.
So che potrebbe essere un’impressa notevole, per cui come primo passaggio vorrei automatizzare la conversione da “Lazarus Form file” (*.lfm)  in codice sorgente pascal (*.pas). Ciò mi permetterebbe di gestire la maggior parte dei controlli in run time e svincolarmi dall’utilizzo del RAD di Lazarus per i file convertiti.
Chiaramente l’utilizzo delle “classi parziali” sarebbe solo un modo per rendere il procedimento più semplice: si potrebbe tranquillamente farne a meno.

Otto.
Titolo: Re:Split Unit
Inserito da: Stilgar - Febbraio 18, 2020, 04:07:19 pm

Diciamo che è una base di partenza.
Puoi cercare di farne un programmino da linea di comando.
Poi estrai le immagini e le metti dentro un file di risorse Lazarus (da non confondere con quelle Windows).
Esiste un parser pascal dato in dotazione. Potresti leggere, modificare il sorgente con le cose che ti servono, scrivere il file così prodotto.
Stilgar
Titolo: Re:Split Unit
Inserito da: Otto - Febbraio 18, 2020, 07:22:07 pm
Grazie Stilgar.
Per il parser pascal ti riferisci a fcl-passrc (https://wiki.freepascal.org/fcl-passrc) che in Windows si trova in: “\Lazarus\fpc\3.0.4\source\packages\fcl-passrc”?

Otto.
Titolo: Re:Split Unit
Inserito da: Stilgar - Febbraio 18, 2020, 08:12:24 pm
Si, mi sembra quello :)
Ho preso alcune cosine del pacchetto e l'ho adattato alle mie esigenza in un progettino :)

Stilgar
Titolo: Re:Split Unit
Inserito da: Otto - Febbraio 19, 2020, 11:11:47 am
Ciao Stilgar, sto controllando il sorgente del fcl-passrc e sicuramente mi terra molto impegnato visto che ha poca documentazione. Per caso tu hai trovato qualche guida durante la fase di adattamento per il tuo progetto?

Otto.
Titolo: Re:Split Unit
Inserito da: Stilgar - Febbraio 19, 2020, 11:14:08 am
No.Debugger a manetta e controllo del codice scritto.
I problemi li ho avuti nella scrittura. Sembra che sia "incompleto".  Ma avevo esigenze specifice su come doveva essere scritto il codice.
:)
Stilgar
Titolo: Re:Split Unit
Inserito da: Otto - Febbraio 19, 2020, 11:19:36 am
Ok! Grazie.

Otto.
Titolo: Re:Split Unit
Inserito da: nomorelogic - Marzo 18, 2020, 03:49:12 pm
in alternativa puoi splittare la classe i più classi che hanno compiti diversi per poi "iniettare" le sottoclassi in qualche property di quella principale
avrei bisogno di un piccolo esempio per velocizzare l’implementazione.

ciao Otto
scusa per il ritardo ma non sempre ho tempo questi giorni.

In allegato un progetto con esempio di split in classi con compiti diversi.
In fase di creazione (dell'oggetto principale) viene creato tramite parametro l'oggetto-split che serve e viene "iniettato" nell'istanza principale.

Chiaramente questa non è una alternativa alla direttiva include né alle partial class ma è un modello di sviluppo OOP.
Visto che la tua necessità era di semplificare una unit con classe complessa + usare ciò che free pascal ha di standard, ho pensato che questo tipo di approccio potesse essere una soluzione.

nomorelogic


Edit:
Nell'esempio, le 2 classi split sono usate come alternativa l'una all'altra; nulla vieta che si possa usare lo stesso approccio anche con classi-split che non prevedono alternative ma compiti unici e specifici.
L'idea è quella di suddividere la classe principale in classi con diversi compiti ed istanziare quando serve.


Edit 2:
Nel caso che non servano classi-split alternative, le classi-split si possono mettere semplicemente in uses nella parte implementation della classe principale.
Così il codice è ancora più semplice.
Titolo: Re:Split Unit
Inserito da: Otto - Marzo 18, 2020, 09:03:18 pm
@nomorelogic

Grazie nomorelogic.

Il tuo è un ottimo esempio. In particolare analizza un aspetto importantissimo nell’utilizzo delle Unit: il punto corretto dove introdurre la parola chiave uses per richiamare le varie classi. Un utilizzo errato porterebbe ad anomalie ed errori, specialmente nel caso di conflitti di nomi.
Se sei d’accordo potremmo inserire il tuo esempio nella sezione dedicata all’interno della wiki, ovviamente citando il tuo nome o (nickname).

Sto studiando un metodo per implementare le PartialUnits senza che sia necessario effettuare delle modifiche al compilatore FPC. Ho aperto un thread  (https://forum.lazarus.freepascal.org/index.php/topic,48882.msg352816.html#msg352816)nel forum internazionale. Volevo verificare anche l’interesse su questo argomento e perciò mi sono tenuto un poco sul vago inizialmente. C’è stata qualche incomprensione, forse non ho tradotto correttamente in inglese, ma ora sembra ce ci sia un certo interesse.
Se volessi intervenire anche tu nel forum internazionale mi farebbe piacere. Ovviamente l’invito è esteso a chiunque.

Otto.
Titolo: Re:Split Unit
Inserito da: nomorelogic - Marzo 19, 2020, 01:38:33 am
per mettere degli esempi nella wiki non posso che essere d'accordo
forse però è meglio prevedere un esempio più semplice
(la citazione non è il caso ;) )

ho letto un po' del thread sul forum internazionale, ho visto che hai acceso una bella discussione :)
sono uscite molte idee, anche grazie all'incomprensione forse
me lo rileggerò con calma :)
Titolo: Re:Split Unit
Inserito da: nomorelogic - Marzo 19, 2020, 01:06:24 pm
visto che oramai c'ero ho semplificato l'esempio
non si tratta di PartialClass, ma di scrivere OOP in modo da splittare per design una classe complessa
ci sono 4 file:


programma principale
notare che, per chi deve far uso della classe da splittare, basta mettere in uses uwork_main e non le classi splittate
Codice: [Seleziona]
program test_split;

uses uwork_main;

begin

  with TMainObject.Create do
     try
       WriteLn(LeggiMessaggioIT);
       WriteLn(LeggiMessaggioEN);
     finally
       Free;
     end;

end.

segue la definizione della classe principale
notare che

Codice: [Seleziona]
unit uwork_main;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, unit_work_split_1, unit_work_split_2;


type


  { TMainObject }

  TMainObject = class
  private
    FWorkerIT: TMainObject_WorkerIT;
    FWorkerEN: TMainObject_WorkerEN;
  public
    constructor Create;
    destructor Destroy; override;

    function LeggiMessaggioIT: string;
    function LeggiMessaggioEN: string;
  end;


implementation

{ TMainObject }

constructor TMainObject.Create;
begin
   FWorkerIT:=nil;
   FWorkerEN:=nil;
end;

destructor TMainObject.Destroy;
begin
   FreeAndNil(FWorkerIT);
   FreeAndNil(FWorkerEN);

   inherited Destroy;
end;

function TMainObject.LeggiMessaggioIT: string;
begin
  if not Assigned(FWorkerIT) then
     FWorkerIT:=TMainObject_WorkerIT.Create;

  result := FWorkerIT.Read;
end;

function TMainObject.LeggiMessaggioEN: string;
begin
  if not Assigned(FWorkerEN) then
     FWorkerEN:=TMainObject_WorkerEN.Create;

  result := FWorkerEN.Read;
end;

end.

operazione  complessa splittata 1
Codice: [Seleziona]
unit unit_work_split_1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;


type

  { TMainObject_WorkerIT }

  TMainObject_WorkerIT = class
  public
    function Read: string;
  end;


implementation

{ TMainObject_WorkerIT }

function TMainObject_WorkerIT.Read: string;
begin
  result:='tutto andrà bene!';
end;

end.


operazone complessa splittata 2
Codice: [Seleziona]
unit unit_work_split_2;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

type

  { TMainObject_WorkerEN }

  TMainObject_WorkerEN = class
  public
    function Read: string;
  end;


implementation

{ TMainObject_WorkerEN }

function TMainObject_WorkerEN.Read: string;
begin
  result:='everything will be fine!';
end;

end.
Titolo: Re:Split Unit
Inserito da: Otto - Marzo 19, 2020, 11:03:26 pm
Ciao nomorelogic.

Ora il tuo è diventato un esempio da manuale corredato da commenti molto dettagliati: complimenti!

In C# uso spesso classi che hanno una costruzione molto simile a quella da te descritta; ma in C# l’uso dell’OOP non è una scelta di stile.
Grazie al tuo esempio credo di avere compreso quale fosse stata la vera causa dell’incomprensione iniziale durante la discussione nel Forum internazionale. Davo per scontato, visto che lo avevo premesso, che ci riferissimo tutti ad un’implementazione di tipo OOP.
Questo mi fa apprezzare ancora di più il tuo esempio.

Otto.

(-: Tutto andrà bene! :-)
Titolo: Re:Split Unit
Inserito da: Stilgar - Marzo 19, 2020, 11:52:04 pm
Pure qui andrà bene tutto?
Occhio che nei film quando viene pronunciata quella frase almeno metà dei personaggi ci lacia le penne :)

Stilgar

Titolo: Re:Split Unit
Inserito da: nomorelogic - Marzo 20, 2020, 12:48:09 am
GANDALF: La gente di Rohan avrà bisogno di te. Le difese devono reggere.
ARAGORN: Reggeranno.


non sempre ;)