Newsgroup: it.comp.lang.vo-clipper
Titolo: Il Clipper ancora mi sorprende

Sapevate che in Clipper puro (provato su 5.2e e 5.3b) si poteva fare una
cosa del genere?
FUNCTION MAIN()
    ? "Prova":ClassName()
    ? 123:ClassName()
    ? DATE():ClassName()
    ? .T.:ClassName()
    INKEY( 0 )
    RETURN NIL
Io proprio non lo immaginavo neanche. Questo dimostra che il Clipper è
veramente un linguaggio moderno, completamente object oriented. Perfino
i tipi base sono oggetti. Ho sentito che probabilmente xHarbour verrà
presto esteso in modo tale che sarà addirittura possibile aggiungere
proprietà e metodi ai tipi base.
Ciao a tutti.
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg

 ha scritto nel
messaggio news:41021BB9...@emagsoftware.it...


 ha scritto nel
messaggio news:41021BB9...@emagsoftware.it...


 i tipi base sono oggetti.
E' vero, e' sorprendente, ma se ci pensi non poi cosi' tanto,
dato che le variabili sono untyped e gestite a runtime, non era
poi cosi' difficile inserire questa funzionalita' e riverberarla ad
ogni tipo di variabile. Ben piu' difficile se Clipper avesse tipi
elementari come C, C++ o Pascal.
Hai provato:
   ? nil:ClassName()
Tutto sommato Clipper e' un linguaggio semplice, non ha certo le
sofisticazioni semantiche di C++ o anche solo di Delphi,
ma e' per questo che lo amiamo, e' semplice e geniale.
Ciao...
Claudio


Beh,  noi ci avevamo provato a "sfruttarlo bene" nel lontano 1991 !!
(e con buoni risultati, direi)
http://www.soft-com.it/pub/clipper/ooplib.zip
   (887 Kb)
Saluti
Paolo
paolo....@soft-com.it
www.soft-com.it

 linguaggi tipo il Pascal e  molti altri.....
In primo luogo il C++.


 ma e' per questo che lo amiamo, e' semplice e geniale.
Vero.


aobrowse[1,n,4],Len(aobrowse[1,n,1]),aobrowse[1,n,4])
            SetFieldName(oCol,aoBrowse[1,n,2]) //l'istanza cargo contiene
nome campi
            SetFieldIndice(oCol,aoBrowse[1,n,12])
            //AGGIUNGO COLONNA ALL'OGGETTO oBrowse
              oBrowse:addColumn(oCol)
          EndIf
        Next
  // AGGIUNGO OGGETTO ALLO STACK
    ogBrowse(0,/*pos*/,{obrowse,Wogbr,aoBrowse,FileName,numIndice})
  SetCursor(sCursore)
RETURN(NumOgBr())
STATIC FUNCTION OgBrowse(tipo,pos,aNewBr) // tipo = 0 ins
                                         // tipo = 1 del
                                        // pos numOggetto da elimina
* VARIABILI
  Static aObrFdati  :={}
  Local ritorno
  If tipo = 0                // inserimento
     aadd(aObrFdati,aNewBr)
     ritorno :=Len(aobrFdati)
  ElseIf tipo = 1 // cancella
    IF pos <> 0
      Wselect(aobrFdati[pos,2])
      Wclose()
      ChiudiFile(aObrFdati[pos,4])
      Adel(aObrFdati,pos)
      Asize(aobrfdati,Len(aobrfdati)-1)
      messaggio()
    EndIf
  ElseIf tipo = 2                   // get oggetto
    ritorno :=  aObrFdati[pos]     //
  ElseIf tipo = 3                  // numero oggetti
    ritorno :=  Len(aobrFdati)
  EndIf
Return(ritorno)
****************************************************************************
****
FUNCTION NumOgBr()        // numero oggetti presenti
  Local numero:= OgBrowse(3)
RETURN(numero)
*****************************************************
FUNCTION CloseOgBrowse(pos)
   OgBrowse(1,pos)
RETURN(NIL)
****************************************************
FUNCTION GetOgBrowse(pos)
 local oggetto:= OgBrowse(2,pos)
RETURN(oggetto)
******************************************************
STATIC FUNCTION TBPrimo(oTbr)
    Eval(oTbr:goPrimo)
    // Occorre per. controllare che il record soddisfi anche
    // la condizione FOR, senza uscire dall'intervallo definito
    // dalla condizione WHILE.
      Do While !Eof() .and. Eval(oTbr:while) .and. !Eval(oTbr:for)
          SKIP
      EndDo
    // Se il record non S valido, va alla fine del file.
      If Eof() .OR. !Eval(oTbr:while)
          GOTO 0
      EndIF
RETURN(nil)
****************************************************************
STATIC FUNCTION TBUltimo(oTbr)
  // Se la modalit. aggiunta S attivata, va alla fine del file
     If oTbr:aggiunta
        GOTO 0
     Else
       // Si posiziona sul primo record che soddisfa la condizione WHILE
          Eval(oTbr:goUltimo)
       // Ora si assicura che il record soddisfi entrambe le
       // condizioni WHILE e FOR.
          Do While !Bof() .and. Eval(oTbr:while) .and. !Eval(oTbr:for)
            SKIP -1
          EndDo
       // Se il record non S valido, va alla fine del file.
          If Bof() .or. !Eval(oTbr:while)
            GOTO 0
          endIf
     EndIf
RETURN(nil)
**********************************************************************
STATIC FUNCTION DbSkipBlock(n, oTbr)
  LOCAL nSaltati := 0 ,;
        col
  If n = 0
      SKIP 0
  ElseIf n > 0
      Do While nSaltati != n .and. TBSucc(oTbr)
          nSaltati++
      EndDo
  Else
      Do While nSaltati != n .and. TBPrec(oTbr)
          nSaltati--
      EndDo
  EndIf
RETURN(nSaltati)
****************************************************************************
STATIC FUNCTION TBSucc(oTbr)
// Prima salva il puntatore dei record, in modo che sia possibile
// ripristinarlo se lo spostamento non riesce.
  LOCAL nNumRec := recno()
  LOCAL lMosso := .T.
  If Eof()
      // Non pu. andare oltre
      lMosso := .F.
  Else
      SKIP
      // Continua ad andare avanti finch, non raggiunge
      // un record valido, non esce dall'intervallo o non
      // raggiunge la fine del file.
        Do While !Eval(oTbr:for) .and. Eval(oTbr:while) .and. !Eof()
          SKIP
        EndDo
      If Eof() .and. oTbr:aggiunta
      // Tutto bene ...
      ElseIf !Eval(oTbr:while) .or. Eof()
          If !Eval(oTbr:while) .and. oTbr:aggiunta
              GOTO 0
          Else
              lMosso := .F.
              GOTO nNumRec
          EndIf
      EndIf
  EndIf
RETURN(lMosso)
***************************************************************************
STATIC FUNCTION TBPrec(oTbr)
  LOCAL nNumRec := Recno()
  LOCAL lMosso := .T.
  // Occorre considerare con attenzione Eof()
  // SKIP -1 non S sufficiente a causa dei filtri.
    If Eof()
        Eval(oTbr:goUltimo)
    Else
        SKIP -1
    EndIf
    Do While !Eval(oTbr:for) .and. Eval(oTbr:while) .and. !Bof()
       SKIP -1
    EndDo
    If !Eval(oTbr:while) .or. Bof()
        GOTO nNumRec
        lMosso := .F.
    EndIf
RETURN(lMosso)
****************************************************************************
**
FUNCTION GestBrowse(header,sWin)
* NOTA BENE: QUESTA PROCEDURA PUO' ESSERE RICORSIVA RICORDANDO PERO' CHE
TUTTE
*            VARIABILI CHE CONTENGONO UN ARRAY SONO PASSATE PER REFERENZA
PERCIO'
*            AL RIENTRO DI UNA RICORSIONE QUESTE RIMANGONO CON IL VECCHIO
VALORE.
*            PECCATO!!!!!!!!!!
* devo quindi occuparmi anche di uno stack di oggetti
  local a                        ,;
        filtro  := ''            ,;
        Meth                     ,;
        tasto                    ,;
        ancora      := .t.       ,;
        prossimo    := 1         ,;
        cursore     := SetCursor()
// QUESTI METODI IMPOSTANO DELLE VARIABILI ISTANZA CHE SERVIRANNO NEL
MOMENTO
// IN CUI SI INVOCA :REFRESHCURRENT o REFRESHALL CHE ESEGUONO L'AZIONE
VOLUTA
// CONSIDERANDO LE VARIABILI ISTANZA MODIFICATE.407C5.2
  Local AoTbMethods := {{ K_DOWN       , {|b| b:Down()     }},;
                        { K_UP         , {|b| b:Up()       }},;
                        { K_PGDN       , {|b| b:PageDown() }},;
                        { K_PGUP       , {|b| b:PageUp()   }},;
                        { K_RIGHT      , {|b| b:Right()    }},;
                        { K_LEFT       , {|b| b:left()     }},;
                        { K_HOME       , {|b| b:home()     }},;
                        { K_END        , {|b| b:End()      }},;
                        { K_CTRL_PGDN  , {|b| b:GoBottom() }},;
                        { K_CTRL_PGUP  , {|b| b:GoTop()    }},;
                        { K_CTRL_LEFT  , {|b| b:panLeft()  }},;
                        { K_CTRL_RIGHT , {|b| b:panRight() }},;
                        { K_CTRL_HOME  , {|b| b:panHome()  }},;
                        { K_CTRL_END   , {|b| b:panEnd()   }}}
   Private   oBrowse
    SET CURSOR OFF
    aoBrFdati := GetOgBrowse(header)
*    FileName := aoBrFdati[4]     // nome del file
    Wselect(aoBrFdati[2])
    ApriFile(aoBrFdati[4],aoBrFdati[5]) // file,indice
    aoBrowse = aOBrFdati[3]
    oBrowse = aOBrFdati[1]
    messaggio("",aoBrowse[3,3])
  ****************** VISUALIZZAZIONE OGGETTO
    If aoBrowse[5] <> NIL
     For a := 1 to Len(aoBrowse[5])
        eval(aoBrowse[5,a])
     Next
    EndIf
    obrowse:GoTop()
    obrowse:RefreshAll()
    Do While(ancora)               //  visualizzazione dell'oggetto
      ****** STABILIZZA INCREMENTALMENTE TBROWSE() ogni volta che si preme
un tasto
                    // l'incremento viene interrotto  aggiornato subito
dopo.
      Do While nextkey() = 0 .and. ! oBrowse:Stabilize()
      EndDo
      If len(aobrowse) > 6
        Aeval(aobrowse[7],aobrowse[7,1])
      EndIF
      ****** EVVERTI SE IN CIMA O IN FONDO
      If oBrowse:hitTop .or. oBrowse:hitBottom // in cima o in fondo avverti
        Tone(125,0)
      EndIf
      ****** FINE AVVERTI SE IN CIMA O IN FONDO
      tasto := inkey(0)                          // cerca funzione legata
      meth :=aScan(aoBrowse[6],{|elem| tasto = elem[1]})
      If meth != 0
        ancora := eval(aoBrowse[6,meth,2],oBrowse,aobrowse,header,@ancora)
        IIF(ancora = NIL,ancora := .t.,)         // per vecchie funzioni
        IF  ancora
          obrowse:RefreshAll()
        EndIf
      Else  // tasti default
        meth := aScan(aoTbMethods,{|elem| tasto = elem[1]})// al tasto
premuto
        If meth != 0                          // se non esiste allora
controlla i pros
          eval(aoTbMethods[meth,2],oBrowse)  // simi tasti
        Else
          ancora :=
MyTasti(@aoBrFdati,tasto,@oBrowse,@aobrowse,ancora,@header,sWin)
        EndIf
      EndIf
    EndDo
   SetCursor(cursore)
 RETURN(NIL)
 FUNCTION myTasti(aoBrFdati,tasto,oBrowse,aobrowse,ancora,header,sWin)
   Local pos,oCol
   Do Case
     Case tasto == K_TAB
       LenaObr    := NumOgBr()        // numero oggetti presenti
       If LenaObr >1
         If LenAobr <> Header
           header++
         Else
           header := 1
         EndIf
         aoBrFdati := GetOgBrowse(header)
         Wselect(aoBrFdati[2])
         ApriFile(aoBrFdati[4])
         aoBrowse = aOBrFdati[3]
         oBrowse = aOBrFdati[1]
         oBrowse:RefreshAll()
         obrowse:GoTop()
         ****************** VISUALIZZAZIONE OGGETTO
         If aoBrowse[5] <> NIL
           For a := 1 to Len(aoBrowse[5])
              eval(aoBrowse[5,a])
           Next
         EndIf
       EndIf
     Case tasto == K_ESC           // esci
       CloseOgBrowse(header)
       If header <> 1
         header    := NumOgBr()        // numero oggetti presenti
         aoBrFdati := GetOgBrowse(header)
         If sWin <> NIL
           Wselect(swin)
         Else
           Wselect(aoBrFdati[2])
         EndIf
         ApriFile(aoBrFdati[4])
         aoBrowse = aOBrFdati[3]
         oBrowse = aOBrFdati[1]
       EndIf
       ancora := .f.
       *********************
     Case tasto == K_ENTER
       If aobrowse[4,1]
         DoGet(oBrowse)              // get read diretto su browse
       EndIf
       oBrowse:RefreshAll()
       ***********************
     Case tasto == K_F4
       If aobrowse[4,2]
         Modelli(0,aobrowse)
         oBrowse:RefreshAll()
       EndIf
       **********************
     Case tasto == K_ALT_B
         obrowse:freeze = obrowse:colpos
         obrowse:RefreshAll()
     Case tasto == 411   // alt + <-
       If obrowse:getcolumn(obrowse:colPos):width > 1
         pos := obrowse:colPos
         ocol := obrowse:getcolumn(obrowse:colPos)
         obrowse:Delcolumn(obrowse:colPos)
         ocol:width --
         obrowse:InsColumn(pos,ocol)
         obrowse:RefreshAll()
       EndIf
     Case tasto == 413   // alt + ->
       pos := obrowse:colPos
       ocol := obrowse:getcolumn(obrowse:colPos)
       obrowse:Delcolumn(obrowse:colPos)
       ocol:width ++
       obrowse:InsColumn(pos,ocol)
       obrowse:RefreshAll()
     Case tasto == K_DEL
         iif(obrowse:colPos()<> 0,obrowse:DelColumn(obrowse:colPos()),)
         obrowse:RefreshAll()
     Case tasto == K_ALT_S
      * StaBrowse(obrowse)
       obrowse:RefreshAll()
     Case tasto == K_F5
        If aobrowse[4,3]
          If Eof() .and. Bof()
            DbAppend()
          EndIf
          Modelli(1,aobrowse)
          oBrowse:RefreshAll()    // meglio ALL che CURRENT se c'e' indice
        EndIf
     Case tasto == K_F6
       If aobrowse[4,4]
         Modelli(2,aobrowse)
         oBrowse:RefreshAll()
       EndIf
     Case tasto == K_F8
      oCol :=OBrowse:getColumn(oBrowse:colPos)
      cField := GetFieldName(oCol)
      set filter to &cField = &cField
      oBrowse:RefreshAll()
     Case tasto == K_F9
       If aobrowse[4,5]
         Messaggio("ATTENZIONE!!!!! TUTTI I DATI PRESENTI VERRANNO
ELIMINATI:","F10EliminaEscUscita ",.t.)
         ZapArchivio()
         Messaggio()
         oBrowse:RefreshAll()
       EndIf
     Otherwise                    // nulla
  EndCase
Return(ancora)
**********************************
Procedure DoGet(oBrowse)      // solo record con un campo
  Local GetlPrec := Getlist
  Local oCol , cField
  Local getList := {}
  Local tastof10 := SetKey(K_F10,nil)
  Set Format to
  If eof() .and. bof()
    DbAppend()
  EndIf
    oCol :=OBrowse:getColumn(oBrowse:colPos)
    cField := GetFieldName(oCol)
    ************************************ valido solo per ore operai
     If obrowse:colpos <> 1
       SetCursor(2)
       If obrowse:colpos = 2
         @ row(),Col() GET &cField Picture aobrowse[1,9,5] Valid
ValOreRep45()
       Else
         @ row(),Col() GET &cField Picture aobrowse[1,9,5]
       EndIf
       * @ row(),Col() GET &cField Picture aobrowse[1,obrowse:colPos,5]
      Read
      oBrowse:RefreshCurrent()
      SetCursor(0)
    EndIf
   getlist := GetlPrec
   Set format to SayGet
   SetKey(K_F10,tastof10)      // sempre valido
RETURN(NIL)
************************************************************************
FUNCTION ordinatoPer(su,nPos)
 * SU INDICA NUMERO RIGHE SOPRA MAXROW()
 * POS = colonna attuale
 messaggio := "ORDINE PER: " + Alltrim(obrowse[6,nPos,6])
 ClearWin(MaxRow()-su,0,MaxRow()-su,MaxCol(),"n")
 SetColor("gb+/n")
 @ MaxRow()-su,0 Say messaggio
RETURN(NIL)
************************************************************************
FUNCTION cambiaIndice(oBrowse,su,nMaxInd)
* SU      = RIGHE SU DAL FONDO
* nMaxInd = numero max di indici visibili all'utente
  If GetFieldIndice(obrowse:GetColumn(oBrowse:ColPos)) <> 0 //0 = nessun
indice
    DbSetOrder(GetFieldIndice(obrowse:GetColumn(oBrowse:ColPos)))
    ordinatoPer(su,oBrowse:ColPos)
  EndIF
    oBrowse:RefreshAll()
RETURN(NIL)
**************************************************************************

 chiami Clipper 5.2.................
Avrei preferito due parole di spiegazione invece che una cartata di
codice. Comunque, nel tuo codice non trovo alcuna traccia dell'uso del
metodo ClassName(). Mi sa che hai capito male il mio intervento. Io mi
riferivo alla possibilità di trattare come oggetti i tipi primitivi del
linguaggio non a quella ben nota (anche se non documentata) di creare
nuove classi.


 dati di Clipper, che sono tanti.
Direi un metodo,
la proprietà è un attributo particolare delle classi che è stato
creato per interfacciare i programmi OOP con i linguaggi visuali,
in pratica è un valore che può essere scritto o letto con metodi
della classe, dal punto di vista dell'utente (il programmatore)
è come se fosse una variabile istanza. Se non fosse per i
linguaggi visuali se ne potrebbe tranquillamente fare a meno.
Ciao...
Claudio


Mi sa che hai un po' di confusione in testa su quanto riguarda la OOP. I
termini "variabile d'istanza", "proprietà", "dato membro" sono tutti
sinonimi e i linguaggi visuali non c'entrano per niente.


 sinonimi e i linguaggi visuali non c'entrano per niente.
Mi permetto di dissentire, quello che dici forse puo' essere vero
per alcuni linguaggi, ma non e' vero in Delphi ed in C# e forse
neanche in VB+ActiveX
In Delphi le variabili istanza possono essere di tutti i tipi
conosciuti da Delphi e possono avere 4 livelli di visibilita':
Privare, Protected, Public e Published. Le properties invece sono
un dato virtuale, infatti si dichiara il tipo e le procedure che
servono per leggerle e scriverle, questo serve perche' variando
il valore della property si puo' agire su piu' dati, ad esempio,
quando cambi il valore della property height di un controllo, lo
stesso viene ridimensionato nella form con la possibilita' di fare
n cose. Anche il livello di visibilita' published e' stato
introdotto solo per distinguere le properties che possono essere
visibili a livello di componente.
In C# esiste un meccanismo assolutamente simile, non per niente
il padre di C# e' anche l'autore di Delphi.
Di ActiveX non sono certo, pero' ritengo che il meccanismo sia lo
stesso, l'utente vede le properties come se fossero variabili
istanza ma in realta' chissa' cosa c'e' dietro.
Ciao...
Claudio

 n cose.
Come ho detto sopra, fai un po' di confusione tra sintassi/semantica
implementative di un particolare linguaggio o strumento e concetti di
base della OOP. In OOP una classe ha un dato membro (o, in Clipper,
variabile d'istanza o, in Visual Basic, proprietà) e questo può essere
acceduto direttamente se dichiarato a visibilità pubblica (ovviamente
sconsigliato) oppure attraverso funzioni membro cosiddette "accessors"
che effettuano controlli sul range dei valori accettabili e/o causano
effetti collaterali come quelli da te citati.


 effetti collaterali come quelli da te citati.
Ok, non conosci ne Delphi ne C# quindi non sai di che cosa sto parlando,
Questi due linguaggi fanno distinzione tra variabili istanza e 
proprieta'. Tutto qui. Non mi puoi dire che faccio confusione, in Delphi
sono cose distinte punto.
Ciao...
Claudio


Scusa se insisto ma la OOP è un paradigma astratto. Non si può parlare
di concetti OOP portando come unico esempio la particolare
implementazione fatta da uno specifico linguaggio. Inoltre, se ci pensi
bene, la distinzione fatta da Delphi e C# è puramente sintattica non
concettuale. Però alla fine pensala come ti pare ma uno sguardo a
qualche testo serio di OOP fossi in te lo darei.


 qualche testo serio di OOP fossi in te lo darei.
Ne ho letti piu' di quanti ne immagini ed adotto la programmazione
OOP gia' da diversi anni.
Ciao...
Claudio


Detto così sembra che VO sia un mostro di difficoltà oppure una schifezza.
Invece, a parte l'aspetto economico che chiaramente non essendo freeware o
open source incide sulla scelta, è un ambiente di sviluppo molto potente,
veloce, affidabile, aperto verso ogni tipo di database, ecc. ecc.  ...
l'erede a pieno titolo del Clipper.
Non vuole essere una polemica la mia. Non voglio togliere niente nè a
Clipper, nè a Harbour e nè a FW. Voglio solo che se c'è qualcuno che deve
fare la scelta non lo escluda a priori.
Buon lavoro a tutti
Biagio

 OOP gia' da diversi anni.
Scusami, non ti volevo offendere. Solo che, francamente, non riesco a
capire il senso preciso delle tue obiezioni. Forse con questo messaggio
ci troviamo un pochino più in linea?


 fare la scelta non lo escluda a priori.
Guarda che qui nessuno vuole rubarvi sviluppatori VO. :-)


 ci troviamo un pochino più in linea?
Ok, alle volte e' inutile spaccare il capello in quattro.
Ciao...
Claudio Driussi.

 fanno.
Ok, ma queste strutture sintattiche non sono contemplate dalla OOP. Se
uno oggi si sveglia e si inventa un metodo che, se chiamato, esegue una
particolare ed esotica operazione su uno o più oggetti di una o più
classi e lo chiama Exotic, puoi dire che il concetto di Exotic sia
diverso da quello di metodo? Ecco, per questo, secondo me, i termini
dato membro, variabile d'istanza e proprietà significano concettualmente
la stessa cosa.
Detto questo, che c'entra tutto il discorso con l'eventuale uso del
metodo ClassName() applicato ai tipi primitivi del Clipper? Ti ricordo
che tu dicesti che lo avevi utilizzato spesso ma forse ci siamo
fraintesi.
