Newsgroup: it.comp.lang.c++
Titolo: funzione membro template

Salve, sono un assiduo lurkatore del NG, e vorrei porvi una
domanda:
ho realizzato una funzione template che ritorna un integral type,
di byte sequenziali (anche un array di char, per es, cosa noiosa
con la rand() ), eccola:
#ifndef _RANDGEN_CPP_
#define _RANDGEN_CPP_
namespace randgen {
static ifstream urand("/dev/urandom");
static istream  rand("/dev/random");
static unsigned long base, seed;
template <class T>
bool randgen(T& t){
    if(!(rand.good() && urand.good()))
        return false;
    seed = 0;
//  per diminuire la capacita' di predizione
//  sul numero di pool bytes utilizzati
//  prendo una base da /dev/random
    rand.read((void*)base, sizeof(base));
    base =  base % 1024 + 255;
//  e un seed da /dev/urandom
    while(!seed)
        urand.read((void*)&seed, sizeof(seed));
//  decido se prendere dall'uno o l'altro a caso
    if((seed % base)){
        urand.read((void*)&t, sizeof(T));
    } else {
       rand.read((void*)&t, sizeof(T));
    }
    return t;
}
} // namespace
#endif // defined _RANDGEN_CPP_
lo so che e' per unices, il mio problema pero' e': come portarla
all'interno di una classe non template? qual'e' l'esatta
sintassi?
ci ho provato in mille modi: questo compila
/********
randgen.h
********/
#ifndef _RANDGEN_H_
#define _RANDGEN_H_
#include <fstream>
class randgen {
    ifstream *rand;
    ifstream *urand;
    unsigned long base;
    unsigned long seed;
    randgen(const randgen&);
    randgen& operator=(const randgen&);
public:
    randgen();
    ~randgen();
    template <class T>
    const T get(T& t);
};
#endif // defined _RANDGEN_H_
/********
randgen.h
********/
#ifndef _RANDGEN_CPP_
#define _RANDGEN_CPP_
#include "randgen.h"
randgen::randgen(){
 urand = new ifstream("/dev/urandom");
 rand  = new ifstream("/dev/random");
 rand->read((void*)base, sizeof(base));
 base = base % 1024 + 255;
}
randgen::~randgen(){
 delete rand;
 delete urand;
}
template <class T>
bool randgen::get(T& t){
    if(!(rand->good() && urand->good()))
        return false;
    seed = 0;
    while(!seed)
        urand->read((void*)&seed, sizeof(seed));
    if((seed % base)){
        urand->read((void*)&t, sizeof(T));
    } else {
       rand->read((void*)&t, sizeof(T));
    }
    return true;
}
#endif // !defined _RANDGEN_CPP_
/********
main.cpp
********/
#include <iostream>
#include "randgen.h"
int main(){
 randgen r;
 int i, count = 0;
 while (count++ < 1000) {
   r.get(i);
   cout << i << endl;
 }
 return 0;
};
ma fallisce il link, con una sfilza di:
/tmp/cc979dkk.o (.text+2e): undefined reference to
randgen<int>.get()
dov'e' l'errore ?
grazie.

 wrote :
| /********
| randgen.h
| ********/
| class randgen {
[...]
| public:
|     template <class T>
|     const T get(T& t);
| };
Ho postato per errore una versiona differente, quella corretta
(ovviamente :) e':
class randgen {
[...]
 public:
     template <class T>
     bool get(T& t);
};
Grazie e scusate per lo sbaglio
Walter

 wrote in message
384a6...@news.megasys.it...
| Ho postato per errore una versiona differente
ma non compila lo stesso! Santo Martelli, aiutami tu! :)
A questo punto comincio a credere che le funzioni mebro template di classi
non template non siano standard...

"Walter" <o@p> wrote in message news:384c0...@news.megasys.it...

Lo sono si`, ma vari compilatori hanno delle limitazioni
in merito (VC++6, mi pare, le supporta solo se inline, ad
esempio).
Alex

 wrote in message
8k5mti$4or$1...@nslave2.tin.it...
| Lo sono si`, ma vari compilatori hanno delle limitazioni
| in merito (VC++6, mi pare, le supporta solo se inline, ad
| esempio).
ho aggiunto l'inline, ma...
(VC++ 6)
F:\Projects\varie>cl main.cpp randgen.cpp -GX -TP -nologo
main.cpp
randgen.cpp
Generating Code...
main.obj : error LNK2001: unresolved external symbol "public: bool
__thiscall randgen::get(long &)" (?get@randgen@@QAE_NAAJ@Z)
main.obj : error LNK2001: unresolved external symbol "public: bool
__thiscall randgen::get(char (&)[10])" (?get@randgen@@QAE_NAAY09D@Z)
main.exe : fatal error LNK1120: 2 unresolved externals
(bcc 5.5)
F:\Projects\varie>\bcc55\bin\bcc32 main.cpp randgen.cpp -P -wall
Borland C++ 5.5 for Win32 Copyright (c) 1993, 2000 Borland
main.cpp:
randgen.cpp:
Turbo Incremental Link 3.0 Copyright (c) 1997, 1998 Borland International
Error: Unresolved external 'randgen::get<$c<...' referenced from
F:\PROJECTS\VARIE\MAIN.OBJ
Error: Unresolved external 'randgen::get<...' referenced from
F:\PROJECTS\VARIE\MAIN.OBJ
(g++ egcs-2.91.57)
[walter@metalzone varie]$ g++ main.cpp randgen.cpp -Wall -pedantic
/tmp/cct7UdW4.o: In function `main':
/tmp/ccHk38RS.o(.text+0x4e):main.cc: undefined reference to `bool
randgen::get<char ()[9]>(char ()[9] &)'
/tmp/ccHk38RS.o(.text+0x5e):main.cc: undefined reference to `bool
randgen::get<long>(long &)'
(g++ egcs-2.91.66)
[walter@metalzone varie]$ g++ main.cpp randgen.cpp -Wall -pedantic
/tmp/cct7UdW4.o: In function `main':
/tmp/cct7UdW4.o(.text+0x1b): undefined reference to `bool randgen::get<char
[9]>(char [9] &)'
/tmp/cct7UdW4.o(.text+0x2b): undefined reference to `bool
randgen::get<long>(long &)'
Se quattro dei piu' diffusi compilatori non se lo filano di pezzo, sara'
pure standard sulla carta, ma de facto non lo e'.
potreste consigliarmi un'alternativa?
Grazie.

 potreste consigliarmi un'alternativa?
Guarda questo esempio giocattolo:
// cons.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
class randgen {
public:
     template <class T>
         bool get(T& t) { t=0; return true; }
#ifdef IT
     template <class T>
         inline bool gat(T& t);
#endif
};
#ifdef IT
template<class T>
bool randgen::gat<T>(T& t)
{
    t=1; return false;
}
#endif
int main()
{
    randgen r;
    int i=0, j=0, count=0;
    while (count++ < 10) {
        r.get(i);
#ifdef IT
        r.gat(j);
#endif
        std::cout << i << " " << j << "\n";
    }
    return 0;
}
Se non definisci IT, VC++6 e` felice e contento.  Se
lo definisci, va in internal compiler error.  In altri
termini, pare che il tutto vada se la funzione membro
template e` inline ed e` definita contestualmente alla
sua dichiarazione.
Dovresti quindi potere usare una funzione membro inline
template come puro zucchero sintattico, che delega ad
una normale funzione template (non inline, non membro),
naturalmente passandole reference a tutto quel che le
serve.  Prova un po'...
Alex

 wrote in message
|
| Beh, non puoi contare diversi release di gcc come diversi
| compilatori, quindi, diciamo tre:-).
Ah, il piu' vecchio era il cygwin, ho dimenticato di specificarlo :)
| Guarda questo esempio giocattolo:
|
| #include "stdafx.h"
Che c'entra lo stdafx? (hai usato il wizard?)
| Dovresti quindi potere usare una funzione membro inline
| template come puro zucchero sintattico, che delega ad
| una normale funzione template (non inline, non membro),
| naturalmente passandole reference a tutto quel che le
| serve.  Prova un po'...
Ok, cosi' compila. ma allora preferisco la versione col namespace...
Tanto una f. esterna la devo implementare, e cosi almeno non ci sono
arzigogoli e va con tutti i compilatori...
(sigh)
il casino sta nell'effettivo binding a link-time della funzione?
Se la classe fosse template, la f. viene istanziata con la classe, mentre
con la f.m.template, dovrebbe esserlo con la chiamata, giusto?
e questo dovrebbe causare ambiguita', in quanto con la stessa classe
potrebbero essere istanziate chiamate a f() diverse:
ad esempio:
randgen r;
char c[10];
int i;
r.get(c); /* chiama randgen::get<char [10]>(char [10] &) */
r.get(i); /* chiama randgen::get<int> (int &) */
e il dubbio sta in come legare alla classe istanziata a compile time
funzioni diverse a link time, anche se hanno prototipi diversi?
e l'overloading?
(ri-sigh)
un'altra questione:
perche' VC++
randgen::get(char (&)[10]) /* a parte che non vede il template... :'( */
e g++
bool randgen::get<char[9]>(char [9] &)
cosa causa la differenza tra char[10] e char[9], visto che ho sempre
istanziato un char[10]?

 


 wrote in message
[...]
    Spostando l'implementazione in randgen.h dovresti risolvere
    il problema. In alternativa puoi instanziare esplicitamente
    le versioni che ti servono in randgen.cpp, ad es:
    template randgen::get<long>(long);
e infatti... grazie al tip di Alex Martelli, ho risolto la cosa... ma
nell'altro msg mi chiedo il perche', se una funzione e' fuori da una
classe, basta la dichiarazione e la compilazione separata va bene, mentre
come membro di una classe template la cosa non funzioni.