Toon posts:

[c++] Template member aanroep vanuit template probleem

Pagina: 1
Acties:

Verwijderd

Topicstarter
De volgende code:
C++:
1
2
template <typename T>
void f (T t) { t.g<int>(); }

is volgens Comeau ongeldig:
code:
1
2
3
4
5
6
7
type name is not allowed
    t.g<int>();
        ^

expected an expression
    t.g<int>();
             ^

Waarom is een type name daar niet toegestaan? En waarom verwacht hij daar een expressie?


Als ik vervolgens de volgende code toevoeg:
C++:
1
template <typename U> void g ();

compileert het zaakje wel. In f roep ik duidelijk een member functie aan, hoe kan één of andere nonmember f ineens geldig maken?

Verwijderd

Topicstarter
De volgende code werkt wel:
C++:
1
2
template <typename T>
void f (T t) { t.template g<int>(); }


De tweede vraag is echter nog steeds een mysterie.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

14.2.4 ;)
When the name of a member template specialization appears after . or ­> in a postfixexpression, or after :: in a qualifiedid that explicitly depends on a templateargument (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a nontemplate.

Example:

class X {
public:
template<size_t> X* alloc();
};

void f(X* p)
{
X* p1 = p->alloc<200>(); // illformed:< means less than
X* p2 = p->template alloc<200>(); // fine: < starts explicit qualification
}
end example

Verwijderd

Hmm.. Ik heb ook een template gerelateerd probleempje. Is wel niet hetzelfde als het probleem van de topicstarter, maar dat scheelt weer een thread openen ;)

Code in een cpp file
C++:
1
    (void)itoa(Abs<int>(i),(char *)(this->m_pData + 1),10);


Algorithm.h header file
C++:
1
template <class T> LD_XBASE const T &Abs(const T &t);


Algorithm.cpp
C++:
1
2
3
4
5
template <class T> 
const T &ABS(const T &t)
{
  if (t < 0) return -t;
}


De compiler vind het allemaal prima (als ik algorithm.h include in die eerste cpp file natuurlijk), maar de linker niet :

error LNK2001: unresolved external symbol "int const & __cdecl Abs(int const &)" (?Abs@@YAABHABH@Z)

N.B. de macro LD_XBASE is hier gedefinieerd als _declspec(dllexport). Wat ik dus wil is deze functies vanuit een DLL exporteren. Maar nu weigert de linker al bij het bouwen van de DLL.

Op msdn.microsoft.com heb ik wel e.e.a. gevonden over de kennelijk nogal matige template support van MSVC6, maar alleen dat het keyword export niet werkt in combinatie met templates. Over dllexport wordt niets gemeld

Verwijderd

Akorahill>> Je probeert een templated function te exporteren, maar niet een instantie van die templated function. (Daarnaast lijkt het me beter een T uit Abs() te retourneren en geen const T&.)

blah.h:
C++:
1
2
3
4
5
6
template <class T> T abs(const T& t)
{
        return t < 0 ? -t : t;
}

extern template int abs<int>(const int&);

blah.cc:
C++:
1
2
#include "blah.h"
template int abs<int>(const int&); // explicit instantiation for export

Verwijderd

mietje : je hebt volkomen gelijk. Stom natuurlijk om een const T& te returnen, dat gaat never werken (hij returned dan als t < 0 is een reference naar een object wat weggegooid wordt bij het verlaten van de functie |:( )

Ik had al zo'n vermoeden dat het exporteren van een template function op deze manier niet kon. Het vreemde is alleen dat ik voordat ik deze code toegevoegd had er bij het compilen al warning waren m.b.t. std::vector<>.

In diezelfde trant dus : std::vector<XString>
warning C4251: 'm_xvBuf' : class 'std::vector<class XString,class std::allocator<class XString> >' needs to have dll-interface to be used by clients of class 'XLogCtrl'
Alleen dat gaat dus prima. Als ik in een programma aan die DLL linkte, gaf dat geen enkel probleem... Komt dat dan omdat dat stukje STL altijd uit een statische library gelinkt wordt?
Hoe werkt dat dan met STL? Ik link namelijk MSVCRT.LIB dynamisch mee (de app hangt dus aan MSVCRT.DLL)...

Verwijderd

Hmmm het toevoegen van die template instantiations gaat niet helemaal goed :
XAlgorithm.cpp
h:\xlib\sources\xbase\xalgorithm.cpp(50) : fatal error C1001: INTERNAL COMPILER ERROR
(compiler file '.\template.cpp', line 6317)
Please choose the Technical Support command on the Visual C++
Help menu, or open the Technical Support help file for more information
Error executing cl.exe.

XAlgorithm.obj - 1 error(s), 0 warning(s)
Maar eigenlijk wil ik dat ook helemaal niet. Wat ik wil (de templated function exporteren, zodat ik in alle projecten waar ik algorithm.h include en de lib meelink willekeurige instanties van deze templated functies kan gebruiken) kan dus niet begrijp ik?
Moet ik dan de source includen in al die projecten?

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Template functies defineer je meestal gewoon in je header files, juist vanwege dit probleem.

Verwijderd

Zoijar : prima, dat kan ook. Maar als ik die header file nou in twee verschillende .cpp's include in hetzelfde project? Dan gaat de linker weer piepen dat een functie meer dan een keer bestaat... En om nou iedere keer de cpp file ook in alle projecten op te nemen : kan wel : liever niet. Is het mogelijk om een functie alleen voor de lokale module te declareren om dit probleem te omzeilen? Bijvoorbeeld door
C++:
1
template<class T>static T Abs(const T &t) {...}

Verwijderd

Verwijderd schreef op 11 oktober 2002 @ 14:35:
Ik had al zo'n vermoeden dat het exporteren van een template function op deze manier niet kon. Het vreemde is alleen dat ik voordat ik deze code toegevoegd had er bij het compilen al warning waren m.b.t. std::vector<>.
Dat is een bekend probleem bij msvc. Je kunt die warnings in het geval van stl-containers gewoon negeren.
Alleen dat gaat dus prima. Als ik in een programma aan die DLL linkte, gaf dat geen enkel probleem... Komt dat dan omdat dat stukje STL altijd uit een statische library gelinkt wordt?
Hoe werkt dat dan met STL? Ik link namelijk MSVCRT.LIB dynamisch mee (de app hangt dus aan MSVCRT.DLL)...
Er zit zo goed als geen object-code aan de STL, het is (bijna) zuiver een library van header-files. Dat komt omdat templates in C++ eigenlijk een soort "veredelde macro's met typechecking" zijn. (Daarom moet je ook de implementaties van templates in headerfiles zetten, zoals Zoijar al zegt.).

Het gevolg voor de generatie van code is dat er normalerwijze éénmaal code voor een geinstantierde template gegenereerd wordt per object file. De linker sloopt vervolgens de dubbele instanties eruit als meerdere object files samen gelinked worden tot een applicatie.

Je kunt de linker helpen door dmv. een explicit instantiation van de template, die je vervolgens exporteert. Dan genereert de compiler maar éénmaal code voor die template-instantie, en wel enkel in de object file waar jij de explicit instantiation doet. (Dit is wat ik boven doe.)

Samenvattend:
• De implementatie (code) van een template moet in een headerfile, hij moet in alle .cc files beschikbaar zijn (dmv. een #include)
• Je hoeft template instances normaal niet te exporteren, de compiler doet het werk.
• De export van een template instance moet ook in een headerfile, liefst in de zelfde als de template definitie staat.
• De instantiation zelf moet in een .cc file.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Nog een opmerking, MS VC.NET ondersteund geen export op templates.
Zie ook: MSDN Artikel

[ Voor 0% gewijzigd door Zoijar op 11-10-2002 15:53 . Reden: Was niet echt een toevoeging, meer een opmerking :P ]


Verwijderd

OK Zoijar, mietje, mijn dank _/-\o_
Ik heb alle template functies nu gewoon in een header file geprakt, en het werkt als een zonnetje. Ik hoef de template functies niet eens apart te instantieren.
Gewoon in nieuwe projecten de header includen en gaan... Perfect!

edit:
Zoijar : dat had ik ook al vermeld. Er staat alleen iets over het export keyword, niks over _declspec(dllexport)....


Hehe.. bespaart GoT ook es een keertje tijd ;)

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Ah ok zie het nu ja, over heen gelezen :P

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
Verwijderd schreef op 11 oktober 2002 @ 15:44:
Er zit zo goed als geen object-code aan de STL, het is (bijna) zuiver een library van header-files. Dat komt omdat templates in C++ eigenlijk een soort "veredelde macro's met typechecking" zijn. (Daarom moet je ook de implementaties van templates in headerfiles zetten, zoals Zoijar al zegt.).
Dat geldt niet helemaal voor de VC6/VC7 STL en (in nog mindere mate) de meeste andere compilers van het moment. Sommige members van sommige std::vector<> instances zijn al geinstantieerd en zitten in de run-time library.

De nieuwste Dinkumware STL heeft bovendien de daadwerkelijke implementatie terug verhuisd naar .cpp files. Dit compileert voorlopig alleen nog op EDG ( lees Comeau )

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Verwijderd

MSalters schreef op 11 oktober 2002 @ 17:24:
Dat geldt niet helemaal voor de VC6/VC7 STL en (in nog mindere mate) de meeste andere compilers van het moment. Sommige members van sommige std::vector<> instances zijn al geinstantieerd en zitten in de run-time library.
Agreed, daarom zet ik er "bijna" tussen haakjes bij. Ik maakte de boel opzettelijk eenvoudiger dan ze in werkelijkheid was for the sake of argument.
De nieuwste Dinkumware STL heeft bovendien de daadwerkelijke implementatie terug verhuisd naar .cpp files. Dit compileert voorlopig alleen nog op EDG ( lees Comeau )
Op zich wel een nobel streven, maar ik ben bang dat er nog minstens 15 jaar programmeurs zullen zijn die implementaties in headerfiles neerkalken. Om dat te voorkomen zou je ook impliciete inlining in class-bodies moeten verbieden, een feature die misschien wel aards-lelijk is, maar ook verdomd handig.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op 11 oktober 2002 @ 17:54:
Op zich wel een nobel streven, maar ik ben bang dat er nog minstens 15 jaar programmeurs zullen zijn die implementaties in headerfiles neerkalken.


en niet omdat ze eigenwijs zijn ;), maar meer omdat er vrijwel geen compilers zijn die de implementatie apart pikken (een uitzondering daargelaten :))

Ik scheid zelf overigens ook meestal de implementatie van de definitie, door de implementatie in een aparte header te gooien die geinclude wordt door de header waar de definitie in staat... werkt wel fijner imho (alleen weer gezeur met template functions in template classes in MSVC (6, 7, 7.1), maar die gebruik ik vrijwel nooit)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

.oisyn schreef op 11 oktober 2002 @ 18:09:
en niet omdat ze eigenwijs zijn ;), maar meer omdat er vrijwel geen compilers zijn die de implementatie apart pikken (een uitzondering daargelaten :))
Ik bedoelde meer de situatie in de toekomst, wanneer 99% van de compilers de feature wel ondersteunt. Ook dan zul je nog mensen hebben die het toch doen, al dan niet om portable te blijven met die 1%. Net zo als er nu nog steeds mensen zijn die K&R C schrijven ipv. ANSI C. Het probleem is vasgeroest zitten aan verouderde programmeerstijlen, bedrijfsculturen en (les)boeken.

Nog ff back ontopic ;)
Ik zou persoonlijk dit soort simpele functies inlinen, dat wil nog wel eens vergeten worden bij templating. Dus:
template <class T> inline T abs(const T& t) { return t < T(0) ? -t : t; }

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

[nohtml]
Verwijderd schreef op 11 oktober 2002 @ 18:43:
[...]


Ik bedoelde meer de situatie in de toekomst, wanneer 99% van de compilers de feature wel ondersteunt. Ook dan zul je nog mensen hebben die het toch doen, al dan niet om portable te blijven met die 1%. Net zo als er nu nog steeds mensen zijn die K&R C schrijven ipv. ANSI C. Het probleem is vasgeroest zitten aan verouderde programmeerstijlen, bedrijfsculturen en (les)boeken.
ah ok, heb je idd gelijk in :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
Verwijderd schreef op 11 oktober 2002 @ 18:43:
Ik zou persoonlijk dit soort simpele functies inlinen, dat wil nog wel eens vergeten worden bij templating. Dus:
template <class T> inline T abs(const T& t) { return t < T(0) ? -t : t; }
Tsja, ik heb recent besloten zelf geen inlines meer neer te zetten. Een profiler vertelt je precies waar het uitmaakt. Ik heb nu nl. een verzameling inline functies die een factor 100 minder vaak worden aangeroepen als de meest gebruikte functie, dat doet dus helemaal niets. Aan de andere kant was de profiler conclusie dat een bepaalde loop gemiddeld 1,2 keer werd doorlopen (maar wel 48 miljoen keer in twee minuten testen ) een reden om die loop eens te herschrijven. Dat maakt dus wel uit.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
Verwijderd schreef op 11 oktober 2002 @ 17:54:
Op zich wel een nobel streven, maar ik ben bang dat er nog minstens 15 jaar programmeurs zullen zijn die implementaties in headerfiles neerkalken. Om dat te voorkomen zou je ook impliciete inlining in class-bodies moeten verbieden, een feature die misschien wel aards-lelijk is, maar ook verdomd handig.
Overbodig. register is ook niet verboden, en dat is toch ook gewoon uitgestorven. inline is net zo'n hint, impliciet of expliciet.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Verwijderd

MSalters schreef op 11 oktober 2002 @ 22:17:
Overbodig. register is ook niet verboden, en dat is toch ook gewoon uitgestorven. inline is net zo'n hint, impliciet of expliciet.
Ik bedoelde dus eigenlijk zaken als:
C++:
1
2
3
4
5
6
7
class X {
public:
    explicit X(int x_) : _x(x_) {}
    int x() const { return _x; }
private:
    int _x;
};

Alle methods in deze class zijn impliciet geinlined, en de hele implementatie zit bijgevolg in de class-body.
Pagina: 1