Newsgroup: it.comp.lang.c++
Titolo: curious compile-time contraint

Considerate il seguente codice:
 
 template< class Derived >
 struct base
 {
    void func()
    {
      static_cast<Derived*>(this)->func();
  }
 };
 struct pippo: public base<pippo>
 {
   void func()	{ }
 };
 template< class T >
  void call_func( base<T>& a ) { a.func(); }
 int main() 
 {
     pippo p;
     call_func( p );
 }
(Per il momento non chiedetevi perchè uno dovrebbe fare una cosa del
genere.)
Come potete vedere, base<pippo>::func() invoca pippo::func() e tutto
funziona egregiamente.
Se tolgo il metodo func() da pippo, il codice compila comunque (in quanto
pippo eredita func() da base<pippo>), ma ovviamente accade il disastro:
func() invoca sé stessa.
Ora, vorrei escogitare qualcosa che facesse fallire la compilazione
qualora la classe usata in quel pattern di derivazione non implementasse
essa stessa un metodo func().
Sono riuscito a inventarmi un trucchetto per ottenere l'effetto *opposto*.
Ossia: compila quando pippo *non* ridefinisce func(), e *non* compila
quando pippo lo ridefinisce. Basta aggiungere nel costruttore di base<>:
     // ...
     base()
	{
             typedef void ( base<Derived>:: * base_func_t ) ();
             base_func_t f1 =  &Derived::func;
             if(f1); // solo per evitare i warning
       }
     //...
L'ho provato con il Comeau, quindi probabilmente è conforme. Ad ogni modo,
come ho detto, questo trucchetto non mi serve a niente. Io vorrei ottenere
esattamente l'effetto opposto, ma non mi riesce di trovare una soluzione.
Forse non c'è.
Qualche idea?
Max

 essa stessa un metodo func().
Ho provato questo:
base()
{
  BOOST_STATIC_ASSERT(&base::func != &Derived::func);
}
Ahime' non viene compilato ne da g++ 3.2 ne da BCB 6.0. Comeau lo
accetta, pero' solo in modo non-strict. Comunque almeno concettualmente
puo' essere una strada.
Ciao,
Nicola

 concettualmente puo' essere una strada.
E in effetti era la strada che avevo intrapreso all'inizio.
Avevo definito una classe template cosě:
  template<bool> struct is_true { };
  template<>     struct is_true<false>; // solo fwd decl!
e nel ctor di base avevo messo:
    is_true< &base::func != &Derived::func >  dummy; if(&dummy);
Qui il Comeau dice
     error: non-integral operation not allowed in nontype
anche in "relaxed mode" (usando il 'tryitout' online). 
Sono andato a guardarmi i sorgenti di Boost e ho visto che
BOOST_STATIC_ASSERT usa una tecnica analoga alla mia, perň non sono
riuscito a compilare il mio codice con Comeau nemmeno in "relaxed mode".
Max

"Max M." ha scritto 
: Considerate il seguente codice:
:  
template< class Derived >
 struct base;
template< class T >
  void Check( void (base<T>::*)() ) {}
  int  Check(...) { return 1;}
:  template< class Derived >
:  struct base
:  {
   base() { char check[sizeof( Check(&Derived::func) )]; }
:     void func()
:     {
:       static_cast<Derived*>(this)->func();
:   }
:  };
: 
:  struct pippo: public base<pippo>
:  {
:    void func() { }
:  };
: 
:  template< class T >
:   void call_func( base<T>& a ) { a.func(); }
: 
:  int main() 
:  {
:      pippo p;
:      call_func( p );
:  }
: 
Con il bcb 5 e con il g++ 3.02 sembra funzionare.
ciao
Massimo

 
Ottimo. Funziona alla perfezione anche con Comeau.
Con le versioni più vecchie di GCC viene emesso soltanto un warning
 
  warning: ANSI C++ forbids taking the sizeof a void type 
che non sono riuscito a far tramutare in errore nemmeno usando -pedantic o
-ansi. Ma non è un problema, con una leggera modifica si riesce a far
fallire la compilazione anche con quelle versioni di GCC.
Grazie,
Max

 
Ragionando sulla tua idea di usare una funzione, ho escogitato un altro
trucchetto:
 
 template<class T,class U> struct are_different_types { };
 template<class T> struct  are_different_types<T,T>; // solo fwd decl.
 template<class T,class U> inline void must_be_different_types( T a, U b) 
 { 
	are_different_types<T,U>();
 }
 template< class Derived >
 struct base 
 {
   base(){ if(0) must_be_different_types( &base::func, &Derived::func ); }
  //...
Compila e funziona con il Comeau. Funziona anche con le versioni più
vecchie di GCC. Se qualcuno me lo prova con il BCB e con 
VC.NET
 mi fa un
piacere.
Ha un inconveniente, però. Se in Derived ci sono più overload di func, non
compila. Ma mi pare questo accada anche con tua soluzione.
Max

     is_true< &base::func != &Derived::func >  dummy; if(&dummy);
Nella libreria Loki di Alexandrescu c'e' la possibilita' di testare a
compile time se una classe sia o non sia derivata da un'altra, mi
pare.  Prova a vedere.
-- 
Maurizio Loreti                         
http://www.pd.infn.it/~loreti/mlo.html
Dept. of Physics, Univ. of Padova, Italy          loreti at pd dot infn dot it

"Max M." ha scritto
:
: Compila e funziona con il Comeau. Funziona anche con le versioni più
: vecchie di GCC. Se qualcuno me lo prova con il BCB e con 
VC.NET
 mi fa un
: piacere.
:
Con il BCB(5.5) ho provato questo:
 template< class Derived >
  struct base;
 template<class T,class U> struct are_different_types { };
 template<class T> struct  are_different_types<T,T>; // solo fwd decl.
 template<class T,class U> inline void must_be_different_types( T a, U b)
 {
   are_different_types<T,U>();  // questa e` la linea 10 cui si rif. gli
Err.
 }
 template< class Derived >
 struct base
 {
   base(){ if(0) must_be_different_types( &base::func, &Derived::func ); }
    void func()
    {
      static_cast<Derived*>(this)->func();
  }
 };
 struct pippo: public base<pippo>
 {
   void func() { } // [1] Se commento non compila
   //void func(int ){} // [2] Se scommento non compila
 };
// ... come prima
Cosi` mi da 2 warning:
[C++ Warning] Check.cpp(16): W8008 Condition is always false
[C++ Warning] Check.cpp(16): W8066 Unreachable code
Commentando la linea [1] oltre ai 2 warning da 3 errori =.
[C++ Error] Check.cpp(10): E2450 Undefined structure
            'are_different_types<void (base<pippo>::*)()>'
Scommentando la linea [2] e non commentando la [1] oltre ai 2 warning:
[C++ Error] Check.cpp(16): E2285 Could not find a match for
            'must_be_different_types<T,U>(void (base<pippo>::*)(),
            void (pippo::*)())'
La linea 16 e` quella del costruttore di base.
La 10 e` segnata.
Ciao
Massimo