[C++] code afhankelijk van template type?

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Darkvater
  • Registratie: Januari 2001
  • Laatst online: 26-08-2024

Darkvater

oh really?

Topicstarter
Hier is eigenlijk wat ik wil doen:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class base {
public:
  void function(int) = 0;
};

template <class T> myclass: public base {
public:
 void function(int) {
  otherclass* i = somevalue_from_somewhere.
  if (typeof(T) == otherclass) {
    somevalue = i.id;
  } else {
    somevalue = 0;
  }
}


Dit compileert niet aangezien voor andere klassen dan otherclass "id" niet bestaat, en de compiler zeurt. Ik dacht aan het specialiseren van de functie, maar zonder parameters van die type werkt het ook niet. De base klasse wil ik niet templatisen. Heeft iemand enig idee hoe ik zoiets zou kunnen oplossen? Heb al gekeken naar BOOST me BOOST_TYPEOF, en boost::is_same<>, maar ze geven alleen maar of types terug of compile-time asserts. Maar misschien mis ik iets.

[ Voor 3% gewijzigd door Darkvater op 09-04-2009 13:48 ]


Windows Vista? *NEVER* Het waarom - Opera forever!!!
I've seen chickens that were more menacing. Chickens in a coma. On ice. In my fridge


Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Sowieso is je code überhaupt geen geldige C++ (de typeof constructie bestaat niet en je zou otherclass::id moeten gebruiken), maar ik ga er maar vanuit dat je even snel een simpel voorbeeld hebt ingetypt.

Je zult een template wrapper moeten maken die 'id' teruggeeft als ie bestaat, en anders gewoon 0. Het meest straightforward is die wrapper handmatig specializen voor elk type dat een 'id' heeft. Je kan geloof ik wel wat met SFINAE doen, maar dat wordt wat ingewikkeld. Eens kijken of ik iets kan in elkaar kan zetten...

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!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dit werkt :)
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
#include <iostream>

template<class T>
struct has_id
{
    template<size_t N> struct type { };
    typedef char small;
    struct big { char c[10]; };
    template<class U> static small test(U*, type<sizeof(U::id)>* = 0);
    static big test(...);

    enum { value = (sizeof(test((T*)0)) == sizeof(small)) };
};

template<class T, bool B = has_id<T>::value>
struct get_id
{
    enum { id = T::id };
};

template<class T>
struct get_id<T, false>
{
    enum { id = 0 };
};

struct A { };
struct B { static const int id = 34; };

int main()
{
    std::cout << get_id<A>::id << std::endl;
    std::cout << get_id<B>::id << std::endl;
}

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!

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

MLM

aka Zolo

Ik moest dat 3x lezen en een wikipedia lookup doen om dat te begrijpen. Dit is een nog lelijker dan gemiddelde C++ constructie. En ik dacht dat ik de ergste template abuse dingen wel had gezien :)

Om een leesbaar alternatief te geven, gezien je schijnbaar maar een gelimiteerd aantal id's gaat maken (gezien je if/else constructie), waar .oisyn ook al op hintte, template specialization:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//het ID nummer van een onbekend type is 0
template<class T> struct id_of { enum { value = 0 }; };

//het ID nummer van een bekend type, bv otherclass
class otherclass
{
public:
  const int id = 42;
};

//deze template specialization voor otherclass pakt de id uit otherclass.
//je kan ook een vast nummer teruggeven mocht je dat praktischer vinden
template<> struct id_of<otherclass> { enum { value = otherclass::id }; };

//je kan het id van een class opvragen als volgt
int id_of_int = id_of<int>::value; //0
int id_of_otherclass = id_of<otherclass>::value; //42

[ Voor 0% gewijzigd door MLM op 09-04-2009 13:40 . Reden: een ; teveel ]

-niks-


Acties:
  • 0 Henk 'm!

  • Darkvater
  • Registratie: Januari 2001
  • Laatst online: 26-08-2024

Darkvater

oh really?

Topicstarter
Dank je wel voor de suggesties .oisyn en MLM. Helaas, voor beiden geldt - was mijn fout in mijn OP - is dat id geen static/const variabele is, beide voorbeelden werken dus helaas niet.

Ik krijg bij .oisyn een foutmelding over "illegal sizeof operand" voor sizeof(test((T*)0)) (eigenlijk type<sizeof(U::id)> omdat je geen sizeof kan gebruiken van een object expliciet object) ook nog eens... misschien is VS2008 niet zo vriendelijk :).

Het probleem met MLM's code is, is dat ik voor alle classes die een id member hebben ik een template specialisatie moet maken.

Als .oisyn's sizeof() magic werkt, dan heb ik hem wel werkend denk ik :)
C++:
1
2
template <class T, bool B = has_id<T>::value> struct get_id {static size_t id(T* val) {return val->id;}};
template <class T> struct get_id<T, false> {static size_t id(...) {return 0;}};

[ Voor 43% gewijzigd door Darkvater op 09-04-2009 14:26 . Reden: clarify #2 ]


Windows Vista? *NEVER* Het waarom - Opera forever!!!
I've seen chickens that were more menacing. Chickens in a coma. On ice. In my fridge


Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Darkvater schreef op donderdag 09 april 2009 @ 13:50:
Dank je wel voor de suggesties .oisyn en MLM. Helaas, voor beiden geldt - was mijn fout in mijn OP - is dat id geen static/const variabele is
Als je er nou meteen bij zegt wat het dan wel is :)

Overigens, als je die U::id op regel 9 verandert in een &U::id dan werkt het ook met non-static members. Verder, als het idd een non-static member is, moet je in get_id() dan gewoon een functie definieren die de id teruggeeft gegeven aan de hand van een instance pointer. In de specialized versie van get_id returnt die functie dan gewoon 0.

En m'n code werkt prima onder VS 2008 ;)
MLM schreef op donderdag 09 april 2009 @ 13:38:
Ik moest dat 3x lezen en een wikipedia lookup doen om dat te begrijpen. Dit is een nog lelijker dan gemiddelde C++ constructie. En ik dacht dat ik de ergste template abuse dingen wel had gezien :)
Dan heb je niet echt veel gezien ;). Veel type traits functionaliteit, zoals std::tr1::is_base_of, is op een vergelijkbare manier geïmplementeerd:
C++:
1
2
3
4
5
6
7
8
9
10
11
template<class Base, class Derived> 
struct is_base_of
{ 
    typedef char small; 
    struct big { char c[10]; }; 

    static small test(Base *); 
    static big test(...); 

    enum { value = (sizeof(test((Derived*)0)) == sizeof(small)) }; 
};

is_base_of<A,B>::value is true op het moment dat A een base is van B, of specifieker, als een B* impliciet naar een A* gecast kan worden.

Het enige wat die has_id struct er nog bij combineert is de SFINAE regel zodat de test() functie die een small returnt niet eens "bestaat" als het type niet het juiste member heeft :)

[ Voor 44% gewijzigd door .oisyn op 09-04-2009 14:37 ]

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!

  • Darkvater
  • Registratie: Januari 2001
  • Laatst online: 26-08-2024

Darkvater

oh really?

Topicstarter
.oisyn schreef op donderdag 09 april 2009 @ 14:19:
Overigens, als je die U::id op regel 9 verandert in een &U::id dan werkt het ook met non-static members.
* Darkvater loves .oisyn :). Met die ampersand ervoor werkt het wel; en om mijn ego te boosten had ik id voor non-static net voor jou nog uitgevogeld ;)


Windows Vista? *NEVER* Het waarom - Opera forever!!!
I've seen chickens that were more menacing. Chickens in a coma. On ice. In my fridge


Acties:
  • 0 Henk 'm!

  • Darkvater
  • Registratie: Januari 2001
  • Laatst online: 26-08-2024

Darkvater

oh really?

Topicstarter
eh...I take it back, het werkend gedeelte; had een typo waardoor het wel compileerde, maar kreeg altijd false terug.

Ik heb nu dus:
C++:
1
2
3
4
template <class U> static small test(U*, type<sizeof(&U::id)>* = 0);
static big test(...);

enum {value = sizeof(test((T*)0)) == sizeof(small)};

en compiler error error C2070: '': illegal sizeof operand


Windows Vista? *NEVER* Het waarom - Opera forever!!!
I've seen chickens that were more menacing. Chickens in a coma. On ice. In my fridge


Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Hmmja idd, ik zie het nu ook. Dit lijkt een VC++ specifieke issue. Het volgende werkt wel:

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
#include <iostream>

template<class T>
struct has_id
{
    template<size_t N> struct type { };
    template<int T::*ptr> struct type2 { };
    typedef char small;
    struct big { char c[10]; };
    //template<class U> static small test(U*, type<sizeof(&U::id)>* = 0);
    template<class U> static small test(U*, type2<&U::id>* = 0);
    static big test(...);

    enum { value = (sizeof(test((T*)0)) == sizeof(small)) };
};

template<class T, bool B = has_id<T>::value>
struct get_id
{
    static int get(T & t) { return t.id; }
};

template<class T>
struct get_id<T, false>
{
    static int get(T & t) { return 0; }
};

struct A { };
struct B { int id; };

int main()
{
    A a;
    B b;
    b.id = 34;

    std::cout << get_id<A>::get(a) << std::endl;
    std::cout << get_id<B>::get(b) << std::endl;
}

Met het nadeel dat je id altijd van een vast type moet zijn (in dit geval int). Het nadeel is vooral dat als je id toevallig geen int is, je ook geen compile error krijgt maar de code simpelweg doet alsof het 'id' member niet bestaat

Overigens, met bovenstaande constructie zou ik get_id sowieso renamen naar iets anders (bijv. get_id_helper), en dan een templatized get_id maken die get_id_helper<T>::get() aanroept. Dan kun je gewoon direct get_id(a) en get_id(b) gebruiken :)

[ Voor 5% gewijzigd door .oisyn op 09-04-2009 15:16 ]

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!

  • Darkvater
  • Registratie: Januari 2001
  • Laatst online: 26-08-2024

Darkvater

oh really?

Topicstarter
Dank je .oisyn, dit werkt inderdaad wel. Ik heb echter ergens een type std::vector<bool>::value_type volgens de compiler en dat vindt ie niet zo goed :s. Integral types are a no-go? Zou niet eens mogen, ik ga eerst uitvogelen waarom ik hier code voor krijg uberhaupt, ik heb namelijk geen types van dit type, alleen maar structs.

C++:
1
2
3
4
5
6
7
typedef std::vector<bool>::value_type C;
C c;
std::cout << get_id<C>::get(c) << std::endl;

"Error  1   error C2825: 'T': must be a class or namespace when followed by '::'"
"Error  2   error C2645: no qualified name for pointer to member (found ':: *')"
"Error  3   error C2143: syntax error : missing ',' before '`global namespace''"

[ Voor 4% gewijzigd door Darkvater op 09-04-2009 15:33 ]


Windows Vista? *NEVER* Het waarom - Opera forever!!!
I've seen chickens that were more menacing. Chickens in a coma. On ice. In my fridge


Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Een vector<bool> is geen vector<bool>. Dat stomme ding is specialized om de bools in 1 bit-per-entry op te slaan. Je kunt dan beter een vector<char> gebruiken oid.

Overigens zou dat wel moeten werken, want std::vector<bool>::value_type is gewoon bool.

.edit: of is je code iets anders dan wat er nu staat? Want als het eigenlijk een std::vector<T> is, en je zit in een template functie waar T een template parameter is, dan is std::vector<T>::value_type een dependent-type, en dus weet de compiler tijdens het parsen niet of het een type of een member is. En dan assumet ie maar dat het een member is. Als je het als type wilt parsen moet je er typename voor zetten, dus: typename std::vector<T>::value_type.

[ Voor 68% gewijzigd door .oisyn op 09-04-2009 15:39 ]

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!

  • Darkvater
  • Registratie: Januari 2001
  • Laatst online: 26-08-2024

Darkvater

oh really?

Topicstarter
Nee, die vector is zeker een fout...maar dat moet ik eerst uitvogelen. Zelfs een heel simpel voorbeeld doet het al niet. In jouw code als ik bijvoorbeeld het volgende B-struct heb
C++:
1
struct B { unsigned int id; };

dan krijg ik altijd al 0 als antwoord.. hoe kan has_id<B>::value in dit geval 'false' retourneren? Het lijkt of alles behalve 'int' niet werkt :?

[edit]laat maar, die int T::*ptr moet natuurlijk ook 'unsigned int' worden... dom[/b]

[ Voor 17% gewijzigd door Darkvater op 09-04-2009 16:07 . Reden: bleh ]


Windows Vista? *NEVER* Het waarom - Opera forever!!!
I've seen chickens that were more menacing. Chickens in a coma. On ice. In my fridge


Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

.oisyn schreef op donderdag 09 april 2009 @ 15:14:
Met het nadeel dat je id altijd van een vast type moet zijn (in dit geval int). Het nadeel is vooral dat als je id toevallig geen int is, je ook geen compile error krijgt maar de code simpelweg doet alsof het 'id' member niet bestaat
;)

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!

  • Darkvater
  • Registratie: Januari 2001
  • Laatst online: 26-08-2024

Darkvater

oh really?

Topicstarter
Ja, lezen is ook een kunst :P. die template<int T::*ptr> struct type {}; was zoveel magic dat ik dat gemist heb. Waar komt die ::*ptr trouwens vandaan?

Mijn laatste vraag hierover, voor nu. Met structs werkt het, maar hoe moet het in het geval van has_id<int>::valueb? Ik heb overal al (denk ik) typename voor proberen te zetten, maar niks werkt.


Windows Vista? *NEVER* Het waarom - Opera forever!!!
I've seen chickens that were more menacing. Chickens in a coma. On ice. In my fridge


Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ah ja, good one. std::tr1::is_union en std::tr1::is_class (of de boost:: equivalenten) gaan je hierbij helpen. Net als met die get_id class, moet je has_id ook een extra boolean geven gebaseerd op is_union<T>::value || is_class<T>::value. In de specialization voor has_id<T,false> is 'value' dan altijd false.

[ Voor 0% gewijzigd door .oisyn op 10-04-2009 16:10 . Reden: die && moest natuurlijk een || zijn. ]

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!

  • xos
  • Registratie: Januari 2002
  • Laatst online: 12-09 12:41

xos

.oisyn schreef op donderdag 09 april 2009 @ 16:52:
Ah ja, good one. std::tr1::is_union en std::tr1::is_class (of de boost:: equivalenten) gaan je hierbij helpen. Net als met die get_id class, moet je has_id ook een extra boolean geven gebaseerd op is_union<T>::value && is_class<T>::value. In de specialization voor has_id<T,false> is 'value' dan altijd false.
Je compiler moet "is_union" wel ondersteunen. Niet alle compilers doen dat, mochten ze het niet onderteunen dan zal is_union altijd false terug geven en is_class true.

Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

En dus hoeft ie het niet te ondersteunen voor dit doeleinde :). Je bent namelijk niet geïnteresseerd in het verschil tussen de twee, maar in de combinatie van de twee. Het maakt daarbij niet uit dat een class foutief wordt gerapporteerd als union of andersom. Als het een van de twee is, dan kan het members hebben en werkt de pointer-to-member syntax.

Overigens had ik in mijn vorige post idd wel foutief een && staan, dat moest natuurlijk een || zijn.

[ Voor 87% gewijzigd door .oisyn op 10-04-2009 16:18 ]

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!

  • Darkvater
  • Registratie: Januari 2001
  • Laatst online: 26-08-2024

Darkvater

oh really?

Topicstarter
Dank je .oisyn, nu werkt het wel zoals het zou moeten :)

Ik moet me nog wat meer in templates verdiepen denk ik...heavy stuff


Windows Vista? *NEVER* Het waarom - Opera forever!!!
I've seen chickens that were more menacing. Chickens in a coma. On ice. In my fridge


Acties:
  • 0 Henk 'm!

  • Darkvater
  • Registratie: Januari 2001
  • Laatst online: 26-08-2024

Darkvater

oh really?

Topicstarter
aaaargh... nog even terugkomend op mijn probleem. Voor een ongerelateerde probleem was ik door de help van VS aan het speuren...en wat zie ik... __if_exists().

C++:
1
2
3
4
5
6
7
8
9
template <class T> struct get_id {
  static int get(T& val) {
    int ret = 0;
    __if_exists(T::id) {
      ret = val.id;
    }
    return ret;
  }
};


Ja, en dat was het, klaar :) (of zelfs met __if_not_exists() voor de error waarde). In ieder geval nog wel erg bedankt voor de template versie, was nogal leerzaam, vooral het SFINAE gedeelte :)


Windows Vista? *NEVER* Het waarom - Opera forever!!!
I've seen chickens that were more menacing. Chickens in a coma. On ice. In my fridge


Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Nice, die kende ik nog niet :)

Maar pfff, nou en, mijn code is portable :Y)

[ Voor 44% gewijzigd door .oisyn op 15-04-2009 14:13 ]

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!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Lijkt me lastig om op andere systemen __if_exists( arg ) als macro te definen.

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

Pagina: 1