[C++] Class wrapper zonder type

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
Ik ben in de boost::random library gedoken en vind het erg mooi spul. Nu wil ik voor mijzelf altijd de Mersenne twister (mt19937) gebruiken (dezelfde die MATLAB gebruikt) en dus wil ik een extra wrapper om boost::variate_generator heen leggen.

Hier een begin, ik ben er nog niet mee klaar, maar ik ben er al wel mee ontevreden.

Ik zou graag van het type-specifying gedeelte in mijn instantiation afwillen (sorry voor waarschijnlijk verkeerd woordgebruik) omdat het dubbelop is. Is mogelijk dit op de een of andere manier te wrappen ofzo? Ik heb heel wat geprobeerd, maar heb niets legaals kunnen vinden.

Zie code:

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
// includes
#include <boost/random.hpp>
#include <time.h>

template<class Dist>
class CRandom
{
public:
    explicit CRandom(Dist d);
    ~CRandom(void) {};
    typedef typename Dist::result_type result_type;

    result_type operator()() { return Gen(); }
    template<class T>
    result_type operator()(T value) { return Gen(value); }

    
private:
    boost::mt19937 Eng;
    boost::variate_generator<boost::mt19937,Dist> Gen;
};

template<class Dist>
CRandom<Dist>::CRandom(Dist d)
    :
    Eng(unsigned int(time(NULL))),
    Gen(Eng,d)
{
}


//main 
int main(int argc, char* argv[])
{
    // ipv onderstaande
    CRandom<boost::uniform_int<>> Cr(boost::uniform_int<>(-2, 4));
    // zou ik graag dit willen kunnen doen, twee keer boost::uniform_int<> is dubbelop
    CRandom Cr(boost::uniform_int<>(-2, 4));
    // of misschien wel:
    CRandom<boost::uniform_int<>> Cr(-2, 4); // ofzo

    int a = Cr();

    // exit
    return 1;
}

Acties:
  • 0 Henk 'm!

  • Dricus
  • Registratie: Februari 2002
  • Laatst online: 08:16

Dricus

ils sont fous, ces tweakers

Ik zou een extra typedef maken waarmee je het type expliciet specificeert. Zoiets dus:
C++:
1
typedef CRandom<boost::uniform_int<>> CRandomInt;


Het instantiëren gaat dan als volgt:
C++:
1
CRandomInt Cr(boost::uniform_int<>(-2, 4));


Edit:
Wellicht is het nog net iets 'meer OO' om een afgeleide class te definiëren dan om een typedef te maken. Het effect is hetzelfde, maar dit is denk ik een iets meer puristische oplossing. Je zou dan ook een nog sterker verkorte instantiëring kunnen krijgen door hier een extra constructor te introduceren:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Afgeleide class
class CRandomInt : public CRandom<boost::uniform_int<>>
{
public:
    // Ik weet niet zeker of de onderstaande declaratie syntactisch helemaal goed is,
    // maar het idee is duidelijk denk ik.
    CRandomInt(int x, int y)
    : CRandom<boost::uniform_int<>>(boost::uniform_int<>(x, y))
    {
    }
};

void foo()
{
    // Instantiëring
    CRandomInt Cr(-2, 4);
}

[ Voor 59% gewijzigd door Dricus op 22-07-2009 09:20 ]

Stel niet uit tot morgen wat je vandaag nog tot morgen kunt uitstellen...


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je kunt evt. natuurlijk ook forwarding constructors definieren:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<class Dist> 
class CRandom 
{ 
public: 
    explicit CRandom(Dist d);

    template<class P1> explicit CRandom(const P1 & p1) : Eng(/*...*/), Gen(Eng, Dist(p1)) { }
    template<class P1, class P2> CRandom(const P1 & p1, const P2 & p2) : Eng(/*...*/), Gen(Eng, Dist(p1, p2)) { }
    template<class P1, class P2, class P3> CRandom(const P1 & p1, const P2 & p2, const P3 & p3) : Eng(/*...*/), Gen(Eng, Dist(p1, p2, p3)) { }

    // ...
};

int main()
{
    CRandom<boost::uniform_int<>> Cr(-2, 4);
}

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
Hoi Dricus en .Oisyn,

Bedankt voor jullie suggesties. Beide werken goed, maar .Oisyn's werkt zonder dat ik ene hele hoop typedef's hoef te doen of afgeleide klassen hoef te doen. Ik ga in dit geval dus voor .Oisyn's oplossing, maar heb van beide geleerd.

Onderaan mijn uiteindelijke class. Gezien de boost::random distributions zo getemplated zijn dat alle instantieringsparameters van hetzelfde type zijn (zie http://www.boost.org/doc/...random-distributions.html), heb ik jouw code iets simpeler gemaakt. Ook hebben sommige distributies defaults die je misschien wilt gebruiken, dus heb ik een constructor zonder input parameters gemaakt.

Dit geheel brengt nog een vraag met zich mee, hoe formatten jullie de sorce for zulk een templated constructor met initialization list? Wat ik nu gedaan hebt lijkt wel duidelijk, maar ik hoor graag van ervaring hoe dit overzichtelijk te 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#pragma once

// includes
#include <boost/random.hpp>
#include <time.h>

// declaration
template<class Dist>
class CRandom
{
public:
    /* use for Dist:
      uniform_int<type=int>(min,max)        - integral types
      uniform_real<type=double>(min,max)    - floating point types
      for other distributions, see: http://www.boost.org/doc/libs/1_39_0/libs/random/random-distributions.html

      CRandom<Dist> Cr([0-3] params, depending on distribution)
      */
    
    // forwarding constructors
    explicit CRandom()
        : Eng(unsigned int(time(NULL))),
          Gen(Eng, Dist())
    { } 
    template<class P>
    explicit CRandom(const P & p1)
        : Eng(unsigned int(time(NULL))),
          Gen(Eng, Dist(p1))
    { } 
    template<class P>
    explicit CRandom(const P & p1, const P & p2)
        : Eng(unsigned int(time(NULL))),
          Gen(Eng, Dist(p1, p2))
    { } 
    template<class P>
    explicit CRandom(const P & p1, const P & p2, const P & p3)
        : Eng(unsigned int(time(NULL))),
          Gen(Eng, Dist(p1, p2, p3))
    { } 

    ~CRandom(void) {};

    typedef typename Dist::result_type result_type;

    result_type operator()() { return Gen(); }
    template<class T>
    result_type operator()(T value) { return Gen(value); }

private:
    boost::mt19937 Eng;
    boost::variate_generator<boost::mt19937,Dist> Gen;
};
// end declaration

Acties:
  • 0 Henk 'm!

  • Dricus
  • Registratie: Februari 2002
  • Laatst online: 08:16

Dricus

ils sont fous, ces tweakers

De hoeveelheid typedefs en/of afgeleide classes van mijn oplossing (1 typedef of afgeleide class) valt toch wel mee? Bedenk wel dat de code van het instantiëren van de classes daar weer simpeler van wordt, je bent in het geval van de afgeleide class helemaal af van de type-specificering in je instantiation.

Daarnaast: mocht je ooit bijvoorbeeld in plaats van uniform_int een andere class willen gaan gebruiken dan hoef je dat in mijn oplossing maar op één plek te wijzigen, namelijk de afgeleide class. In .oisyn's oplossing moet je alle code aanpassen waar de class geïnstantieerd wordt.

Om die twee redenen zou ik eerder voor mijn oplossing dan voor die van .oisyn gaan. Maargoed, in hoeverre dat voor jouw project relevante argumenten zijn kan ik niet beoordelen. Mijn oplossing is niet per definitie beter dan die van .oisyn. Ik houd er alleen erg van om abstractions zo min mogelijk leaky te laten zijn. Als ik in een functie een random integer tussen de -2 en 4 nodig heb, dan is het vanuit die functie gezien meestal niet zo interessant dat daar uniform_int gebruikt wordt.

Stel niet uit tot morgen wat je vandaag nog tot morgen kunt uitstellen...


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Je kan ook nog zoiets doen (even uit het hoofd):

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename T>
struct DistSelect {
};
template <>
struct DistSelect<int> {
   typedef boost::uniform_int<> type;
};
template <>
struct DistSelect<double> {
   typedef boost::uniform_real<> type;
};

template<typename P> 
explicit CRandom(const P & p1, const P & p2) 
    : Eng(unsigned int(time(NULL))), 
      Gen(Eng, typename DistSelect<P>::type(p1, p2)) 
{ } 

CRandom Ci(-2,-4);
CRandom Cr(-2.0f,-4.0f);

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dricus schreef op donderdag 23 juli 2009 @ 11:07:
Daarnaast: mocht je ooit bijvoorbeeld in plaats van uniform_int een andere class willen gaan gebruiken dan hoef je dat in mijn oplossing maar op één plek te wijzigen, namelijk de afgeleide class. In .oisyn's oplossing moet je alle code aanpassen waar de class geïnstantieerd wordt.
Da's natuurlijk ook onzin, doorgaans gebruik je typedefs voor je templates, zodat je het maar op 1 plek hoeft aan te passen. In dit geval dus een typedef voor CRandom<boost::uniform_int<>>
(jouw eerste suggestie). Het enige wat mijn methode toevoegt is dat je niet die hele boost::uniform_int<> hoeft te specificeren bij de instantiatie.

Een afgeleide class zou ik sowieso niet doen. Dat impliceert namelijk dat een instantie van die afgeleide niet meer uitwisselbaar is met het origineel.
Zoijar schreef op donderdag 23 juli 2009 @ 11:19:
Je kan ook nog zoiets doen (even uit het hoofd):

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename T>
struct DistSelect {
};
template <>
struct DistSelect<int> {
   typedef boost::uniform_int<> type;
};
template <>
struct DistSelect<double> {
   typedef boost::uniform_real<> type;
};

template<typename P> 
explicit CRandom(const P & p1, const P & p2) 
    : Eng(unsigned int(time(NULL))), 
      Gen(Eng, typename DistSelect<P>::type(p1, p2)) 
{ } 

CRandom Ci(-2,-4);
CRandom Cr(-2.0f,-4.0f);
Nee dat kan niet, de template argumenten van CRandom moet je nog steeds expliciet specificeren.

[ Voor 29% gewijzigd door .oisyn op 23-07-2009 11:25 ]

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
Hoi Dricus,

Bedankt voor je verdere discussie. Over het algemeen ben ik het met je eens en houd ik ervan om hoe iets gebeurd zoveel mogelijk in de class te verstoppen zodat je daar in gebruik niet over hoeft te denken. Ik wil deze klasse echter met een hele reeks verschillende distributies gebruiken (en kan niet uitsluiten dat ik zelf andere distributie-omvormers schrijf, bijvoorbeeld eentje voor uniform sampling in een frustum).

Wanneer ik in mijn code een bepaalde distributie gebruik en dus een klasse met die distributie instantieer, zal dat niet veranderen. Mocht ik nu besluiten een bepaalde distributie te gebruiken die ik nog niet eerder heb gebruikt, dan moet ik als ik typedefs gebruik de code van mijn class (nouja, gerelateerd aan mijn class) aanpassen. Met de oplossing die ik nu heb gekozen hoeft dat niet.

Niet een erg sterk argument, en erg afhankelijk van persoonlijke voorkeur, maar ik dit geval ga ik ervoor om de distributie in de class instantiering te stoppen zodat het voor mij duidelijk en flexibel is.

Ik merk weer eens dat C++ zoveel hout bevat dat ik door de vele mogelijkheden niet altijd de juiste kan vinden danwel dat je de usagecase goed duidelijk moet hebben voordat je kan bepalen welke de juiste is. Bedankt voor je input, die typedefs had ik zelf nooit aan gedacht en zullen voor andere gevallen zeker handig zijn!

Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

.oisyn schreef op donderdag 23 juli 2009 @ 11:24:
Nee dat kan niet, de template argumenten van CRandom moet je nog steeds expliciet specificeren.
Crandom is geen template class meer dan. Waar wordt het type nog meer gebruikt dan? oh in de geenrator member en indirect het result type. Ok, laat maar dan :)

[ Voor 12% gewijzigd door Zoijar op 23-07-2009 11:31 ]


Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
O! Dat is wat je bedoelde .Oisyn.. die twee oplossingen combineren, zover had ik nooit gedacht. Dat is natuurlijk helemaal mooi en dat zal ik dus gebruiken, zeker voor die paar standaard uses zoals uniform_int en uniform_real.

@Zoijar, dat is geen oplossing voor mij, omdat er meerdere distributies zijn met dezelfde input of output typen, e.g., uniform_real en normal_distribution nemen beiden twee doubles/floats.

[ Voor 29% gewijzigd door TheWickedD op 23-07-2009 11:36 ]


Acties:
  • 0 Henk 'm!

  • Dricus
  • Registratie: Februari 2002
  • Laatst online: 08:16

Dricus

ils sont fous, ces tweakers

.oisyn schreef op donderdag 23 juli 2009 @ 11:24:
Da's natuurlijk ook onzin, doorgaans gebruik je typedefs voor je templates, zodat je het maar op 1 plek hoeft aan te passen. In dit geval dus een typedef voor CRandom<boost::uniform_int<>>
(jouw eerste suggestie). Het enige wat mijn methode toevoegt is dat je niet die hele boost::uniform_int<> hoeft te specificeren bij de instantiatie.
Ik reageer op een voorbeeld waar die typedefs niet gebruikt worden. Vanuit dat perspectief is mijn opmerking geen onzin lijkt me.
Een afgeleide class zou ik sowieso niet doen. Dat impliceert namelijk dat een instantie van die afgeleide niet meer uitwisselbaar is met het origineel.
Hoe bedoel je dat precies?

Stel niet uit tot morgen wat je vandaag nog tot morgen kunt uitstellen...


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dricus schreef op donderdag 23 juli 2009 @ 11:39:
[...]

Ik reageer op een voorbeeld waar die typedefs niet gebruikt worden. Vanuit dat perspectief is mijn opmerking geen onzin lijkt me.
Je reageert alsof mijn oplossing typedefs uitsluiten (je hebt het immers over mijn oplossing en jouw oplossing en dat daaruit gekozen moet worden - je praat niet over een combinatie van beide). Dat is weldegelijk onzin :). Ik begon mijn eerste reactie dan ook met: "Je kunt evt. natuurlijk ook forwarding constructors definieren". Het gebruik van typedefs zou ik nooit af willen raden :)
Hoe bedoel je dat precies?
Dat je een CRandom<boost::uniform_int<>> instantie bijv. niet aan een CRandomInt kunt assignen. En dat andere template code die een CRandom<T> gebruikt waarbij T = boost::uniform_int<> niet automatisch een CRandomInt instantieert.

[ Voor 20% gewijzigd door .oisyn op 23-07-2009 11:49 ]

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!

  • Dricus
  • Registratie: Februari 2002
  • Laatst online: 08:16

Dricus

ils sont fous, ces tweakers

.oisyn schreef op donderdag 23 juli 2009 @ 11:45:
Je reageert alsof mijn oplossing typedefs uitsluiten
Ik zeg dat niet en dat bedoel ik ook niet, dus da's dan jouw (verkeerde) interpretatie.
(je hebt het immers over mijn oplossing en jouw oplossing en dat daaruit gekozen moet worden - je praat niet over een combinatie van beide). Dat is weldegelijk onzin :).
Hee, ik probeer mee te denken door twee oplossingen te beschouwen en een afweging van argumenten te geven. Ik zeg nergens dat het één het ander uitsluit of dat er niets anders mogelijk is. Dat maak jij ervan om vervolgens wat ik zeg meteen als 'onzin' af te fakkelen, erg jammer...

Voor de duidelijkheid dan maar even: Er zijn meerdere goede manieren om het probleem van de TS op te lossen. Welke de beste is hangt IMHO af van hoe de class uiteindelijk gebruikt gaat worden en van smaak.
.oisyn schreef op donderdag 23 juli 2009 @ 11:45:
Dat je een CRandom<boost::uniform_int<>> instantie bijv. niet aan een CRandomInt kunt assignen. En dat andere template code die een CRandom<T> gebruikt waarbij T = boost::uniform_int<> niet automatisch een CRandomInt instantieert.
Op zich goede argumenten hoor, dus begrijp me niet verkeerd, maar in dit specifieke geval lijken ze mij nogal hypothetisch.

[ Voor 32% gewijzigd door Dricus op 23-07-2009 12:44 ]

Stel niet uit tot morgen wat je vandaag nog tot morgen kunt uitstellen...


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Overigens instantieer je nu voor elke range een nieuwe random engine die je seed op de clock; dat zou ik niet zo doen. Ik zou zorgen dat je een enkele random engine hebt (wellicht per thread) die je een keer seed.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dricus schreef op donderdag 23 juli 2009 @ 12:33:
[...]

Ik zeg dat niet en dat bedoel ik ook niet, dus da's dan jouw (verkeerde) interpretatie.
Nou ja da's de enige manier waarop ik het volgende kan interpreteren:
Dricus schreef op donderdag 23 juli 2009 @ 11:07:
Om die twee redenen zou ik eerder voor mijn oplossing dan voor die van .oisyn gaan. Maargoed, in hoeverre dat voor jouw project relevante argumenten zijn kan ik niet beoordelen. Mijn oplossing is niet per definitie beter dan die van .oisyn.
Maar soit, als jij zegt dat je het niet zo bedoelde dan heb ik niets gezegd :)
Dat maak jij ervan om vervolgens wat ik zeg meteen als 'onzin' af te fakkelen, erg jammer...
Nou neem je het te persoonlijk. Ik zeg niet dat dat wat jij zegt onzin is. Ik zeg dat het onzin is dat mijn oplossing het gebruik van typedefs uitsluit (waarbij nu blijkt dat jij dat niet bedoelde, maar dat staat er los van). Of vind jij dat geen onzin?

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!

  • Dricus
  • Registratie: Februari 2002
  • Laatst online: 08:16

Dricus

ils sont fous, ces tweakers

.oisyn schreef op donderdag 23 juli 2009 @ 12:48:
Nou neem je het te persoonlijk. Ik zeg niet dat dat wat jij zegt onzin is.
Nou ja, da's de enige manier waarop ik het volgende kan interpreteren: ;)
.oisyn schreef op donderdag 23 juli 2009 @ 11:24:
Da's natuurlijk ook onzin, [...]
Maar soit, als jij zegt dat je het niet zo bedoelde dan heb ik niets gezegd :) :+.
Ik zeg dat het onzin is dat mijn oplossing het gebruik van typedefs uitsluit (waarbij nu blijkt dat jij dat niet bedoelde, maar dat staat er los van). Of vind jij dat geen onzin?
Ik ben het met je eens dat jouw oplossing het gebruik van typedefs niet uitsluit. Je kunt hier hoogstens nog een soort van estetische/academische discussie voeren over of het gebruik van typedefs nu wel 'echt OO' is of niet, maar dan begeeft je je imho in het gebied van de smaak en persoonlijke voorkeur en da's voor iedereen verschillend.

Stel niet uit tot morgen wat je vandaag nog tot morgen kunt uitstellen...


Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
Zoijar schreef op donderdag 23 juli 2009 @ 12:38:
Overigens instantieer je nu voor elke range een nieuwe random engine die je seed op de clock; dat zou ik niet zo doen. Ik zou zorgen dat je een enkele random engine hebt (wellicht per thread) die je een keer seed.
Je hebt een goed punt daar! Zou je me met een paar keywords in de goede richting kunnen trappen? Ik zou dit graag zo oplossen dat bij de eerste instatiatie van CRandom een engine wordt gecreerd en daarna bij latere instantiaties deze enige simpelweg wordt hergebruikt. Is er een manier om die engine persistent te maken over de verschillende classe instanties? Of moet ik simpelweg de enige buiten mijn classe instantieren en dan in de klasse via reference gebruiken?

Na een klein schopje in de goede richting ga ik graag aan de slag en zal ik terugkomen met werkende code of meer vragen.

Bedankt!

Acties:
  • 0 Henk 'm!

  • Dricus
  • Registratie: Februari 2002
  • Laatst online: 08:16

Dricus

ils sont fous, ces tweakers

Daarvoor kun je bijvoorbeeld het Singleton design pattern gebruiken.

Stel niet uit tot morgen wat je vandaag nog tot morgen kunt uitstellen...


Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
Hoi Dricus,

Dank voor de snelle tip, ik heb het nu aan de praat met dit singleton design pattern.

Als ik watch zet op EngineSingleton::pEng, zie ik dat de pointer eerst nul is. Maak ik een CRandom object aan, dan krijgt hij een waarde. Als de CRandom instantie uit scope gaat en dus gedestruct wordt behoud de pointer zijn waarde en bij instantie van een later CRandom object point de pointer nog steeds naar hetzelfde geheugenadres. Het geheel lijkt dus te werken.

Wel heb ik de standaard implementatie iets aangepast, mijn instance() (eng()) returned geen pointer naar de class zelf, maar naar de engine class. Is dit goed?

Verder moet ik een delete op de pointer zetten die aan het einde van het programma vanzelf aangeroepen wordt. Hoe doe ik dat? Een boost::shared_ptr ipv boost::mt19937* op de eerste implementatie regel, of gaat de refcount daarvan niet naar 0 aan het einde van de main functie omdat hij static is? (note: ik ben niet echt bekend met de boost:shared_ptr.
De meest zinnige tekst die ik tegenkwam was http://www.research.ibm.com/designpatterns/pubs/ph-jun96.txt, zie de drie paragrafen onder de eerste twee blokken code. Maar dit is wel erg oud. De boost:shared_ptr is toch een betere en meer bruikbare versie van de std::auto_ptr waar ze het in de tekst over hebben?

edit:
Hmm, na wat meer gelezen te hebben lijkt een shared_ptr me overkill. Ik moet een boost::scoped_ptr gebruiken. Mijn engine zal nooit van owner veranderen, en altijd maar een owner hebben: alleen die ene singleton class. Het enige wat ik nodig heb is dat de resource waar de pointer naar wijst automatisch weggegooit wordt.

Ik ga maar eens de destructor van die enigine tijdelijk aanpassen om te kijken of al dit gedoe uberhaupt nodig is, misschien is MSVC slim genoeg om dit zelf te doen en hoef ik geen smart pointers te gebruiken. Aan de andere kant zal ik het denk ik toch doen, zodat met een blik op de code duidelijk is dat we ons geen zorgen hoeven te maken over resource leaks.

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
#pragma once

// includes
#include <boost/random.hpp>
#include <time.h>

// declaration
template<class Dist>
class CRandom
{
public:
    /** use for Dist:
        CRandomI Ci(min,max);   uses uniform_int<int>       - integral types
        CRandomR Cr(min,max);   uses uniform_real<double>   - floating point types
        
        for other ditributions or types, use:
        CRandom<distribution<optional type>> Cr(0--3 params, depending on distribution);
        for distributions and params, see: http://www.boost.org/doc/libs/1_39_0/libs/random/random-distributions.html
    */

    // forwarding constructors
    explicit CRandom()
        : Gen(*EngineSingleton::Eng(), Dist())
    { } 
    template<class P>
    explicit CRandom(const P & p1)
        : Gen(*EngineSingleton::Eng(), Dist(p1))
    { } 
    template<class P>
    explicit CRandom(const P & p1, const P & p2)
        : Gen(*EngineSingleton::Eng(), Dist(p1, p2))
    { } 
    template<class P>
    explicit CRandom(const P & p1, const P & p2, const P & p3)
        : Gen(*EngineSingleton::Eng(), Dist(p1, p2, p3))
    { } 

    ~CRandom(void) {};

    typedef typename Dist::result_type result_type;

    result_type operator()() { return Gen(); }
    template<class T>
    result_type operator()(T value) { return Gen(value); }

private:
    boost::variate_generator<boost::mt19937,Dist> Gen;
};
// end declaration


// shorthand typedefs
typedef CRandom<boost::uniform_int<int>>        CRandomI; // even int is default type   , specify it in case it ever changes
typedef CRandom<boost::uniform_real<double>>    CRandomR; // even double is default type, specify it in case it ever changes


/** engine wrapper class following singleton pattern, so every instantiation
    of CRandom uses the same engine
    based on: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
*/
// declaration
class EngineSingleton {
public:
    static boost::mt19937* Eng();
protected:
    EngineSingleton();
private:
    static boost::mt19937* pEng;
};
// end declaration

// implementation
boost::mt19937* EngineSingleton::pEng = NULL;

boost::mt19937* EngineSingleton::Eng() {
    if (pEng == NULL) {
        pEng = new boost::mt19937(unsigned int(time(NULL)));
    }
    return pEng;
}
// end implementation

[ Voor 13% gewijzigd door TheWickedD op 24-07-2009 14:21 . Reden: auto_ptr stukje toegevoegd; scoped_ptr toegevoegd ]


Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
Na een beetje discussie op de boost mailing lijst, is dit de uiteindelijke code, voor zij die geinteresseerd zijn.

Ik heb op aanraden geprobeerd de singleton van boost:serialization te gebruiken, maar die ondersteund geen objecten met parameterized constructors. Dus heb ik (na het lezen van een artikel over singletons) een Meyers Singleton tevoorschijn gehaald and daar nog wat code aan toegevoegd. Alles werkt nu zoals het moet.

Bedankt voor alle hulp!

CRandom class:
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
#pragma once

// includes
#include <boost/random.hpp>
#include <time.h>
#include <iostream>
#include "Singleton.h"

// namespaces
namespace s     = std;
namespace b     = boost;

// declaration
template<class Dist>
class CRandom : public b::variate_generator<b::mt19937&,Dist> // notice the reference, we do not want to copy the engine
{
public:
    /** use for Dist:
    CRandomI Ci(min,max);   uses uniform_int<int>       - integral types
    CRandomR Cr(min,max);   uses uniform_real<double>   - floating point types

    for other ditributions or types, use:
    CRandom<distribution<optional type>> Cr(0--3 params, depending on distribution);
    for distributions and params, see: http://www.boost.org/doc/libs/1_39_0/libs/random/random-distributions.html
    */

    // forwarding constructors
    explicit CRandom()
        : variate_generator<b::mt19937&,Dist>(
        Singleton<b::mt19937>::Instance(unsigned int(time(NULL))),
        Dist()
        )
    { } 
    template<class P>
    explicit CRandom(const P & p1)
        : variate_generator<b::mt19937&,Dist>(
        Singleton<b::mt19937>::Instance(unsigned int(time(NULL))),
        Dist(p1)
        )
    { } 
    template<class P1, class P2>
    explicit CRandom(const P1 & p1, const P2 & p2)
        : variate_generator<b::mt19937&,Dist>(
        Singleton<b::mt19937>::Instance(unsigned int(time(NULL))),
        Dist(p1, p2)
        )
    { } 
    template<class P1, class P2, class P3>
    explicit CRandom(const P1 & p1, const P2 & p2, const P3 & p3)
        : variate_generator<b::mt19937&,Dist>(
        Singleton<b::mt19937>::Instance(unsigned int(time(NULL))),
        Dist(p1, p2, p3)
        )
    { }
};
// end declaration


// shorthand typedefs
// random integer within range
typedef CRandom<b::uniform_int<int>>                CRandomI; // even int is default type   , specify it in case it ever changes
// random real number within range
typedef CRandom<b::uniform_real<double>>            CRandomR; // even double is default type, specify it in case it ever changes
// random boolean
typedef CRandom<b::bernoulli_distribution<double>>  CRandomB; // even double is default type, specify it in case it ever changes


Singleton class:
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
#pragma once

// Meyers singleton with support for constructors with 1 parameter

// http://www.devarticles.com/c/a/Cplusplus/C-plus-plus-In-Theory-The-Singleton-Pattern-Part-I/
// http://www.devarticles.com/c/a/Cplusplus/C-plus-plus-In-Theory-The-Singleton-Pattern-Part-2/
// http://www.devarticles.com/c/a/Cplusplus/The-Singleton-Pattern-Revisited/
// http://www.cplusplus.com/forum/beginner/1459/

template <class T>
class Singleton
{
public:
    static T& Instance() {
        static T _instance;
        return _instance;
    }
    template<class P>
    static T& Instance(const P & p) {
        static T _instance(p);
        return _instance;
    }
private:
    Singleton();                            // ctor hidden
    ~Singleton();                           // dtor hidden
    Singleton(Singleton const&);            // copy ctor hidden
    Singleton& operator=(Singleton const&); // assign op hidden
};

[ Voor 49% gewijzigd door TheWickedD op 05-08-2009 07:41 . Reden: eigen singleton die parameterized construction doet + CRandom is nu een derived class ]

Pagina: 1