[C++] MSVC2008 Template Instantiation *

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
Ik ben nog redelijk nieuw met C++, wel flinke MATLAB ervaring, maargoed dat helpt niet te veel.

Ik heb een library, zelf geschreven, waarin allerlei handige helper functies zitten, zoals een paar om makkelijk een reeks waarden in een std::vector te knallen. Die wil ik natuurlijk op zowel floats als doubles kunnen uitvoeren, dus heb ik er een template overheen gegooid. Als ik nu simpel onderstaande in de header en .cpp gooi gaat het niet goed.
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// easy vector assignment
template <class T> void AssignVec(vector<T>* vector, const T& val0, const T& val1, const T& val2);
template <class T> void AssignVec(vector<T>* vector, const T& val0, const T& val1, const T& val2, const T& val3);

// .cpp
// easy vector assignment
template <class T>
void AssignVec(vector<T>* vector, const T& val0, const T& val1, const T& val2)
{
    (*vector).resize(3,0);
    (*vector)[0] = val0;
    (*vector)[1] = val1;
    (*vector)[2] = val2;
}

template <class T>
void AssignVec(vector<T>* vector, const T& val0, const T& val1, const T& val2, const T& val3)
{
    (*vector).resize(4,0);
    (*vector)[0] = val0;
    (*vector)[1] = val1;
    (*vector)[2] = val2;
    (*vector)[3] = val3;
}


Dan krijg ik linker errors als ik de functies uit mijn lib wil gebruiken, bijv.: "error LNK2001: unresolved external symbol:
code:
1
error LNK2001: unresolved external symbol "void __cdecl AssignVec<float>(class std::vector<float,class std::allocator<float> > *,float const &,float const &,float const &)" (??$AssignVec@M@@YAXPAV?$vector@MV?$allocator@M@std@@@std@@ABM11@Z)


Ik heb daar toen de volgende link op gevonden die het een en ander wel aardig uitlegde: Comeau Computing.

De oplossing hiervoor die ik gebruik (niet zelf bedacht) is dit:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
// header
// macro for instantiating different types to be assigned
#define Instantiate_Assign(Type)\
    template void AssignVec(vector<Type>* vector, const Type& val0, const Type& val1, const Type& val2);\
    template void AssignVec(vector<Type>* vector, const Type& val0, const Type& val1, const Type& val2, const Type& val3);

// .cpp
// instantiate the various types to be assigned -- http://www.comeaucomputing.com/techtalk/templates/#export
Instantiate_Assign(int);
Instantiate_Assign(unsigned int);
Instantiate_Assign(double);
Instantiate_Assign(float);


Dit is naar omdat ik of vooruit moet kijken voor welke datatypes de functies beschikbaar moeten zijn, of elke keer als ik ergens tegen aanloop mijn libraries moet veranderen. Is er een andere/betere manier om dit aan te pakken of moet ik maar blijven duimen dat Microsoft ooit export implementeerd?

Acties:
  • 0 Henk 'm!

  • Stukfruit
  • Registratie: Oktober 2007
  • Niet online
Waarom schrijf je niet gewoon een Point/Vector class met template parameter voor het gebruik van integers, floats, doubles, etc?

Dat zit wel Schnorr.


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-09 22:17
Lijkt erop dat je die templates niet in een header hebt staan? Dat zal ( bij de meeste compilers ) nl niet werken; struikelblok is hier dat je daarvoor het gevreesde 'export' keyword nodig hebt en dat zal naar verwachting niet worden geimplementeerd in de meeste compilers.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

In MSVC moeten templates altijd inline beschikbaar zijn, waar je ze ook gebruikt.

Om het kort te houden, betekent dat dat je je code niet kan splitsen in een .h en een .cpp
Je kan eventueel splitsen in een .h en een .inl, die je onderin je .h include, waardoor je toch nog 2 files hebt, maar dat brengt soms meer problemen met zich mee dan het oplost (partial specialization en andere).

Over het algemeen kan je het beste gewoon je hele constructie uittypen in de header file, dan krijg je dus iets als dit:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template <class T> 
inline void AssignVec(vector<T>* vector, const T& val0, const T& val1, const T& val2) 
{ 
    (*vector).resize(3,0); 
    (*vector)[0] = val0; 
    (*vector)[1] = val1; 
    (*vector)[2] = val2; 
} 

template <class T> 
inline void AssignVec(vector<T>* vector, const T& val0, const T& val1, const T& val2, const T& val3) 
{ 
    (*vector).resize(4,0); 
    (*vector)[0] = val0; 
    (*vector)[1] = val1; 
    (*vector)[2] = val2; 
    (*vector)[3] = val3; 
}

(basically, alleen de implementatie in de header. Het inline keyword is in MSVC optioneel vziw, maar kan nooit kwaad)

Tevens nog een paar sidenotes:
- waarom een pointer naar de vector als argument? gebruikelijk is om een reference te gebruiken
- een vector in C++ is een dynamische array. een vector in de wiskunde is een fixed size rijtje getallen. ondanks dat de naam hetzelfde is, zijn het verschillende beestjes :) als je 3D math wilt gaan doen, heroverweeg dan het gebruik van std::vector

[ Voor 14% gewijzigd door MLM op 15-02-2009 13:31 ]

-niks-


Acties:
  • 0 Henk 'm!

  • TweakPino
  • Registratie: September 2005
  • Laatst online: 08:06
Je kunt je code splitsen in een header en een code file, maar je moet dan wel in je .cpp file "templates" aanmaken voor het type van je functies die je wilt gebruiken.
Het volgende moet je onderaan in je .cpp bestand zetten, dan kun je de functie AssignVec gebruiken voor float en double in elk ander .cpp bestand waar je het header bestand include.

C++:
1
2
3
4
5
template void AssignVec(vector<double>* vector, const double& val0, const double& val1, const double& val2);
template void AssignVec(vector<double>* vector, const double& val0, const double& val1, const double& val2, const double& val3);

template void AssignVec(vector<float>* vector, const float& val0, const float& val1, const float& val2);
template void AssignVec(vector<float>* vector, const float& val0, const float& val1, const float& val2, const float& val3);


Hierdoor verplicht je de compiler de functie AssignVec voor zowel float als double te gegenereren wanneer je .cpp bestand compileert en kun je deze functie dus ook gebruiken in je andere .cpp bestanden, zonder dat je die error (error LNK2001: unresolved external symbol "void __cdecl AssignVec<float>) krijgt.

[ Voor 16% gewijzigd door TweakPino op 15-02-2009 14:23 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Er is een 'normale' manier om templates op te splitsen in header en implementatie bestanden m.b.h.v. het extern keyword. Volgens het boek C++ Templates van Van de Voorde is de beste oplossing om het in twee headers op te splitsen:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//a_def.hpp
#ifndef A_DEF_HPP
#define A_DEF_HPP

template<typename T>
class A {
public:
  T foo();
};

#endif

//a.hpp
#ifndef A_HPP
#define A_HPP

#include "a_def.hpp"

template<typename T>
T A<T>::foo() {}

#endif


En dan gewoon telkens a.hpp includen in je code.

Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

TweakPino schreef op zondag 15 februari 2009 @ 14:19:
Je kunt je code splitsen in een header en een code file, maar je moet dan wel in je .cpp file "templates" aanmaken voor het type van je functies die je wilt gebruiken.
Het volgende moet je onderaan in je .cpp bestand zetten, dan kun je de functie AssignVec gebruiken voor float en double in elk ander .cpp bestand waar je het header bestand include.

C++:
1
2
3
4
5
template void AssignVec(vector<double>* vector, const double& val0, const double& val1, const double& val2);
template void AssignVec(vector<double>* vector, const double& val0, const double& val1, const double& val2, const double& val3);

template void AssignVec(vector<float>* vector, const float& val0, const float& val1, const float& val2);
template void AssignVec(vector<float>* vector, const float& val0, const float& val1, const float& val2, const float& val3);


Hierdoor verplicht je de compiler de functie AssignVec voor zowel float als double te gegenereren wanneer je .cpp bestand compileert en kun je deze functie dus ook gebruiken in je andere .cpp bestanden, zonder dat je die error (error LNK2001: unresolved external symbol "void __cdecl AssignVec<float>) krijgt.
Het idee van templates is juist dat ze generiek zijn, hetgene wat jij nu doet is types als nog hardcoden.

Het is erg jammer dat er zo weinig (like, 1) compilers zijn die zich aan de standaard houdt, zodat dit soort workarounds nodig zijn :(

-niks-


Acties:
  • 0 Henk 'm!

  • Gorion3
  • Registratie: Februari 2005
  • Laatst online: 24-08 08:28

Gorion3

ABC++

Header:
C++: test.h
1
2
3
4
5
6
template <class T>
class Test
{
public:
    Test(T val);
};


CPP:
C++: test.cpp
1
2
3
4
5
6
#include "test.h"
template <class T>
Test<T>::Test(T val)
{

}


Waarom zou dit niet werken?

Awesomenauts! Swords & Soldiers


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

Gorion3 schreef op zondag 15 februari 2009 @ 17:57:
Header:
C++: test.h
1
2
3
4
5
6
template <class T>
class Test
{
public:
    Test(T val);
};


CPP:
C++: test.cpp
1
2
3
4
5
6
#include "test.h"
template <class T>
Test<T>::Test(T val)
{

}


Waarom zou dit niet werken?
Omdat het geen standaard C++ is (missend keyword), en zelfs als dat keyword er stond, werkt het op geen enkele compiler (op 1 na, Comeau). En waarom dat is? Vraag het de compiler makers zou ik zeggen, hoe dan ook heb je geen keus dan er om heen te werken :P

Geloof je het niet? Probeer dan dit eens te compilen:
code:
1
2
3
4
5
6
7
8
//random CPP file, behalve test.cpp
#include "test.h"

int main(int, char**)
{
  Test<int> t(10);
  return 0;
}

[ Voor 13% gewijzigd door MLM op 15-02-2009 18:15 ]

-niks-


Acties:
  • 0 Henk 'm!

  • Gorion3
  • Registratie: Februari 2005
  • Laatst online: 24-08 08:28

Gorion3

ABC++

Aah my bad, ik bedoelde natuurlijk dit..
Header
C++: test.h
1
2
3
4
5
6
7
8
template <class T>
class Test
{
public:
    Test(T val);
};

#include "test.cpp";


test.cpp, niet builden ("Exclude from build" in VS 2008)
C++: test.cpp
1
2
3
4
5
template <class T>
Test<T>::Test(T val)
{
    T v = val;
}


Dit werkt wel, en volgens mij ook correct. Kan er natuurlijk naast zitten :) (Zelf getest in VS2008, compiled en runt bij mij)

[ Voor 16% gewijzigd door Gorion3 op 15-02-2009 21:35 ]

Awesomenauts! Swords & Soldiers


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-09 16:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

MLM schreef op zondag 15 februari 2009 @ 13:25:
(basically, alleen de implementatie in de header. Het inline keyword is in MSVC optioneel vziw, maar kan nooit kwaad)
Het inline keyword is verplicht, wegens de ODR (geldt dus ook voor de post van Gorion3 hierboven ;)).
MLM schreef op zondag 15 februari 2009 @ 14:47:
Het is erg jammer dat er zo weinig (like, 1) compilers zijn die zich aan de standaard houdt, zodat dit soort workarounds nodig zijn :(
3, Borland C++ en Intel C++ ook. Niet zo gek, alle 3 de compilers zijn gebaseerd op de EDG front-end, die het ondersteunt.
Desalniettemin blijft export een vreemde feature waarvan mensen in de standards committee liever gehad hebben dat de feature nooit bestaan heeft. Herb Sutter heeft zelfs gesuggereerd om de feature te verwijderen uit de nieuwe release van de C++ standaard, of iig optioneel te maken zodat een compiler zonder export support toch conforming kan zijn.

Bovendien, export wordt vaak gezien als de heilige graal, maar de realiteit is allesbehalve dat. Alle zogenaamde "voordelen" die export volgens veel mensen zou moeten geven worden in de praktijk onderuit gehaald. Je hebt geen snellere compile tijden en je kunt de source niet daadwerkelijk verborgen houden. Dit, plus de enorme moeite die je moet doen om het te implementeren, en het feit dat het leven van export in standaard C++ niet eens zeker is, maakt dat het gewoon niet loont om het alsnog te implementeren. Het hele bestaan van export kun je imho beter gewoon vergeten.

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.


Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
Bedankt voor de antwoorden!

Eerst een paar vragen/punten: Ik zie Ginyal "#ifndef A_DEF_HPP #define A_DEF_HPP [...] #endif" gebruiken. Ik gebruik "#pragma once". Het lijkt mij dat beide ertoe leiden dat het geheel maar een keer geinclude wordt?

@.oisyn et al.: Bovenstaande functie definities compileren en linken (code draait ook zonder probleem) met en zonder het inline keyword. Heb het er maar in gegooid omdat het volgens mij absoluut geen kwaad kan en misschien wel voordelen heeft.

@MLM: Bedankt voor de pointers :p. std::vector is wat ik hier wil gebruiken, toevallig heb ik nu triples en quads nodig, maar had ook anders kunnen zijn. Of zeg je dat het handiger zou zijn om zelf een array van de juiste lengte te malloccen? Dat is eigenlijk (voor mij) een wespennest waar ik me niet in wil steken, met vector gaat alle constructie, resizing en destructie vanzelf.
Wat betreft je andere punt, ik geloof dat ik het niet volg (pointers en references haal ik inderdaad wel eens door elkaar). Als ik in de oude code "AssignVec(vector<T>* vector, [...])" vervang door "AssignVec(vector<T>& vector, [...])" (ook in declaraties natuurlijk), dan krijg ik "illegal indirection". In de nieuwe compileerd hij de functie niet (zit in een header tenslotte), dus krijg ik pas gezeik als ik de functie wil gebruiken. De aanroep "AssignVec(&_vTransOps[i].params, 0.f,0.f,0.f);" resulteert nu in "error C2664: 'AssignVec' : cannot convert parameter 1 from 'std::vector<_Ty> *' to 'std::vector<_Ty> &'". Moet ik AssignVec() anders aanroepen, of doelde je op iets anders?

Bedankt Ginyal en Gorion3. Volgens mij zijn jullie methoden gelijk, en ze werken inderdaad mooi. Ik heb nu een extra twee bestanden Template_clusterfucks.h en Template_clusterfucks_def.h die ik weer in mijn normale header include (zodat de toegang tot de functies in mijn lib niet veranderd, gewoon zelfde .h includen en alles werkt _/-\o_ ):

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// header helpers.h
#include "Template_clusterfucks.h"


// Template_clusterfucks.h
#pragma once
#include "Template_clusterfucks_def.h"

// easy vector assignment
template <class T>
inline void AssignVec(vector<T>* vector, const T& val0, const T& val1, const T& val2)
{
    (*vector).resize(3,0);
    (*vector)[0] = val0;
    (*vector)[1] = val1;
    (*vector)[2] = val2;
}

template <class T>
inline void AssignVec(vector<T>* vector, const T& val0, const T& val1, const T& val2, const T& val3)
{
    (*vector).resize(4,0);
    (*vector)[0] = val0;
    (*vector)[1] = val1;
    (*vector)[2] = val2;
    (*vector)[3] = val3;
}


// Template_clusterfucks_def.h
#pragma once
#include <vector>

// easy vector assignment
template <class T> void AssignVec      (vector<T>* vector, const T& val0, const T& val1, const T& val2);
template <class T> void AssignVec      (vector<T>* vector, const T& val0, const T& val1, const T& val2, const T& val3);


Added benefit: Nu kan ik meteen voor sommige functies een complete templatization doen ala bovenstaande, waar ik andere functies via de methode die ik eerst postte makkelijk alleen voor bepaalde types kan defineren. Ik heb bijvoorbeeld een functie MaakGauss([...]) die voor floats en doubles moet bestaan, maar niet voor integer-typen. Die functie aanroepen en integers eruit willen hebben zou nu @linktime foutgaan inplaats van moeilijk te debuggen zijn.

Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

TheWickedD schreef op maandag 16 februari 2009 @ 05:02:
@MLM: Bedankt voor de pointers :p. std::vector is wat ik hier wil gebruiken, toevallig heb ik nu triples en quads nodig, maar had ook anders kunnen zijn. Of zeg je dat het handiger zou zijn om zelf een array van de juiste lengte te malloccen? Dat is eigenlijk (voor mij) een wespennest waar ik me niet in wil steken, met vector gaat alle constructie, resizing en destructie vanzelf.
Wat betreft je andere punt, ik geloof dat ik het niet volg (pointers en references haal ik inderdaad wel eens door elkaar). Als ik in de oude code "AssignVec(vector<T>* vector, [...])" vervang door "AssignVec(vector<T>& vector, [...])" (ook in declaraties natuurlijk), dan krijg ik "illegal indirection". In de nieuwe compileerd hij de functie niet (zit in een header tenslotte), dus krijg ik pas gezeik als ik de functie wil gebruiken. De aanroep "AssignVec(&_vTransOps[i].params, 0.f,0.f,0.f);" resulteert nu in "error C2664: 'AssignVec' : cannot convert parameter 1 from 'std::vector<_Ty> *' to 'std::vector<_Ty> &'". Moet ik AssignVec() anders aanroepen, of doelde je op iets anders?
Natuurlijk als je pointer naar reference zet hoef je niet meer te indirecten (als in, "(*vector)" wordt gewoon "vector"). En als je 3D math wilt doen, kan je beter iets gebruiken met predefined operators voor addition/dot product enzo :P

Anyway, als je code werkt en het doet wat je wilt, verander er niks meer aan...

[ Voor 43% gewijzigd door MLM op 16-02-2009 10:55 ]

-niks-


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-09 16:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

TheWickedD schreef op maandag 16 februari 2009 @ 05:02:
@.oisyn et al.: Bovenstaande functie definities compileren en linken (code draait ook zonder probleem) met en zonder het inline keyword. Heb het er maar in gegooid omdat het volgens mij absoluut geen kwaad kan en misschien wel voordelen heeft.
Zolang je de functies maar in 1 translation unit (source file) gebruikt zal er weinig mis gaan. Maar zodra je 'm in meerdere translation units gebruikt dan krijgt elke gecompileerde file z'n eigen versie, en krijg je doorgaans linker conflicten. Wellicht dat MSVC++ daar wat toleranter in is, maar desalniettemin verplicht de C++ standaard je in dat geval om inline te gebruiken, omdat je dan meerdere definities van dezelfde functie mag hebben (of ie daadwerkelijk wordt geinlined of niet is verder niet zo interessant en de compiler heeft er doorgaans ook maling aan - die doet wel wat 'm zelf het beste lijkt)
@MLM: Bedankt voor de pointers :p. std::vector is wat ik hier wil gebruiken, toevallig heb ik nu triples en quads nodig, maar had ook anders kunnen zijn. Of zeg je dat het handiger zou zijn om zelf een array van de juiste lengte te malloccen? Dat is eigenlijk (voor mij) een wespennest waar ik me niet in wil steken, met vector gaat alle constructie, resizing en destructie vanzelf.
Maar wil je überhaupt resizen dan? Is het niet zo dat je van tevoren al weet hoe groot je vector moet zijn? Dan kun je net zo goed een class zoals std::tr1::array (of z'n boost counterpart) gebruiken - dan geef je de lengte van de array mee als template parameter en heb je dus helemaal geen allocaties meer nodig. 't Is dan ook sneller.

Maar echt voor math dingen is het wellicht handiger om gewoon je eigen vector class te schrijven (desnoods gebaseerd op / geinherit van str::tr1::array), zodat je operators kunt overloaden.

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.


Acties:
  • 0 Henk 'm!

  • Stukfruit
  • Registratie: Oktober 2007
  • Niet online
Voor math dingen zou je trouwens ook nog kunnen kijken naar Eigen, dat ziet er echt prachtig uit :)

Dat zit wel Schnorr.


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Voor algemene (i.e. n-dimensionale) lineaire algebra zou ik gewoon ublas gebruiken. In het verleden heb ik geexperimenteerd met veel van dat soort verschillende libraries, en uiteindelijk kom ik altijd weer uit op ublas + lapack. Voor fixed-sizes, bv graphics, vind ik eigenlijk gewoon het makkelijkste om wat verschillende classes te schrijven voor vector 2, 3 en 4, quaternion en matrix 3x3, 3x4, 4x4. Uiteindelijk werkt dat gewoon makkelijker dan al die template dingen naar mijn idee.De performance winst van die dingen valt toch altijd tegen (en zodra er aliasing in het spel is valt het vaak helemaal weg); als het echt nodig is kan je dat wel handmatig doen.

Dat Eigen ziet er wel ok uit. Alleen jammer dat ze het altijd alleen over performance hebben, terwijl stability veel belangrijker is. Daarom gebruik ik bv niet snel zo'n SVD die ze daar implementeren. Met lapack weet je in ieder geval dat het code is die al jaren op stability is getest.

[ Voor 17% gewijzigd door Zoijar op 16-02-2009 13:51 ]


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
.oisyn schreef op zondag 15 februari 2009 @ 22:13:

3, Borland C++ en Intel C++ ook. Niet zo gek, alle 3 de compilers zijn gebaseerd op de EDG front-end, die het ondersteunt.
Desalniettemin blijft export een vreemde feature waarvan mensen in de standards committee liever gehad hebben dat de feature nooit bestaan heeft. Herb Sutter heeft zelfs gesuggereerd om de feature te verwijderen uit de nieuwe release van de C++ standaard, of iig optioneel te maken zodat een compiler zonder export support toch conforming kan zijn.
Laat ik hier maar op reageren. Herb Sutter heeft dat inderdaad voorgesteld, en er is zelfs over gestemd. De (informele) stemming viel zo in het nadeel van Herb uit, dat hij z'n voorstel introk. De officiele WG21 mening was dus dat het feature nog steeds verplicht moet blijven.
Bovendien, export wordt vaak gezien als de heilige graal, maar de realiteit is allesbehalve dat. Alle zogenaamde "voordelen" die export volgens veel mensen zou moeten geven worden in de praktijk onderuit gehaald. Je hebt geen snellere compile tijden en je kunt de source niet daadwerkelijk verborgen houden. Dit, plus de enorme moeite die je moet doen om het te implementeren, en het feit dat het leven van export in standaard C++ niet eens zeker is, maakt dat het gewoon niet loont om het alsnog te implementeren. Het hele bestaan van export kun je imho beter gewoon vergeten.
De EDG mensen zijn het hier niet met je eens. Ik heb voor de desbetreffende WG21 mening een pre-release Comeau compiler getest ('k werd tenslotte verondersteld om geinformeerd te stemmen) en mijn conclusie was dat het al op de eerste poging werkte zoals verwacht. Zelfs recursief, dus een template uit a.cpp die een ander template uit b.cpp instantieert die weer een template uit a.cpp instantieert. De kwaliteit van die implementatie was overall hoog genoeg om tot de conclusie te komen dat een redelijke implementatie van export mogelijk is, zoals EDG claimde. Vandaar dat ik destijds ook tegen Herb cq. voor export stemde.

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


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-09 16:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik zeg ook niet dat het niet werkt, maar veel gehoorde argumenten van mensen die export willen zien in hun compiler zijn "snellere compile tijden" en "sourcecode van templates in de library niet mee hoeven shippen". Beide zijn niet waar, en voor de EDG implementatie moet je zelfs de gehele sourcecode sec erbij hebben ipv dat ze gebruik maken van een opgeslagen parsetree.

Ik meen ook ergens gelezen te hebben dat een apart subteam van EDG een jaar bezig is geweest om export support toe te voegen. Welke voordelen van export justified dan zoveel werk? Geeneen imho. Wat zijn sowieso echt de voordelen van export tov een #include "a_impl.h"?

.edit: ah ja, het stond ook in N1426, het voorstel van Sutter. 1.5 jaar om te plannen, 3 manjaren om te implementeren.
(Note: By comparison, implementing the complete Java language from scratch took the
same team 2 person-years.)
Dan kunnen ze wel tegen stemmen, maar dat betekent niet dat de argumenten daarmee van de baan waren natuurlijk :). Bij Intel C++ staat export support default uit, bij MSVC++ gaan ze het denk ik nooit implementeren omdat er geen vraag naar is, en ook bij gcc zie ik het niet snel gebeuren tenzij er echt een team opstaat dat zich commit aan het implementeren van export in die compiler.

[ Voor 42% gewijzigd door .oisyn op 16-02-2009 17:42 ]

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.


Acties:
  • 0 Henk 'm!

Verwijderd

TheWickedD schreef op maandag 16 februari 2009 @ 05:02:
Ik zie Ginyal "#ifndef A_DEF_HPP #define A_DEF_HPP [...] #endif" gebruiken. Ik gebruik "#pragma once". Het lijkt mij dat beide ertoe leiden dat het geheel maar een keer geinclude wordt?
Klopt. Ik was niet bekend met #pragma once. Nog een klein schoonheidsfoutje van mij: i.p.v. foo_DEF gebruik ik meestal foo_DECL om aan te geven dat het om een declaratie en niet om een definitie want die staat juist in dat andere bestand. Niet dat het voor het principe iets uitmaakt.

Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
.oisyn schreef op maandag 16 februari 2009 @ 17:24:
Ik zeg ook niet dat het niet werkt, maar veel gehoorde argumenten van mensen die export willen zien in hun compiler zijn "snellere compile tijden" en "sourcecode van templates in de library niet mee hoeven shippen". Beide zijn niet waar, en voor de EDG implementatie moet je zelfs de gehele sourcecode sec erbij hebben ipv dat ze gebruik maken van een opgeslagen parsetree.
Af en toe moet je opletten dat je oorzaak en gevolg niet door elkaar haalt. Het klopt dat er mensen zijn die belangstelling hebben voor de features die jij noemt. Die features verwachten ze echter van hun compiler, niet van ISO. Van ISO verwachten ze alleen een taal die de bouw van dergelijke compilers mogelijk maakt.

Andere mensen hebben andere wensen voor hun compilers, en dus voor ISO. Name scoping is bijvoorbeeld een goede reden. Two-phase name lookup in non-exported template implementaties is lastig. De eerste fase name lookup gebeurt namelijk in elke .cpp waar de header included is, maar ODR eist dat het resultaat desondanks hetzelfde is.

Het keyword 'export' maakt het eerste mogelijk, en lost het tweede probleem op.
Ik meen ook ergens gelezen te hebben dat een apart subteam van EDG een jaar bezig is geweest om export support toe te voegen.
Ze hebben maar 3 man in totaal :). Maar Daveed heeft inderdaad gedurende een jaar aan export gwerkt. Niet non-stop, overigens, gezien de WG21 bijdragen die EDG deed gedurende die tijd.
.edit: ah ja, het stond ook in N1426, het voorstel van Sutter. 1.5 jaar om te plannen, 3 manjaren om te implementeren.
Dat is dan ook een paper van Herb, en enigzins eenzijdig. Zo stapt hij erg gemakkelijk over het probleem heen dat EDG een hele verzameling Defects vond in de standaard. De tweede implementatie van export is dus een stuk makkelijker, omdat je nu wel de standaard (+Defect Resolutions) kunt volgen.

Herb suggereert ook dat de EWG betere voorstellen had voor name scoping. Het klopt dat er ideeen waren, maar die hadden nog 0 implementaties. Van export was op z'n minst bekend dat het mogelijk was. En zo waren er wel meer subjectieve argumenten: "It is easy for programmers to write programs that have unpredictable meaning". Dat probleem verbetert juist door export. Je houdt de name lookup van dependent names in meerdere scopes, maar export reduceert de name lookup scope van non-dependent names tot 1 .cpp file. En juist die lookup van non-dependent names is verrassend. (Iedereen snapt dat de lookup X in van T::X afhangt van je exacte T.)

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


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-09 22:17
Neemt niet weg dat twee veel gebruikte compilers ( MS en gcc ) vziw vooralsnog geen plannen hebben om deze feature te implementeren, dus het is nog verre van portable om het te gebruiken.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
Hmm, ik ben nu een wat ingewikkelder geval aangelopen.

Deze keer zit de functie die ik wil templatizeren in een class in mijn library:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CFileIO
{
public:
[...]
    // write files
    template <class T> bool WriteMatToFile(const CMatrix<T>& tMat_, const string& sHeader_);
    template <class T> bool WriteVecToFile(const vector<T>& tVec_, const string& sHeader_);
    template <class T> bool WriteValToFile(const T& tVal_, const string& sHeader_);
    bool WriteStrToFile( const string& strVal_ );
[...]
};

// macro for instantiating different types to be outputted
#define Instantiate_Write(Type)\
    template bool CFileIO::WriteMatToFile<Type>(const CMatrix<Type>& tMat_, const string& sHeader_);\
    template bool CFileIO::WriteVecToFile<Type>(const vector<Type>& tMat_, const string& sHeader_);\
    template bool CFileIO::WriteValToFile<Type>(const Type& tMat_, const string& sHeader_);


Uiteindelijk heb ik het omgevormd tot onderstaande, waarmee de library prima compileerd, maar ik nog steeds linker errors krijg. Heb vele andere varianten geprobeerd met de template include buiten de class en CFileIO:: voor elke functie declaratie en definitie, maar dat werkt ook niet: dan krijg je foutmeldingen ala functie bestaat niet in class (wat ook wel logisch is, ik neem aan dat je een classdefinition niet achteraf kan herdefineren). Hoe los ik zoiets op?

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//FileIO.h
class CFileIO
{
public:
[...]    
    // write files
    // include for template that should be defined for all data types (see end of Helper.h for explanation)
    #include "FileIO_def.h"
    bool WriteStrToFile( const string& strVal_ );
[...]
};


//FileIO_def.h
#pragma once
#include "FileIO_decl.h"

// Writing functions
template <class T>
bool CFileIO::WriteMatToFile( const CMatrix<T>& tMat_, const string& sHeader_ )
{
    [...]
}

template <class T>
bool CFileIO::WriteVecToFile(const vector<T>& tVec_, const string& sHeader_)
{
    [...]
}

template <class T>
bool CFileIO::WriteValToFile( const T& tVal_, const string& sHeader_ )
{
    [...]
}


//FileIO_decl.h
#pragma once
#include "Matrix.h"

// Writing functions
template <class T> bool WriteMatToFile(const CMatrix<T>& tMat_, const string& sHeader_);
template <class T> bool WriteVecToFile(const vector<T>& tVec_, const string& sHeader_);
template <class T> bool WriteValToFile(const T& tVal_, const string& sHeader_);

Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
Goed, na mijn eigen post doorgelezen te hebben kreeg ik nog een idee, dat bleek het op te lossen. Ik was te moeilijk aan het doen, ik kan de functiedefinities gewoon lekker in de classdefinition laten hangen, ik moet alleen zorgen dat de functieimplementatie ook beschikbaar is..
Dit in een losse post, voor het geval anderen tegen dit probleem aanlopen, hoewel het probleem überhaupt een hersenkronkel was.
Goed:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//FileIO.h
class CFileIO
{
public:
[...]
    // write files
    template <class T> bool WriteMatToFile(const CMatrix<T>& tMat_, const string& sHeader_);
    template <class T> bool WriteVecToFile(const vector<T>& tVec_, const string& sHeader_);
    template <class T> bool WriteValToFile(const T& tVal_, const string& sHeader_);
    bool WriteStrToFile( const string& strVal_ );
[...]
};

// include for template that should be defined for all data types (see end of Helper.h for explanation)
#include "FileIO_def.h"


//FileIO_def.h
#pragma once

// Writing functions
template <class T>
bool CFileIO::WriteMatToFile( const CMatrix<T>& tMat_, const string& sHeader_ )
{
    [...]
}

template <class T>
bool CFileIO::WriteVecToFile(const vector<T>& tVec_, const string& sHeader_)
{
    [...]
}

template <class T>
bool CFileIO::WriteValToFile( const T& tVal_, const string& sHeader_ )
{
    [...]
}
Pagina: 1