Newsgroup: it.comp.lang.c++
Titolo: optional members

Supponete di avere una struttura dati alla quale si possa aggiungere, se 
necessario, un'ulteriore variabile membro.
Per ora non mi viene in mente niente di piů semplice/comodo di quanto 
segue. Se avete qualche consiglio...
----
struct _ {};		// forse rischioso...
#define Member(w1)					\
	template <class MEMBER>			\
	struct Member_##w1				\
	{								\
		MEMBER w1;					\
	};								\
	template <>						\
	class Member_##w1<_> {};
//////////////
Member(pub);
Member(priv);
template <class PUB_DATA = _, class PRI_DATA = _>
struct A : public Member_pub<PUB_DATA>,
		   private Member_priv<PRI_DATA>
{
	int a;
};
//////////////
int main()
{
	A<_,int> a;
	a.a = 1;
	a.pub = 7;			// "inexistent"
	a.priv = 9;			// unaccessible
}
----
Kiuhnm


Che cosa c'e' che non va nelle seguenti dichiarazioni?
struct B1: public A { public: int pub; } b1;
struct B2: public A { private: int priv; } b2;
Il compilatore Visual C++ 7.1 (2003) accetta anche le seguenti 
dichiarazioni, ma non sono sicuro che siano standard:
   struct : public A { public: int pub; } b1;
   struct : public A { private: int priv; } b2;
-- 
     Carlo Milanesi
 
http://digilander.libero.it/carlmila

 struct B2: public A { private: int priv; } b2;
Mi pare che tu abbia dichiarato, definito e instanziato ;)
Comunque, vedrai che non sarai in grado di riprodurre il mio esempio con 
quel metodo. Per es. non c'č modo di sostituire 'int' (pensa al 
template) con qualcosa che faccia sparire 'pub'. Avevo pensato a 'static 
const int', ma ovviamente non si puň.
Voglio inoltre evitare di specializzare direttamente B1.
Kiuhnm

 
anche la seguente cosa non l'ho mai vista e chiedo conferma : si 
tratta di un template con argomenti di default ?
tutto il resto devo ancora digerirlo :-)
Ciao
Il tuo personale soviet_tormento
Soviet


 di un template con argomenti di default ?
Sě. A<> equivale a A<_,_> e A<int> a A<int,_>.
Le STL usano questa tecnica ovunque.
Kiuhnm

 Voglio inoltre evitare di specializzare direttamente B1.
Che problemi ti crea?


 Che cosa non ti va bene?
Quello che fai è creare una struttura manualmente, mentre la mia 
situazione è un po' diversa.
Ho alcune strutture che possono contenere o no membri opzionali a 
seconda di alcuni typedef contenuti in altre strutture.
E' per questo che uso un template.
Ogniqualvolta voglio una struttura A con due membri aggiuntivi 
opzionali, non devo fare altro che scrivere:
Member(pub);
Member(priv);
template <class PUB_DATA = _, class PRI_DATA = _>
struct A : public Member_pub<PUB_DATA>,
            private Member_priv<PRI_DATA>
{
     int a;
};
L'"utente" utilizza dei typedef in modo da guidare la costruzione di 
alcune strutture.
L'utente non ha la minima idea di quante saranno e di come saranno fatte 
queste strutture. L'unica cosa che sa è che vuole ricevere, a un certo 
punto, una struttura contenente i suoi due "membri opzionali" che possa 
usare a proprio piacimento.
In questo modo evito i classici "void* userdata" che si usavano in C per 
permettere all'utente di associare informazioni utili ad alcune strutture.
Non credo che esista un modo più comodo per fare quello che ho fatto, ma 
mi rendo conto che senza vedere *tutto* il codice è impossibile dire 
qualcosa. Non credevo che il post fosse ambiguo (ma lo è).
Kiuhnm

[...]
Kiuhnm ha scritto:
 > In questo modo evito i classici "void* userdata" che si usavano in C per
 > permettere all'utente di associare informazioni utili ad alcune 
strutture.
 >
 > Non credo che esista un modo più comodo per fare quello che ho fatto, ma
 > mi rendo conto che senza vedere *tutto* il codice è impossibile dire
 > qualcosa. Non credevo che il post fosse ambiguo (ma lo è).
 >
 > Kiuhnm
Lo troverete orrendo ma io il void *userdata lo trovo + comprensibile 
per chi legge il codice ...

 per chi legge il codice ...
Preferisco rendere il codice un po' più difficile piuttosto che:
a) sprecare 4 bytes (per ogni struttura),
b) aggiungere un'indirezione (minore efficienza),
c) eliminare i check naturali del compilatore sul tipo.
Praticamente torneresti a programmare in C.
Kiuhnnm

 necessario, un'ulteriore variabile membro.
class A {
private:
friend class B<T>; //non mi ricordo la sintassi corretta di class
template che siano "amici" :-(
A(const A&) {}
const A& operator =(const A&){}
};
template <typename T> class B: public A { T t };
Immagino che nel tuo caso reale non fosse praticabile una soluzione che
ricalca l'idea di questo esempio, magari neanche usando una sintassi
compilabile.

 compilabile.
Non capisco perché usi "friend".
Kiuhnm

 Non capisco perché usi "friend".
La versione idiota era
class A {};
template <typename T> class B: public A { T t; }
ma immaginavo che tu volessi un A che non fosse istanziabile
direttamente. Così volevo mettere il costruttore di copia, di default
(che mancava nell'esempio) e l'operatore di assegnamento privati in A,
e poi con la clausola friend volevo permettere a B di accedere comunque
a tali funzioni/costruttori .

 a tali funzioni/costruttori .
Ok, comunque leggi la mia ultima riposta a Carlo Milanesi.
Kiuhnm

 Perché non si può.
Ma si che si puo':
class B {
  public:
    B() : c(42) {}
  private:
    template <typename T> friend class A;
    int c;
};
template <typename T> class A {
  public:
    int f(B & b) { return b.c; }
};
int main() {
  A<int> ai;
  A<double> ad;
  B b;
  return ai.f(b) + ad.f(b);
}
Ciao,
Nicola

     template <typename T> friend class A;
Non lo sapevo, comunque la scrittura non ha molto senso :)
Potrei capire
"friend template <typename T> class A;"
o
"template <typename T> friend class A<T>;"
Mi ricordo di un vecchio post dove si è risposto che era impossibile. 
Perché non sei intervenuto?
Forse la situazione era diversa? Non ricordo...
Kiuhnm

 Ok, comunque leggi la mia ultima riposta a Carlo Milanesi.
L'ho letta, ma abbiamo approcci troppo diversi al codice perché
potessi darti un contributo utile. Tu parli di un "utente" che ad un
certo punto vuole due membri opzionali A e B, di cui non sa nulla, di
cui non vuole sapere nulla, ma che vuole usare.
Per me già questo è una cosa che non capisco. L'utente quanto sa? Se
li vuole usare allora qualcosa sul "come" usarli deve saperla. Leggendo
in questo modo arbitrario le tue parole mi sembra l'utente ha bisogno
di interfacce con opzionalmente dietro degli oggetti neri che fanno
"del" lavoro, non tanto di oggetti opzionali. A questo punto mi viene
da chiedere se la genericità del template e realmente utile, o se è
solo eleganza.
Non conoscendo i termini del problema però la probabilità che dica
scemenze è molto alta. Io tendo ad usare usare i template poco, ed
esclusivamente nella parte "non visibile" delle mie libreria. Ma questo
è un approccio personalistico dovuto al mio percorso programmatorio, e
ai miei casi reali di utilizzo delle librerie realizzate da altre
persone. Per esempio trovo demenziale usare i template quando i
possibili argomenti del template sono giusto tre in croce, e
rigorosamente non documentati (parlo di una libreria di un "valente"
collaboratore esterno della mia azienda)
Ciao, e considera la lettura dei miei post qualcosa da macchina del
caffè.

 cui non vuole sapere nulla, ma che vuole usare.
I membri opzionali sono suoi quindi sa esattamente come sono fatti.
L'utente, durante una certa operazione, verrà contattato e riceverà 
alcuni oggetti con i quali farà delle operazioni. Queste strutture 
conterranno anche i membri opzionali richiesti (che avranno anche il 
/nome/ richiesto). In qusto modo l'utente potrà utilizzarli come meglio 
crede. L'utente potrebbe ricevere varie volte gli stessi oggetti così 
può usare i membri aggiuntivi come cache ed evitare di ripetere calcoli 
uguali o simili. Comunque non posso sapere cosa vorrà farci l'utente. 
Quelle strutture sono sue.
L'utente è un altro sviluppatore di librerie (in realtà sono sempre io 
:) ). Divido il problema in vari layer in modo che possa aggiungere 
funzionalità o modificare la struttura rapidamente.
Il risultato finale sarà uno dei possibili risultati e potrà essere 
modificato in futuro senza fatica.
Kiuhnm

 "template <typename T> friend class A<T>;"
Per es. gradirei si potesse scrivere qualcosa del genere:
class B {
public:
	B() : c(42) {}
private:
	template <typename T> friend class A<T,T>;
	int c;
};
template <typename T, typename T2> class A {};
template <typename T> class A<T,T>
{
public:
	int f(B & b) { return b.c; }
};
Kiuhnm


Non ti va bene il seguente codice?
   class B {
   public:
     B() : c(42) {}
   private:
     template <typename T, typename T2> friend class A;
     int c;
   };
   template <typename T, typename T2> class A {};
   template <typename T> class A<T,T>
   {
   public:
     int f(B & b) { return b.c; }
   };
   int main()
   {
     B b;
     A<char, char> a1;
     if (a1.f(b) != 42) return 1;
     A<double, double> a2;
     if (a2.f(b) != 42) return 1;
     A<double, char> a3;
   //    if (a3.f(b) != 42) return 1; // Illegale
     return 0;


 modificato in futuro senza fatica.
Ti sei dimenticato i miei post, altrimenti non avresti scritto parole
simili rischiando un flame. Credo che la promessa del riuso del codice,
a distanza di tempo, usando qualsiasi linguaggio sia una promessa da
marinaio, perché, almeno io, a distanza di tempo non mi ricordo nulla.
Io riutilizzo con facilità solo il codice scritto come black box, o il
codice magari complesso, ma di cui conosco a menadito l'architettura.
In questo caso mi ricordo solo dei progetti che mi hanno appassionato,
che non sono la maggioranza di quelli che scrivo. 
Davide

 Non ti va bene il seguente codice?
No, il mio desiderio era quello di rendere friend soltanto A<T,T>.
Kiuhnm

 No, il mio desiderio era quello di rendere friend soltanto A<T,T>.
Con "soltanto" intendi uno solo tipo T?
Ciò deciso cosa vale T, da quel momento deve valere solo quello?
Volendo c'era poi l'opzione di far valere T un std::pair< T1, T2 > e
via complicando.

 che non sono la maggioranza di quelli che scrivo. 
Nella mia vita seguirò un /solo/ progetto ed è quello al quale sto 
lavorando.
Kiuhnm

 Con "soltanto" intendi uno solo tipo T?
Intendo "soltanto" la specializzazione A<T,T> (famiglia di classi).
Mi sfugge il motivo per il quale si sia scelta una sintassi cosě poco 
espressiva. Sembra piů una patch messa lě all'ultimo momento.
Kiuhnm

 Con "soltanto" intendi uno solo tipo T?
Intende: usare una specializzazione *parziale* in una dichiarazione friend.
Non è consentito, in effetti. 
Max

Perché rimandare a run-time quello che può essere fatto a compile-time?
Per esempio per avere oggetti dinamici per non dire mutevoli.
Cose tipo l'istanza di A si "aggrega" il parametro B e poi ad un certo
punto lo cambia in C.
Dubito fortemente che sia il tuo caso.

 Max
Non avevo capito. Per quello non si può usare uno di quei trucchi
"alla template" rigorosamente illeggibili? Qualcosa tipo una
dichiarazione che è compilabile solo se si è in presenza di una
specializzazione parziale.
Boh, qualcosa del genere:
template <typename T1, typename T2>
class B
{
template <typename T1, typename T2> friend class A;
void papero ()
{
  T1 * a = 0;
  const T2& b = *a;
}
};

 Dubito fortemente che sia il tuo caso.
In tal caso abbandonerei il C++ senza pensarci due volte.
Infatti il layer superiore (non a contatto con l'hw) non lo programmerò 
in C++, ma in un linguaggio proprietario.
In seguito forse ricodificherò con quest'ultimo anche le parti 
attualmente in C++.
Kiuhnm