[C++] (Simpel ?) template probleem

Pagina: 1
Acties:

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Ik zit met een C++ template-probleempje. Nu ben ik nog niet zo'n template-expert, en gebruiken van andermans templates gaat wel vrij aardig, maar zelf schrijven... :S

Het idee is dat ik een Vertex class krijg die o.a. een toPoint() methode ondersteunt. Omdat er in mijn geval twee manieren zijn om een toPoint() uit te rekenen, en ik deze methode flexibel, maar wel at compile time wil kunnen kiezen, wil ik de Vertex class templaten.
Dus, stel dat ik een object van Vertex<fixed_tag> aanmaak, dan moet de 'gefixte' toPoint()-variant worden gebruikt, terwijl als ik een Vertex<interval_tag> aanmaak, dan moet er een andere toPoint() worden gebruikt.

Hoe doe ik dit goed? Ik heb tot nu toe:

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
struct fixed_tag{};
struct interval_tag{};

template<class S>
class Vertex{
  //
  //Member-vars, constructor e.d.
  //

  template<fixed_tag>
  Point toPoint() const;
  
  template<interval_tag>
  Point toPoint() const;
  
};

//Implementation

template<fixed_tag>
Point Vertex::toPoint() const{
  //Calculate and return fixed point
}

template<interval_tag>
Point Vertex::toPoint() const{
  //Calculate and return interval point
}


De bovenstaande code compiled niet (VS.NET), en ik heb al een heleboel varianten hierop geprobeerd, maar ik krijg mijn vinger maar niet achter wat ik fout doe...
Wat doe ik verkeerd?

Thanks.

  • marino_centrino
  • Registratie: November 2003
  • Laatst online: 05-04 20:14
ik zet de implementatie van de functies bij templated classes altijd maar gewoon in de klasse declaratie. Als de implementatie ergens anders staat dan lukt het mij ook nooit om het goed gecomileerd en gelinkt te krijgen. Dus de body van de methode gelijk bij de declaratie zetten.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Overweeg policies.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class DefaultToPointPolicy {
public:
   Point topoint() { // implementation }

protected:
   ~DefaultToPointPolicy() {}
};

template <
   typename S,
   class ToPointPolicy = DefaultToPointPolicy
>
Vertex : public ToPointPolicy {
public:

   Point makepoint() {
      return ToPointPolicy::topoint();
   }

};


in jouw geval moet je het denk ik zo doen...

C++:
1
2
3
template <typename T> topoint() { // compile time error }
template <> topoint<fixedstruct> { ... }
template <> topoint<realstruct> { ... }


En bij die implementatie moet je ook nog aangeven dat Vertex een template class is. Dus iets van template <class S> Vertex<S>::, en dan die andere template erbij...ik weet niet of je compiler dat pakt; nog nooit gedaan.

zo??
C++:
1
2
3
4
template<typename S> 
Point Vertex<S>::toPoint<interval_tag>() const{ 
  //Calculate and return interval point 
}

[ Voor 51% gewijzigd door Zoijar op 03-06-2004 12:11 ]


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

Psst, [google=template specialization] ;)

De kortste en bondigste uitleg.

Het punt van die policy zie ik niet echt, dit kun je met simpele specialization gewoon oplossen afaics.

[ Voor 22% gewijzigd door curry684 op 03-06-2004 12:32 ]

Professionele website nodig?


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:19
Hmz, ik volg niet helemaal waarom je dit compile-time wil oplossen. Is het niet zinnig om ofwel twee aparte methodes te maken voor de twee berekeningen, ofwel een enkele methode met een parameter die het type berekening aangeeft? (Het eerste lijkt me netter, trouwens.)

Als je per se een enkele methode in de klasse wil hebben maar je wil het gedrag ervan variëren, dan kun je er beter een virtual method van maken op een abstract base class en twee verschillende implementaties van de klasse schrijven. Class inheritance is precies bedoeld om met een eenduidige interface afwijkend gedrag te kunnen implementeren.

Verwijderd

Kan je niet zoiets doen?

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
struct fixed_tag{};
struct interval_tag{};

template<class S>
class Vertex
{
  //
  //Member-vars, constructor e.d.
  //

  Point toPoint(fixed_tag) const;
  
  Point toPoint(interval_tag) const;
  
};

//Implementation

Point Vertex::toPoint(fixed_tag) const
{
  //Calculate and return fixed point
}

Point Vertex::toPoint(interval_tag) const
{
  //Calculate and return interval point
}

//en dan zoiets...
Point p = v.toPoint (fixed_tag());

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Je gebruikt een policy als je twee types vertices wilt hebben, die je typedefed. Als je app dus bv altijd fixed point vertices gebruikt overal. Dan is het makkelijker en consistent.

Maar als je een lijst van vertices wilt, waar je op sommige fixed en op sommige iets anders wilt doen (compile time!), dan kan je beter een specialization gebruiken. Ik zie dat niet zo snel gebeuren.

Policies zijn ook uitbreidbaar, en de gebruiker kan zijn eigen policies schrijven zonder aan jouw vertex class te hoeven komen. Ik ben een policy fan :) Werkt echt veel makkelijker dan allemaal lastige template specializations. En bovendien is het net zo snel, alles wordt inlined.

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Goed, dankzij de hints van jullie ben ik er toch uitgekomen :D Het had dus moeten zijn (heb voor 't gemak maar even een dummy()-methode genomen ipv de eerdere toPoint())

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
/**  Only one class declaration */
template<class S>
struct Vertex{

  void dummy();
};


template<class S>
void Vertex<S>::dummy(){
  std::cout << "General dummy() method" << std::endl;
}

template< >
void Vertex<fixed_tag>::dummy(){
  std::cout << "Fixed dummy() method" << std::endl;
}

template< >
void Vertex<interval_tag>::dummy(){
  std::cout << "Interval dummy() method" << std::endl;
}


int main(){
  Vertex<int> v;
  Vertex<fixed_tag> vFixed;
  Vertex<interval_tag> vInterval;

  v.dummy();
  vFixed.dummy();
  vInterval.dummy();

  return 0;
}

Dit geeft als uitvoer:
code:
1
2
3
General dummy() method
Fixed dummy() method
Interval dummy() method

Allen, hardstikke bedankt voor het geven van de juiste voorzetjes!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Oh dat is wat je wilde. Ik dacht dat die vertex class zelf ook een template class was... wat je nu doet is iets anders dan wat je schreef :) Nu is het een specialized template class, en daarvoor was het een (totally) specialized template member (van een template class)

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

MrBucket schreef op 03 juni 2004 @ 14:33:
Allen, hardstikke bedankt voor het geven van de juiste voorzetjes!
No problem, je had het idee goed maar je moet even de exacte syntax van specialization weten :)

Professionele website nodig?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-05 23:07

.oisyn

Moderator Devschuur®

Demotivational Speaker

Als het aantal typen dat je kunt gebruiken voor een template gelimiteerd moet zijn, kun je beter enums gebruiken. In je voorbeeld heb je het bijvoorbeeld over een Vertex<fixed_tag> en een Vertex<point_tag>, maar wat let mij om een Vertex<MijnEigenType> te gebruiken? Terwijl als je een enum gebruikt, dan heb je in principe alleen maar het aantal mogelijkheden van die enum (ok, daar kun je nog wel om heen door een int te casten naar een enum type, maar dan ben je sowieso vies bezig)

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum vertex_type
{
    vertex_fixed,
    vertex_interval
};

template <vertex_type T>
struct Vertex
{
    // ...
};

int main ()
{
    Vertex<vertex_fixed> a;
    Vertex<vertex_interval> b;
    Vertex<int> c;  // error
    Vertex<3434> d; // error
}


Geen idee of die limitatie ook echt je bedoeling was, maar dat maakte ik een beetje op uit je originele code

[ Voor 8% gewijzigd door .oisyn op 03-06-2004 15:44 ]

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.


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

.oisyn schreef op 03 juni 2004 @ 15:43:
[nohtml]Als het aantal typen dat je kunt gebruiken voor een template gelimiteerd moet zijn, kun je beter enums gebruiken. In je voorbeeld heb je het bijvoorbeeld over een Vertex en een Vertex, maar wat let mij om een Vertex te gebruiken?
Waarom zou je die limitatie in een vertex willen? Op zich kan het, maar ik ging er niet van uit toen ik de specializations voorstelde: het hele idee achter templates is juist dat je iedere class erin mag frotten die de correcte interface aanbiedt, en dat iemand dus rustig een vertex mag bouwen met BigInts of complexe getallen :)

Professionele website nodig?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-05 23:07

.oisyn

Moderator Devschuur®

Demotivational Speaker

curry684 schreef op 03 juni 2004 @ 16:27:
het hele idee achter templates is juist dat je iedere class erin mag frotten die de correcte interface aanbiedt, en dat iemand dus rustig een vertex mag bouwen met BigInts of complexe getallen :)
De vertex is misschien een slecht voorbeeld omdat je daar idd gewoon een value-type nodig hebt, maar het is zeker niet onzinnig om soms het aantal implementaties te limiteren. Het "hele idee achter templates" is heus niet dat je er elk gewenst type in kon stoppen, anders hadden ze integral template parameters nooit geintroduceerd. Zeker voor templates die expliciete implementaties hebben voor een eindig aantal verschillende soorten is het gebruik van enums enorm handig, omdat je dan geen undefined behaviour krijgt als je er eens een keer wat anders in stopt.

Als jij een template class hebt waarbij je een functie expliciet specialiseert voor bepaalde template parameters, dan geeft dat undefined behaviour als je het voor andere typen gebruikt. Beter scherm je het gebruik van die andere typen dan gewoon af. En dan kun je wel zeggen dat, als je zo'n type gebruikt waar de ontwerper geen rekening mee heeft gehouden, dat je dan je eigen implementatie voor die ene methode kan leveren, maar dat compromised je class security gewoon (aangezien je zelf een implementatie voor een methode kan leveren, kun je ook ineens bij de private members van die class). Een foute opzet is dan gewoon in het geding (als de bedoeling is dat je zelf een implementatie ergens voor geeft, dan hoor je dat eigenlijk via een traits class oid te doen)

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.


  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 110% gewijzigd door Eelis op 18-02-2015 19:31 ]


  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Goed punt van die enum trouwens, is idd eleganter dan 2 structs :)
Eelis schreef op 03 juni 2004 @ 19:06:
TS:

En wat nou als je voor een Vector object beide punten wil berekenen?
Of wat nou als je een Vector uit een functie terugkrijgt van het 'verkeerde' type?

Moet je in dit soort gevallen de Vector gaan kopiëren naar een Vector van het gewenste bereken-type alleen maar om toegang te krijgen tot de gewenste berekenmethode? Dat lijkt me nogal onwenselijk, en daarom zou ik ervoor pleiten om af te zien van dit hele template verhaal, en om gewoon twee aparte functies in een niet-geparameteriseerde Vector te definiëren (of misschien zelfs daarbuiten).
Euhm, nee. Let wel, het gaat hier om een vertex, in dit geval een vertex in de context van een zoekgraaf. Deze zoekgraaf leid ik rechtstreeks af vanuit een andere datastructuur, en er zijn 2 manieren waarop Vertices afgeleid kunnen worden uit deze onderliggende datastructuur (idd, fixed en interval).

Omdat deze 2 manieren nooit in dezelfde graaf terecht kunnen komen heeft het geen meerwaarde om deze Vertices te specialiseren als subclasses. Echter, subclasses hebben wel nadelen, nl. dat ze meer geheugen innemen en iets trager zijn (door de VTable), en dat de virtuele functieaanroepen niet geinlined kunnen worden.

Daarnaast is het ook zo dat niet alleen de Vertex class te maken krijgt met specialisaties afhankelijk van de 'fixed' of 'interval'-methode, maar ook de classes die de graaf en edges uit de graaf voorstellen.
Het idee is dat door of 'fixed_tag', of 'interval_tag' in de graaf-klasse te pluggen, de manier waarop de zoekgraaf wordt gegenereerd makkelijk te kunnen selecteren.

  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 99% gewijzigd door Eelis op 18-02-2015 19:31 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Het is net zo dom om twee verschillende functies dezelde naam te geven, als twee versies van een enkele functie twee namen te geven. Dus als je twee versies hebt, noem ze dan vooral allebei toPoint.

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


  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 99% gewijzigd door Eelis op 18-02-2015 19:32 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
MrBucket schreef op 03 juni 2004 @ 11:43:
Ik zit met een C++ template-probleempje. Nu ben ik nog niet zo'n template-expert, en gebruiken van andermans templates gaat wel vrij aardig, maar zelf schrijven... :S

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
struct fixed_tag{};
struct interval_tag{};

template<class S>
class Vertex{
  //
  //Member-vars, constructor e.d.
  //

  template<fixed_tag>
  Point toPoint() const;
  
  template<interval_tag>
  Point toPoint() const;
  
};

//Implementation

template<fixed_tag>
Point Vertex::toPoint() const{
  //Calculate and return fixed point
}

template<interval_tag>
Point Vertex::toPoint() const{
  //Calculate and return interval point
}


De bovenstaande code compiled niet (VS.NET), en ik heb al een heleboel varianten hierop geprobeerd, maar ik krijg mijn vinger maar niet achter wat ik fout doe...
VS.NET heeft geen 'export', dus alle definities (niet alleen de class definitie) moeten in de header.
Wat je bedoelt is
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
struct fixed_tag{};
struct interval_tag{};

template<class S>
class Vertex{
  //
  //Member-vars, constructor e.d.
  //

  template<typename TAG>
  Point toPoint( ) const;
  
};
template<typename S>
template< >
Point Vertex::toPoint<fixed_tag>() const{
  //Calculate and return fixed point
}

template<typename S>
template< >
Point Vertex::toPoint<interval_tag>() const{
  //Calculate and return interval point
}

alleen is dat illegaal volgens 14.7.3/18, [Explicit specialization]:
[Example:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
template<class T1> class A {
template<class T2> class B {
template<class T3> void mf1(T3);
void mf2();
};
};
template<> template<class X>
class A<int>::B { };
template<> template<> template<class T>
void A<int>::B<double>::mf1(T t) { };
template<class Y> template<>
void A<Y>::B<double>::mf2() { }; // ill-formed; B<double> is specialized but
// its enclosing class template A is not
—end example]

[ Voor 8% gewijzigd door MSalters op 04-06-2004 14:31 . Reden: quoten van C++ standard geeft niet automatisch C++ tags 8)7 ]

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


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-05 23:07

.oisyn

Moderator Devschuur®

Demotivational Speaker

MSalters: je hoeft van die toPoint toch geen template te maken? Dit werkt toch net zo goed?

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct fixed_tag {}; 
struct interval_tag {}; 

template <class T>
class Vertex
{
    Point toPoint () const;
};

template <>
Point Vertex<fixed_tag>::toPoint () const
{
    // ...
}

template <>
Point Vertex<interval_tag>::toPoint () const
{
    // ...
}

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: 09-04 22:08
.oisyn schreef op 04 juni 2004 @ 15:06:
MSalters: je hoeft van die toPoint toch geen template te maken? Dit werkt toch net zo goed?

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
struct fixed_tag {}; 
struct interval_tag {}; 

template <class T>
class Vertex
{
    Point toPoint () const;
};

template <>
Point Vertex<fixed_tag>::toPoint () const {}
template <>
Point Vertex<interval_tag>::toPoint () const {}
Ja, dat werkt ook. Ik snap alleen nog steeds niet helemaal wat de TS nou precies wil. Zijn er idd maar twee soorten Vertex'en, Vertex<fixed_tag> en Vertex<interval_tag>, of is het antal veel groter en moeten alle Vertex varianten allemaal allebei de methoden ondersteunen?

PS. de algemene Vertex<T> moet je in jouw geval geen toPoint methode geven; de individuele Vertex specialisatie voegen die members toe.

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


  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
.oisyn schreef op 04 juni 2004 @ 15:06:
MSalters: je hoeft van die toPoint toch geen template te maken? Dit werkt toch net zo goed?
<snip>
Ik kan me voorstellen dat MSAlters een beetje in de war is gebracht door het code-fragment in de OP, waarin ik (daar ben ik nu dus achter) zowel de class als de method probeerde te templaten. De bedoeling was dus idd, zoals hierboven, alleen de class te templaten.

--edit--
Ik lees net de post hierboven pas. Er zijn idd maar twee soorten Vertex-objecten, ofwel een 'fixed' variant, ofwel een 'interval'-variant.
--

Uitbreiding op dit probleem:
Stel dat ik, naast het type fixed_tag of interval_tag, ook nog een numerieke parameter n mee wil geven in de template, maar daar tijdens het specialiseren nog geen waarde aan wil geven (omdat het een waarde is die ik in de specialisaties van toPoint() wil gebruiken).
Tijdens het declareren wil ik daar pas een waarde aan geven, dus zoiets:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct fixed_tag {}; 
struct interval_tag {}; 

template <class T, int N>
class Vertex
{
    Point toPoint () const;
};

template <> //What should I specify here?
Point Vertex<fixed_tag, N>::toPoint () const  //Partial specialization?
{
    // Use N here, say:
    int iVal = N + 1;
}

//Same goes for interval_tag

//Create vertex object like this:
Vertex<fixed_tag, 5> v;

Hoe doe ik dit dan? VS.NET ondersteunt geen partial specialization, en volgens mij is bovenstaande fragment daar wel een geval van.

Hoe zou ik dit het beste aan kunnen pakken?
Modjes, kan dit nog in dit topic, of is het beter een nieuwe te openen?

[ Voor 7% gewijzigd door MrBucket op 04-06-2004 16:48 ]


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

MrBucket schreef op 04 juni 2004 @ 16:46:
[...]
Modjes, kan dit nog in dit topic, of is het beter een nieuwe te openen?
Is toch hetzelfde onderwerp nog steeds? :)

Professionele website nodig?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-05 23:07

.oisyn

Moderator Devschuur®

Demotivational Speaker

MrBucket schreef op 04 juni 2004 @ 16:46:
Hoe doe ik dit dan? VS.NET ondersteunt geen partial specialization, en volgens mij is bovenstaande fragment daar wel een geval van.
Het is idd partial specialization. Welke versie van vs.net gebruik je, 2002 of 2003? (Resp. VC++ 7.0 en 7.1). 2003 ondersteunt namelijk wel partial specialization.

Het probleem is echter dat partial specialisation niet mogelijk is op individuele functies; die kun je alleen explicit specialisen.

Als het zou kunnen zou het vrij straightforward in code zijn: je hebt namelijk 1 template parameter die niet vast staat, dus daar maak je een parameter van in de template parameter list. Het type staat wel vast, dus die hoef je daar niet te specificeren. Dat wordt dus:

C++:
1
2
3
4
template <int N> Point S<fixed_tag, N>::toPoint ()
{
    //...
}


maar goed, het kan dus niet, dan zul je gewoon de hele class zelf moeten specialisen

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.


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Wederom een policy?

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <int N>
class TPPolicy {

   Point toPoint() {
      return Point(N+1);
   }

// protected dtor
};

template <
   template <int N> class Policy
>
class Vertex : public Policy {
public:
// topoint wordt rechtstreeks op de policy aangeroepen
// kan eventueel een wrapper maken die Policy::toPoint aanroept
};

typedef Vertex<TPPolicy<2> > MyVertex; // let op spatie tussen >> !

[ Voor 8% gewijzigd door Zoijar op 04-06-2004 18:10 ]

Pagina: 1