Newsgroup: it.comp.lang.c++
Titolo: Come si inizializza una mappa static?

]

 il problema, quindi e' ben gradita ^__^)
Se per qualche ragione non puoi, o non vuoi,
usare il costruttore di cui sopra (e ce ne possono
essere tante di ragioni -- i ctor degli oggetti
statici, in C++, danno spesso rogne), la cosa
piu` semplice potrebbe essere usare un semplice
flag statico booleano che ti dice se la mappa sia
gia` stata inizializzata; se no, il tuo codice
puo` provvedere ad inizializzarla.
Per intenderci, ad esempio...:
class mappa_mia: public
    std::map<std::string, std::string> {
        bool inited;
        typedef std::map<std::string, std::string> base;
        void init();
public:
        mappa_mia(): inited(false) {}
        std::string& operator[](const std::string& key)
        {
            if(!inited) init();
            return base::operator[](key);
        }
};
static mappa_mia lamappa;
se l'unico metodo che usi della mappa e` l'operator[]
(se no dovrai esporne anche altri); naturalmente
nella mappa_mia::init() porrai tutto cio` che tu
desideri (fra cui porre inited=true!-).  C'e` un piccolo
overhead (quel test ad ogni []), ma in molti casi
puo` essere accettabile (ci sono certe piccole
ottimizzazioni che puoi fare, come usare un idioma
letter-envelope con la letter che cambia al volo per
l'inizializzazione, ma l'indirettezza risultante puo`
costarti quasi quanto quel test -- se l'uso e` del
tutto critico alla tua applicazione, puoi provare nei
due modi e *misurare* le differenze...).
Alex

puo` provvedere ad inizializzarla.
Era la mia prima opzione, ma non mi piaceva assolutamente (una
questione di eleganza e pulizia). Per quanto riguarda la portabilita'
io faccio questo ragionamento: e' conforme allo standard ed egcs lo
compila senza rogne? Allora va bene. Se lo compila anche il tuo
compilatore ok, altrimenti esiste sempre egcs che e' free e portato
praticamente ovunque.


 In questo caso un costruttore static sarebbe perfetto.
"Un costruttore static" mi sembra un poco un non-senso (un ctor che
non e` legato a un particolare oggetto...?!).  Comunque, per fare
quello che desideri, puoi usare il classico idioma:
typedef std::map<std::string,std::string> tipodellamappa;
static tipodellamappa lamappa;
struct ilmioinizializzatorepreferitochepiubellinoncene {
    ilmioinizializzatorepreferitochepiubellinoncene(tipodellamappa& m);
}
static ilmioinizializzatorepreferitochepiubellinoncene 
    __paraponzolo(lamappa);
Adesso, nel costruttore di default eseguito per la variabile
static chiamata __paraponzolo, cioe`:
ilmioinizializzatorepreferitochepiubellinoncene::
    ilmioinizializzatorepreferitochepiubellinoncene(
        tipodellamappa& m
    )
{
    // inserire operazioni a piacere (su m, e/o altro)
}
potrai scrivere qualsivoglia operazione di inizializzazione da te
desiderata (relativa, in particolare, all'oggetto di "tipodellamappa"
che viene passato come "m" al costruttore di default stesso),
comprese tutte le dipendenze che desideri dall'ambiente di
esecuzione (lettura di file, connessioni a database, ecc ecc).
Alex

     __paraponzolo(lamappa);
E' garantito che lamappa sia inizializzata prima di __paraponzolo?
Non converrebbe forse usare un singleton? ad esempio:
typedef std::map<std::string,std::string> MapType;
class MapManager {
  public:
    static MapType * getTheMap();
  private:
    static MapType * theMap;
};
MapType * MapManager::theMap = 0;
MapType * MapManager::getTheMap() {
  if ( theMap == 0 ) {
    theMap = new MapType;
    // inizializzo la mappa
  }
  return theMap;
}
Attenzione: scritto di getto e non compilato!
Ciao,
Nicola


[snip]
Si', ma mi pare un po' una cosa da UCAS :) Cosi' sei costretto a
definire un tipo solo con lo scopo di inizializzare altri dati (con
ben poca leggibilita' per un estraneo che esamina il codice), quando
il tutto potrebbe essere racchiuso in un costruttore di default static


   if ( theMap == 0 ) {
Qui, l'uso della pattern singleton paga questo "pesante" (:-)
overhead: un test ad ogni "get", e una indirettezza ad ogni
accesso.  Tra l'altro, il resto del codice, o si fa una sua
piccola cache del puntatore a mappa, oppure paga ancora
di piu` (un get per ogni accesso).  Se si fa la cache (in
un oggetto statico), paga un ulteriore test per vedere
se quel puntatore statico e` ancora zero (shudder).
Tutto questo si evita con l'idioma piu` classico da me
proposto, il quale incorre _zero_ overhead.  Quali
vantaggi presenterebbe "singleton", a parte il suo
accattivamente nomignolo...?-)
Alex

 Alex
Ciao,
Nicola

 (MS VC++6, comunque, questa la digerisce bene:-).
Anche il DJGPP la compila ed esegue perfettamente.
EMG

 il tutto potrebbe essere racchiuso in un costruttore di default static
Per carita` -- un nuovo concetto totalmente ad hoc (con
una profonda revisione dell'object model), per fare quel
che si puo` fare benissimo gia` oggi, senza nessun costo
addizionale, usando il piu` normale e comune di tutti gli
idiomi?!
Dio ci scampi e liberi da iatture del genere.  C++ ha gia`
dentro sin troppa roba (appunto e` un linguaggio tanto
ricco e complicato!), eppure non manca mai chi e` pronto
a suggerire *ulteriori* estensioni ad hoc per "evitare" (?!)
l'uso di feature che esistono gia` e vanno benissimo...!
La gente e` proprio strana.
Alex

 dipende molto dal contesto in cui la soluzione si colloca.
Amerei vedere un caso in cui, usando la
    "MapType * MapManager::getTheMap()"
si ottiene "migliore evidenza".  In che senso, ad esempio...:
    mapManager.getTheMap()->operator[]("chiave")...
e` migliore di
    lamappa["chiave"]...
???
Oppure, si puo` accedere "in due tempi", tipo:
    MapType& lamappa(*mapManager.getTheMap());
    lamappa["chiave"]...
ma anche qui, l'aggiunta della prima riga, piuttosto
complicata, mi pare che possa offuscare, non rendere
piu` limpide, gli accessi.
Alex

 piu` limpide, gli accessi.
Premetto che cio' che sto per scrivere e' estremamente soggettivo ed
atto a scatenare guerre di religione; io intendo "evidenza in termini di
disegno" ad un livello un po' piu' elevato, in particolare:
- Trovo che in generale cio' che compare in un header file e' piu'
evidente
  di cio' che e' codificato in un file di implementazione;
- Preferisco utilizzare membri statici privati piuttosto che variabili
  locali ad una specifica unita' di compilazione o ad una funzione;
- Preferisco ottenere l'information hiding separando le dichiarazioni in
  file di include diversi, e decidendo dove includerli, che non
  "nascondendo" dichiarazioni in file di implementazione;
Questo mi permette di scrivere codice che io (ovviamente...) trovo
chiaro senza incorrere in overhead (di compilazione o di esecuzione)
inaccettabili, anche se mi rendo conto che possono esistere per
particolari applicazioni vincoli di scala e/o prestazioni che obbligano
a ricorrere ad approcci diversi dal mio.
Detto questo ritengo che l'evidenza che un elemento di un programma deve
avere dipende dall'importanza dell'elemento stesso: se la mappa di cui
all'esempio originale fosse una specie di blackboard attraverso la quale
tutti i sottosistemi di un'enorme applicazione accedono per scambiarsi
informazione, penso che sprecherei un'insieme di classi (e di file
sorgente) per definirne nel modo piu' esplicito possibile interfaccia ed
implementazione.
Se invece fosse un meccanismo interno di un oscura funzione membro
privata di una qualche classe, probabilmente userei delle classi
innestate per definirne la struttura e la dichiarerei variabile locale
statica della funzione membro stessa.
Post-messa: trovo impossibile esaurire questo tipo di argomento nello
spazio (e tempo) ragionevole per un messaggio di news.
Ciao,
Nicola

 spazio (e tempo) ragionevole per un messaggio di news.
Nulla impedisce di scambiarne molteplici.  Sempre piu`
interessante del 17.467-esimo post che chiede come si
legge il mouse, o la tastiera, o come si colora di blu un
bottone, o, pur essendo quest'ultimo subject on-topic,
quale libro uno dovrebbe comprare per imparare...!-)
Alex

 quale libro uno dovrebbe comprare per imparare...!-)
Come vedi ho risposto all'invito, anche perche' considero questo uno
degli argomenti piu' interessanti di cui si possa parlare in questo
newsgroup; tra l'altro mouse e tastiera non so leggerli, l'ultima volta
che ho programmato cosi' a basso livello il mouse non esisteva. In piu'
dovetti farlo in assembler, quindi ancora piu' fuori argomento...
Pubblicizzare i libri che mi piacciono pero' mi diverte, spero sempre
che gli altri frequentatori del gruppo si mettano a spendere le stesse
caterve di soldi che spendo io: chissa' che alla fine i prezzi non
scendano!!
Ciao,
Nicola

 newsgroup;
Sono perfettamente d'accordo con voi. Mi dispiace di non essere spesso
in grado di partecipare a questi discorsi ma li seguo sempre con il
massimo interesse. La programmazione vera, in opposizione allo
smanettamento, non può prescindere da conoscenze teoriche e profonde
riflessioni di questo tipo.
EMG

 effimera qualcosa che invece "sopravvive". Tra l'altro come accennato
??? una funzione non e` effimera -- non puoi crearla al volo,
e neppure si estingue.  Ha "durata" sin maggiore di quella
delle sue variabili locali static.
Alex

     }
Mi spiegheresti meglio a cosa serve? Mi pare di capire che implementa
una conversione implicita. Basta questo a rendere un cast più sicuro?
EMG

 una conversione implicita. Basta questo a rendere un cast più sicuro?
La conversione e`, appunto, implicita, e quindi non vi e` in essa
nessun rischio: si tratta delle stesse conversioni che il compilatore
farebbe implicitamente _se_ il contesto gli dicesse che servono.
E`, se vogliamo, appunto un modo esplicito e sicuro di fornire un
contesto in cui "il tipo che serve" e` A.
Ad esempio, invece di:
    inline double frazione(int a, int b)
    {
        double temp = a;
        return temp/b;
    }
posso scrivere:
    inline double frazione(int a, int b)
    {
        return safe_cast<double>(a)/b;
    }
invece di usare un altro cast, "vero", come static_cast<>, che
notoriamente e` meglio usare il meno possibile (perche` piu`
potente e quindi piu` prono all'errore).
Usare safe_cast invece di static_cast rispetta il principio della
"minima 'forza' possibile".
Rispetto all'alternativa di definire una variabile temporanea,
puo` facilitare la vita all'ottimizzatore (rende piu` chiaro che
la variabile temporanea non serve davvero, e` solo un modo
di richiedere una conversione implicita), al lettore (idem), e
inoltre, essendo sintatticamente un'espressione, si presta
all'uso in casi in cui la variabile temporanea non sarebbe
comoda da usare, tipo...:
class Base { ... };
class Deriv: public Base { ... };
int testa(Base* b);
int testa(Deriv* b);
Deriv* next();
    ...
    Deriv* pd;
    while(testa(safe_cast<Base*>(pd=next()))) {
        if(testa(pd)) std::cout<<"Perfetta: "<<*pd<<"\n";
        else std::cout<<"Passabile: "<<*pd<<"\n";
    }
Se e` comoda in questi casi di overload, come alternativa
a cast piu` forti o uso di variabili temporanee per forzare
la conversione, lo e` anche nell'uso di template:
template<typename T>
int plup(T a, T b);
    ...
    int krek(int a, double b) {
        return plup(safe_cast<double>(a),b)+
                  plup(a,safe_cast<int>(b));
    }
Qui i due parametri della plup devono essere di tipo
identico, per come e` scritto il suo template, e quindi,
per chiamarla, in un modo o nell'altro l'identita` dei
tipi dev'essere garantita.  Ancora una volta safe_cast
permette di farlo senza variabili temporanee, e senza
scomodare un 'vero' cast, che e` meglio evitare se
possibile.
Alex

 delle sue variabili locali static.
In quanto codice ovviamente no, in quanto espressione di uno stato,
ovvero l'insieme di dati che concorrono a permetterne la computazione,
dura per ogni chiamata il tempo che intercorre tra l'attivazione ed il
return, cosi' come un blocco di codice dura dall'istruzione che segue
l'aperta graffa a quella che precede la chiusa graffa.
Un'altro problema della soluzione basata su variabili statiche locali
(non a funzioni) e' che si corre il rischio di dipendere dall'ordine di
inizializzazione di variabili dichiarate in unita' di traduzione
diverse.
Ciao,
Nicola

idiomi?!
Quale sarebbe la profonda revisione?


 Quale sarebbe la profonda revisione?
Applicare a non-oggetti (ad esempio, una classe)
alcuni dei metodi la cui semantica e` accuratamente
definita (e spesso complessa e sottile) esclusivamente
per _oggetti_.  Se si volesse adottare una simile
modifica, occorrerebbe definire con altrettanta
accuratezza, complessita`, e sottigliezza, la nuova
semantica prevista.
Alex

Mi pare che le modifiche proposte da Sciasbat siano certamente
"rivoluzionarie" rispetto a quello che e' il C++ oggi, ma non sono ne
particolarmente nuove, ne davvero inutili, visto che sono usate con
profitto in altri linguaggi.
Una e' tipica dei linguaggi "modulari", che prevedono generalmente
segmenti di inizializzazione e terminazione esplicita e la gestione
automatica delle dipendenze tra moduli.
Il C++ eredita dal C la completa indipendenza tra moduli (ovvero unita'
di traduzione) che a volte e' un vantaggio e a volte no. Certamente
molti idiomi sono stati sviluppati per superare l'assenza di questi
meccanismi.
L'altra e' l'introduzione di veri e propri oggetti classe, che potrebbe
servire a supportare meccanismi di costruzione tramite prototyping e
meccanismi di riflessione tipo Java, ovvero la rappresentazione
esplicita della struttura della classe e dei suoi elementi.
Questi meccanismi sono alla base degli ambienti RAD basati su componenti
che conosco, e che guarda caso (vedi C++Builder) si fondano su
estensioni al linguaggio.
Personalmente trovo che di entrambe i meccanismi si possa sentire la
mancanza, anche se mi rendo perfettamente conto che la loro introduzione
altererebbe profondamente alcuni assunti fondamentali del linguaggio.
Sono altrettanto convinto che il giusto compromesso tra potenza
espressiva e complessita' e' per un linguaggio di programmazione una
caratteristica preziosa e condivido la cautela con cui il C++, che per
altro e' gia' un linguaggio complesso, e' stato esteso nel corso degli
anni.
Ciao,
Nicola