[C++] Object in lijst vinden mbv. member functies

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Ik merk dat ik steeds vaker de std functies van C++ gebruik, maar er zijn er nog steeds enkele die mij keer op keer ontglippen. Aangezien dit tot steeds meer frustratie leidt en ik vast niet de enige ben die hier last van heeft leek het me handig mijn vraag in een topic te plaatsen in de hoop wat duidelijkheid te scheppen :)

het probleem: ik heb een lijst met objecten en ik wil graag een specifiek object vinden op basis van een member functie.

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
class Aap
{
    public:
        Aap( int nummer ) : nr( nummer ) { }
        int nummer() const { return nr; }
    private:
        int nr;
};

class DezelfdeAap : public std::binary_function< Aap, int, bool >
{
    public:

        bool operator()( const Aap &aap, const int &i ) const
        {
            return aap.nummer() == i;
        }
};

int main(int argc, char *argv[])
{
    std::vector< Aap > apen;
    for( unsigned int i = 0; i < 10; i++ )
    {
        apen.push_back( i );
    }

    std::vector< Aap >::iterator gevonden = std::find_if( apen.begin(), apen.end(), std::bind2nd( DezelfdeAap(), 3 ) );

    return 0;
}

Deze methode werkt, al betekend dit dat ik voor elke member functie een eigen klasse moet schrijven.
Het lijkt me dat dit makkelijker moet kunnen :?

Een mogelijk optie lijkt me dat je je gewenste member als parameter meegeeft aan de bind functie. Aangezien er volgens mij geen trinary_function bestaat zul je dit wellicht moeten oplossen met een std::pair ofzo? Of bestaat er nog een slimmer methode om code duplication tegen te gaan?

alvast bedankt!

oprecht vertrouwen wordt nooit geschaad


Acties:
  • 0 Henk 'm!

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Met Wikipedia: C++0x gaat dit beter.
Tot die tijd gebruik ik zelf gewoon een for / BOOST_FOREACH loop.

Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Arjan schreef op vrijdag 09 juli 2010 @ 12:10:
Deze methode werkt, al betekend dit dat ik voor elke member functie een eigen klasse moet schrijven.
Het lijkt me dat dit makkelijker moet kunnen :?
Ja, door gewoon een functie te schrijven en geen klasse. Anders moet je kijken naar boost::lambda, of een compiler gebruiken die C++0x' lambda feature al implementeert (zoals VC++ 2010):
C++:
1
std::find_if( apen.begin(), apen.end(), [](const Aap & aap) { return aap.nummer() == 3; })

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
.oisyn schreef op vrijdag 09 juli 2010 @ 12:30:
[...]

Ja, door gewoon een functie te schrijven en geen klasse.
Ik dacht dat een classe verplicht was in zo'n geval :? ik kreeg het iig niet werkend met een functie, zou je een voorbeeld willen geven?
Anders moet je kijken naar boost::lambda, of een compiler gebruiken die C++0x' lambda feature al implementeert (zoals VC++ 2010):
C++:
1
std::find_if( apen.begin(), apen.end(), [](const Aap & aap) { return aap.nummer() == 3; })
Ik wilde de C++0x kat eigenlijk nog even uit de boom kijken :)

oprecht vertrouwen wordt nooit geschaad


Acties:
  • 0 Henk 'm!

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

H!GHGuY

Try and take over the world...

Je gebruikt de foute container. Wat jij wil is een set/map (of hashset hashmap).

Mocht je meerdere keys willen dan kun je kijken naar de boost multi indexed container.

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

H!GHGuY schreef op vrijdag 09 juli 2010 @ 12:47:
Je gebruikt de foute container. Wat jij wil is een set/map (of hashset hashmap).
Da's natuurlijk ook onzin. Je gaat niet ineens van container switchen alleen maar omdat je in een specifiek geval een element wilt opzoeken. Bij de keuze van een container kijk je met name naar de gemiddelde usecase, niet naar de worst case usecase.

[ Voor 8% gewijzigd door .oisyn op 09-07-2010 12:52 ]

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...

.oisyn schreef op vrijdag 09 juli 2010 @ 12:51:
[...]

Da's natuurlijk ook onzin. Je gaat niet ineens van container switchen alleen maar omdat je in een specifiek geval een element wilt opzoeken. Bij de keuze van een container kijk je met name naar de gemiddelde usecase, niet naar de worst case usecase.
Aangezien hij, volgens de startpost, meer tegen dit probleem aanloopt heeft ie wel degelijk een foute container genomen.
Bovendien kan de multi indexed container ook list behavior doen.

edit:

C++:
1
2
3
4
5
6
7
8
9
10
template <class T, typename Y, Y (*func)(const T&)>
struct compare
{
  bool operator()(const T& t, const Y& y)
  {
    return func(t) == y;
  }
};

std::find_if(..., compare<Aap, int, App::nummer>)

zou ook kunnen. Deze code is niet getest en kan/zal zeker niet meteen werken, maar het principe zou moeten werken.

[ Voor 23% gewijzigd door H!GHGuY op 09-07-2010 13:05 ]

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

Arjan schreef op vrijdag 09 juli 2010 @ 12:38:
[...]

Ik dacht dat een classe verplicht was in zo'n geval :? ik kreeg het iig niet werkend met een functie, zou je een voorbeeld willen geven?
Heb je std::ptr_fun() wel gebruikt? Die wrapt een functiepointer in zo'n class :)
C++:
1
2
3
4
5
6
7
8
bool DezelfdeAap( const Aap &aap, const int &i ) const
{
    return aap.nummer() == i; 
}

// ...

std::vector< Aap >::iterator gevonden = std::find_if( apen.begin(), apen.end(), std::bind2nd( std::ptr_fun(DezelfdeAap), 3 ) );

Of je gebruikt std::tr1::bind(), die heeft er niet zo'n last van.
H!GHGuY schreef op vrijdag 09 juli 2010 @ 12:56:
[...]


Aangezien hij, volgens de startpost, meer tegen dit probleem aanloopt heeft ie wel degelijk een foute container genomen.
Hou toch eens op met die generalisaties. Je hebt totaal geen idee van z'n requirements maar toch denk je conclusies te kunnen trekken over de gebruikte container? Bovendien zegt ie dat hij steeds vaker tegen het probleem aanloopt dat hij iets niet op kan lossen met C++ en STL constructies. Niet dat hij steeds dingen op moet zoeken in containers. En dan nog, zelfs al moet ie steeds dingen opzoeken, als je search op iedere container nog steeds maar 0.1% van de tijd bedraagt dan blijft het onzin om het te optmaliseren door een index erbij te gebruiken. Als je veel insert en delete moet die index ook geüpdate worden wat tijd kost, én het kost meer geheugen.

[ Voor 46% gewijzigd door .oisyn op 09-07-2010 13:02 ]

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...

.oisyn schreef op vrijdag 09 juli 2010 @ 12:57:
[...]
Hou toch eens op met die generalisaties. Je hebt totaal geen idee van z'n requirements maar toch denk je conclusies te kunnen trekken over de gebruikte container? Bovendien zegt ie dat hij steeds vaker tegen het probleem aanloopt dat hij iets niet op kan lossen met C++ en STL constructies. Niet dat hij steeds dingen op moet zoeken in containers. En dan nog, zelfs al moet ie steeds dingen opzoeken, als je search op iedere container nog steeds maar 0.1% van de tijd bedraagt dan blijft het onzin om het te optmaliseren door een index erbij te gebruiken. Als je veel insert en delete moet die index ook geüpdate worden wat tijd kost, én het kost meer geheugen.
Als je 1 of 2 maal zo'n struct moet schrijven dan doe je dat snel en vergeet je het maar.
Als je het 10 maal moet doen dan zoek je naar een algemenere snellere methode.

De TS zegt: "al betekend dit dat ik voor elke member functie een eigen klasse moet schrijven"
Conclusie hij wil kunnen zoeken op elke mogelijke member. Iets zegt me dat zoeken in dat geval een belangrijke use-case is.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
ASSUME makes an ASS out of U and ME
Het is zo'n 20 graden te warm voor discussies :p
.oisyn schreef op vrijdag 09 juli 2010 @ 12:57:
[...]

Heb je std::ptr_fun() wel gebruikt? Die wrapt een functiepointer in zo'n class :)
Dat wist ik niet.
Of je gebruikt std::tr1::bind(), die heeft er niet zo'n last van.

[...]
dat werkt idd. prima, maar vereist een functie per member. Geen onoverkoombaar probleem, maar zou het niet nog iets generieker kunnen.

Ik liep (weer) tegen het probleem aan dat ik geen zin had om een for loopje te schrijven om een item uit een lijst te selecteren op basis van een member. Op zich geen probleem natuurlijk, maar omdat ik datzelfde loopje op meer plaatsen gebruik ( soms met kleine variaties ) leek het me handig om eens te kijken of de bedenkers van C++ niet ook al eens tegen dit 'probleem' waren aangelopen ;)
H!GHGuY schreef op vrijdag 09 juli 2010 @ 12:56:
[...]

C++:
1
2
3
4
5
6
7
8
9
10
template <class T, typename Y, Y (*func)(const T&)>
struct compare
{
  bool operator()(const T& t, const Y& y)
  {
    return func(t) == y;
  }
};

std::find_if(..., compare<Aap, int, App::nummer>)

zou ook kunnen. Deze code is niet getest en kan/zal zeker niet meteen werken, maar het principe zou moeten werken.
Ik heb deze code nog niet werkend gekregen, maar een dergelijke oplossing zou mijn voorkeur hebben.

oprecht vertrouwen wordt nooit geschaad


Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Voor een pointer-to-member moet je altijd & gebruiken, dus compare<Aap, int, &Aap::nummer>. En de aanroep moet zijn: (t.*func)() Handiger is in dat geval trouwens een functie te schrijven die de juiste compare teruggeeft, scheelt een hoop verbositeit.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template <class C, class T>  
struct compare_t : std::binary_function<const C &, const T &, bool> 
{ 
    compare_t(T (C::*func)() const) : m_func(func) { } 

    bool operator()(const C& c, const T& t) const
    { 
        return (c.*m_func)() == t; 
    } 
     
private: 
    T (C::*m_func)() const; 
}; 

template <class C, class T> 
compare_t<C,T> compare(T (C::*func)() const) 
{ 
    return compare_t<C,T>(func); 
} 


std::find_if(aap.begin(), aap.end(), std::bind2nd(compare(&Aap::nummer), 3));

(ook niet getest)

[ Voor 5% gewijzigd door .oisyn op 09-07-2010 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!

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Leuke oplossing, creatief met templates :)

kleine aanpassing was bij mij nog nodig:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <class C, class T>
struct compare_t
{
    typedef C first_argument_type;
    typedef T second_argument_type;
    typedef bool result_type;

    compare_t(T (C::*func)() const) : m_func(func) { }

    bool operator()(const C& c, const T& t) const
    {
        return (c.*m_func)() == t;
    }

private:
    T (C::*m_func)() const;
};

Volgens mij heeft binary_function ook geen virtuele destructor, nu weet ik niet of dat echt een probleem is in een klasse zonder members, maar toch :)

oprecht vertrouwen wordt nooit geschaad


Acties:
  • 0 Henk 'm!

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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Vaag, waarom zou die aanpassing nodig zijn :? Die types worden al gedefinieerd door std::binary_function.

Een virtuele destructor is alleen van belang als een derived class ook een dtor heeft, en je een delete wilt kunnen doen op een pointer van het type base.

[ Voor 26% gewijzigd door .oisyn op 09-07-2010 17:22 ]

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
ik ben er al achter, het kwam omdat je de template argumenten voor std::binary_function const / reference had gemaakt.

[ Voor 3% gewijzigd door Arjan op 09-07-2010 17:36 ]

oprecht vertrouwen wordt nooit geschaad

Pagina: 1