[C++] Type informatie 'opslaan' in lijst, mogelijk?

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Ik zit nu al een hele tijd mijn hoofd te breken of het volgende mogelijk is, ik heb het idee van wel, maar ik kan dit niet hardmaken.

code:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Base { };
template< class T >
struct Derived : public Base()
{
    T value;
};

std::vector< Base* > base;

base.push_back( new Derived< int >() );
base.push_back( new Derived< float >() );

for ( std::vector< Base* >::iterator i = base.begin(); i != base.end(); ++i )
{
    // code
}

In de for loop zou ik willen checken of de waarde uit de lijst te converteren valt naar een willekeurig andere Derived klasse. ie. Derived< int >::value = Derived< float >::value; zou prima mogelijk moeten zijn.

Mijn vraag is simpel; Is dit überhaupt mogelijk?

en zo ja, graag een hint in de goede richting ;)

oprecht vertrouwen wordt nooit geschaad


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Mijn antwoord is ook simpel: nee dat kan niet.

Iets uitgebreider antwoord: hiervoor heb je iets als reflection nodig. Het zou wel kunnen als je handmatig een lijstje bouwt van alle mogelijke conversies.

[ Voor 63% gewijzigd door .oisyn op 14-10-2011 18:51 ]

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!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Kun je iets met
C++:
1
2
3
4
5
6
7
8
9
10
11
12
template <class T>
struct Derived
{
  T value;
  template <class U>
  Derived<U> Convert() const
  {
     Derived<U> u;
     u.value = value;
     return u;
  }
}


Alle impliciete conversies van T naar U zijn hiermaar beschikbaar.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
.oisyn schreef op vrijdag 14 oktober 2011 @ 18:42:
Mijn antwoord is ook simpel: nee dat kan niet.

Iets uitgebreider antwoord: hiervoor heb je iets als reflection nodig. Het zou wel kunnen als je handmatig een lijstje bouwt van alle mogelijke conversies.
Hmm, dat valt tegen :) Wel weer een nieuw keyword om google te voeren ;)
H!GHGuY schreef op vrijdag 14 oktober 2011 @ 20:02:
Kun je iets met
C++:
1
2
3
4
5
6
7
8
9
10
11
12
template <class T>
struct Derived
{
  T value;
  template <class U>
  Derived<U> Convert() const
  {
     Derived<U> u;
     u.value = value;
     return u;
  }
}


Alle impliciete conversies van T naar U zijn hiermaar beschikbaar.
Dat werkt niet omdat je in de loop geen Derived class maar een Base class beschikbaar hebt.
Als er zoiets was als virtual template members dan kon het.

oprecht vertrouwen wordt nooit geschaad


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Als er zoiets was als virtual template members dan kon het.
Wat natuurlijk niet kan op de manier zoals templates werken in C++. Er wordt per type een functie geïnstantiatieerd en gecompiled. Je zou dan van tevoren voor elk type de template moeten instantieren, maar er zijn oneindig veel types mogelijk. Eventueel zou je nog kunnen redeneren dat je alleen die Derived::Convert<T> hoeft te instantieren waar er een Base::Convert<T> call voor bestaat in het programma, maar helaas zit het compileermodel van C++ zo niet in elkaar.

Zoiets zou wel kunnen:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <iostream>
#include <vector>
#include <utility>
#include <map>
#include <functional>
#include <map>
#include <typeinfo>

class converter;

// utility to store std::type_info by value
class type_info_ref
{
public:
    type_info_ref(const std::type_info & i) : pInfo(&i) { }
    bool operator<(type_info_ref other) const { return pInfo->before(*other.pInfo); }

private:
    const std::type_info * pInfo;
};

typedef std::pair<type_info_ref, type_info_ref> type_pair;
template<class From, class To> type_pair make_type_pair()
{
    return std::make_pair(std::cref(typeid(From)), std::cref(typeid(To)));
}

typedef std::map<type_pair, converter*> converter_map;
static converter_map & get_converter_map()
{
    static converter_map m;
    return m;
}

// base conversion interface
class converter
{
public:
    virtual void * convert(const void * ptr) const = 0;
};

// conversion implementation for specific types
template<class From, class To> class converterimpl : converter
{
public:
    converterimpl()
    {
        get_converter_map()[make_type_pair<From, To>()] = this;
    }

    virtual void * convert(const void * ptr) const
    {
        return new To(*static_cast<const From*>(ptr));
    }
};

template<class To> To * convert(const void * data, const std::type_info& from)
{
    if (from == typeid(To))
        return new To(*static_cast<const To*>(data));

    auto it = get_converter_map().find(type_pair(from, std::cref(typeid(To))));
    if (it == get_converter_map().end())
        return 0;
    return static_cast<To*>(it->second->convert(data));
}

template<class To, class From> To * convert(const void * data)
{
    return convert<To>(data, typeid(From));
}

// some default instantiations
converterimpl<int, float> int_to_float;
converterimpl<float, int> float_to_int;

// your code
struct Base
{
    virtual const std::type_info& get_type_info() const = 0;
    virtual const void * get_data() const = 0;

    template<class T> T * convert() { return ::convert<T>(get_data(), get_type_info()); }
};

template<class T> struct Derived : Base
{
    T value;
    Derived(T v) : value(v) { }
    virtual const std::type_info& get_type_info() const { return typeid(T); }
    virtual const void * get_data() const { return &value; }
};

int main()
{
    std::vector<Base*> v;
    v.push_back(new Derived<int>(1));
    v.push_back(new Derived<float>(2.4f));
    v.push_back(new Derived<const char *>("hoi"));

    for (auto it = v.begin(); it != v.end(); ++it)
    {
        Base* b = *it;
        int * p = b->convert<int>();
        if (p)
        {
            std::cout << *p << std::endl;
            delete p;
        }
    }    
}

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!

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Hey, dat is cool!

De reden dat ik deze vraag had, kwam door een eerder topic van mij ( \[C++] Signals/Slots, adres van argument nemen in template )

Ik was nog eens een poging aan het doen om tot zinnige conversies te komen, maar met deze informatie moet ik volgens mij wel een werkend systeem kunnen opzetten dat werkt binnen cxx03.

Als ik mij niet vergis moet het geen probleem zijn om inplaats van de type_info reference die jij als key gebruikt de pointer waarde van typeid( ... ).name() te gebruiken, klopt dat?

Het lastige van deze oplossing is het handmatig moeten opgeven van conversie mogelijkheden, maar volgens mij heb ik daar ook een idee voor...

Volgens mij moet het mogelijk zijn om met deze methode ook op te vragen of type x converteerbaar is naar y. Mbv. is_convertible moet het dan toch mogelijk zijn om alle tot dan toe bekende types af te gaan en waar mogelijk de converter map uitbreiden..

/edit

dat zou de converter map sowieso overbodig maken..

[ Voor 18% gewijzigd door Arjan op 15-10-2011 23:51 ]

oprecht vertrouwen wordt nooit geschaad


Acties:
  • 0 Henk 'm!

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

MLM

aka Zolo

De pointer-waarde van typeid(T).name() is niet gegarandeerd constant.
Bijvoorbeeld in MSVC word voor elk type een type_info struct gegenereerd in een binary. Echter, als je een DLL en een EXE hebt, hebben beide een loss instantie van een type_info, en je kan dus NIET &typeid() of typeid().name() gebruiken, omdat die in elke binary anders zullen zijn. Mocht dat kunnen gebeuren in jouw geval, kan je beter de hele string van name() gebruiken omdat je anders dubbele keys gaat krijgen. Daarnaast zijn er helemaal 0 garanties voor type_info's uitwisselbaarheid tussen binaries gegenereerd door verschillende (versies) van compilers.

En dan heb ik het nog niet eens over de mogelijkheid om types met dezelfde namen in verschillende translation units te hebben, en hoe je daar onderscheid in wilt gaan maken.

C++ is gewoon niet goed geschikt om te doen wat jij wilt, gebruik dan liever C# :)
Het is misschien wel mogelijk om iets inelkaar te hacken wat werkt in sommige gevallen, maar ik garandeer dat er vast wel een manier is om het te slopen :)

[ Voor 24% gewijzigd door MLM op 16-10-2011 13:25 ]

-niks-


Acties:
  • 0 Henk 'm!

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Hoewel het bij C++ sowieso maar de vraag is of een volgende versie van een compiler binair compatible is met de oude, snap ik het ( valide ) punt dat je maakt.

Daarnaast geloof ik ook dat mijn eerdere eureka moment in de kiem gesmoord is.
Om een conversie te kunnen doen, zonder daarbij een conversie_map te gebruiken, heb je compile-time twee types nodig. Voor zover ik kan inschatten lukt je dit niet zonder dat je de eerder genoemde virtual template beschikbaar hebt.
Als je namelijk een type en een pointer naar een type beschikbaar hebt, dan kun je weliswaar dmv. .oisyn's methode in een functie terechtkomen waarbij een van de twee typen beschikbaar is, maar niet beide :(

oprecht vertrouwen wordt nooit geschaad


Acties:
  • 0 Henk 'm!

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

MLM

aka Zolo

mja, ik vind type_info/typeid sowieso een halfzachte feature, het is er ooit ingestopt om reflectie mogelijk te maken, maar dat is niet echt praktisch haalbaar, aangezien de enige runtime informatie die je kan krijgen uit "name()" komt, en dat is al tricky. daarnaast is de enigzins intressante type-informatie (die jij wilt hebben :P) zoals type-conversion wel ergens verborgen, maar niet te bereiken met een API (dat is bijvoorbeeld hoe C++ exception handlers kunnen vinden of een type matcht)

-niks-


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
type_info bestaat om te sorteren; .name() is alleen maar een debug hulpje.

Iets meer on-topic: het fundamentele probleem is dat je een NxN matrix van mogelijke conversies hebt. Die matrix is niet af te leiden uit N individuele types.

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: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

MLM schreef op zondag 16 oktober 2011 @ 15:27:
daarnaast is de enigzins intressante type-informatie (die jij wilt hebben :P) zoals type-conversion wel ergens verborgen, maar niet te bereiken met een API (dat is bijvoorbeeld hoe C++ exception handlers kunnen vinden of een type matcht)
Louter wat de class hierarchy is. Een gethrowde float kun je niet catchen met een int bijvoorbeeld. Diezelfde class hierarchy informatie is trouwens ook van belang voor dynamic_cast, al gaat dat dan weer alleen op voor classes met virtual functions.

[ Voor 14% gewijzigd door .oisyn op 17-10-2011 13:01 ]

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!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Zou je misschien iets zijn met de boost::lexical_cast, of iets erop geinspireerd?
Het is een serieuze omweg, maar voor sommige toepassingen kan het voldoende zijn.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Kun je iets specifieker zijn? Ik zie niet helemaal in hoe een techniek als lexical_cast hierbij kan helpen eigenlijk...

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!

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Je zou een 'proxy' type kunnen gebruiken, als een type een 'toString' of een conversie naar een std::string methode ondersteunt, voor int/float zou dat uitkomst kunnen bieden, maar voor 'abstracte' data helpt het natuurlijk niet.

oprecht vertrouwen wordt nooit geschaad

Pagina: 1