Newsgroup: it.comp.lang.c++
Titolo: fattorizzazione codice

Siano A, B1 e B2 tre classi tali che A < B1 e A < B2, dove X < Y 
significa che l'interfaccia di X è sottoinsieme di quella di Y. 
Praticamente si vuole evitare di duplicare codice.
La mia soluzione sembra troppo complicata per essere consigliata. Non è 
difficile da capire, ma soltanto poco elegante.
La mia speranza è che riusciate a trovare qualcosa di meglio.
Trattandosi di un codice di prova, ho tralasciato B2. Vedo adesso che 
alcuni 'protected' sono superflui, ma male non fanno.
#include <iostream>
class A
{
	int v;
public:
	A() {}
	A(int v) : v(v) {}
	int get() const { return v; }
	A& neg() { v = -v; return *this; }
};
class Hidden
{
protected:
	class PA : private A
	{
		int v;
	protected:
		PA() {}
		PA(int v) : A(v) {}
	public:
		using A::get, A::neg;
	};
	template <class T>
	class Op : public PA
	{
	protected:
		Op() {}
		Op(int v) : PA(v) {}
		Op(const ::A& a) : PA(a.get()) {}
	public:
		T& neg() { PA::neg(); return *static_cast<T*>(this); }
		friend T operator+(const T& x, const T& y) { return T(x.get()+y.get()); }
	};
};
class B1 : private Hidden, public Hidden::Op<B1>
{
	int m;
public:
	B1(const ::A& a) : Op<B1>(a) {}
	B1() : m(1) {}
	B1(int v) : Op<B1>(v) {}
};
int main()
{
	A a(5);
	B1 b(7),c(8);
//	PA* df = &b;		// errato per via di Hidden
	B1 d = b+c;
	B1 e = d.neg();
	std::cout << e.get() << std::endl;
}
Kiuhnm

 Praticamente si vuole evitare di duplicare codice.
Cioe': A e' data, ma vuoi riutilizzarne il codice evitando la
sostituibilita'? Perche' la strada piu' semplice a me parrebbe:
template <typename DisambT>
class A_impl
{
	int v;
        DisambT * self;
protected:
	A_impl(DisambT & s) : self(&s) {}
	A_impl(DisambT & s, int v) : self(&s), v(v) {}
public:
	int get() const { return v; }
        DisambT& neg() { v = -v; return *self; }
};
class A : public A_impl<A>
{
public:
	A() : A_impl(*this) {}
	A(int v) : A_impl(*this, v) {}
};
e lo stesso per B1 e B2.
Se invece A e' data e non modificabile, perche' non semplicemente:
class B1 : private A
{
public:
        B1() {}
        B1(int v) : A(v) {}
        using A::get;
        B1 & neg() { return static_cast<B1 &>(A::neg()); }
};
Ciao,
Nicola

 };
In questo modo sei costretto a duplicare tutte le signature delle 
funzioni analoghe a 'neg', cioè a inserire vari forward.
Nel mio caso ho molte funzioni di una o due righe quindi la duplicazione 
di "linee di codice C++" sarebbe evidente.
Però posso, come nel tuo primo esempio, fare derivare anche A da A_impl<A>:
---
class A;
class B1;
class Hideout
{
	friend A;
	friend B1;
	template <class T>
	class A_impl
	{
		int v;
		T& self() { return *static_cast<T*>(this); }
	public:
		A_impl() {}
		A_impl(int v) : v(v) {}
		int get() const { return v; }
		T& neg() { v = -v; return self(); }
		friend T operator+(const T& x, const T& y) { return T(x.get()+y.get()); }
	};
};
class A : public Hideout::A_impl<A>
{
public:
	A(int v) : A_impl<A>(v) {}
};
class B1 : public Hideout::A_impl<B1>
{
//	int m;
public:
	B1(int v) : A_impl<B1>(v) {}
//	int getM() { return m; }
};
---
Qui incontro un problema: se aggiungo una variabile membro a B1, mi 
viene segnalato l'errore
"error C2248: 'Hideout::A_impl<T>' : cannot access private class 
declared in class 'Hideout'"
in corrispondenza della riga "int m;"
Ho provato a compilare il codice in Dev-C++ e funziona. Il compilatore 
si è lamentato perché avrei dovuto scrivere "friend class A" invece di 
"friend A" e poi mi ha fatto aggiungere qualche "Hideout::".
Anche con queste correzioni, MSVC8 non ne vuole sapere.
Ecco perché nel codice precendente avevo usato la derivazione per 
nascondere (purtroppo non completamente) le classi.
Kiuhnm

 di "linee di codice C++" sarebbe evidente.
Pero' puoi combinare i due approcci e far derivare A_impl da A.
Ciao,
Nicola

 Pero' puoi combinare i due approcci e far derivare A_impl da A.
E' quello che ho fatto nell'ultimo codice postato.
L'unica differenza tra il mio codice iniziale e l'ultimo è che ora 
derivo tutto da A_impl.
Adesso devo capire se ho torto io o VC8.
Kiuhnm

 Adesso devo capire se ho torto io o VC8.
Ho torto io.
Kiuhnm


Comunque mi sbagliavo su una cosa: il C++ /non/ "supporta" i 
programmatori paranoici, infatti non ho trovato una soluzione perfetta.
Sono costretto a fare una cosa del genere:
class Hideout
{
	friend struct X;
	template <class T>
	class A_impl
	{
	};
};
struct X
{
	class A : public Hideout::A_impl<A>
	{
	};
	class B1 : public Hideout::A_impl<B1>
	{
	};
};
... con la conseguenza che devo portarmi dietro la X.
Kiuhnm