Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[boost] Parcourir une map de shared_ptr via for_each & bind

10 réponses
Avatar
Stephane Wirtel
Bonjour à tous,

Voici quelques jours que j'essaie de comprendre boost::bind,
pour cela, j'ai réalisé un petit exercice à base de vector de pointeur
d'instance, et l'exemple semblait concluant, jusqu'à ce que je commence
à utiliser une map comme container.

Voici le code que j'ai écrit, mais je n'arrive pas à résoudre mon
problème de compilation. Je cherche après un indice pour arriver à mon but.

#include <iostream>
#include <typeinfo>
#include <map>
#include <vector>
#include <functional>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <typeinfo>
#include <boost/assign/list_of.hpp>

#define DECLARE_ATTRIBUTES_WITH_ACCESSORS(_TYPE,_NAME) \
private: \
_TYPE m##_NAME; \
public: \
_TYPE _NAME() { return m##_NAME; } \
void _NAME( _TYPE const & value ) { m##_NAME = value; }

class User {
public:
typedef boost::shared_ptr< User > SharedPtr;
typedef std::vector< SharedPtr > Vector;
typedef std::map< std::string, SharedPtr > Map;

public:
User( std::string const & _login ) {
login( _login );
}

std::ostream & print( std::ostream & oss ) {
oss << "login: " << login() << std::endl
;
return oss;
}

DECLARE_ATTRIBUTES_WITH_ACCESSORS(int, age)
DECLARE_ATTRIBUTES_WITH_ACCESSORS(std::string, login)
};

int main( int argc, char ** argv ) {
try {
User::Map users =
boost::assign::map_list_of
( "user1", new User( "user1" ) )
( "user2", new User( "user2" ) )
;

std::for_each(
users.begin(), users.end(),
boost::bind(
&User::print,
boost::bind( &User::Map::value_type::second, _1 ),
boost::ref( std::cout )
)
);

/* std::for_each( users.begin(), users.end(), boost::bind(
&User::print, _1, boost::ref( std::cout ) ) ); */
}
catch( std::exception const & ex ) {
std::cerr << "exception: " << ex.what() << std::endl;
}
return 0;
}


Merci pour vos conseils.

10 réponses

Avatar
Michael DOUBEZ
Bonjour à tous,

Voici quelques jours que j'essaie de comprendre boost::bind,
pour cela, j'ai réalisé un petit exercice à base de vector de pointeur
d'instance, et l'exemple semblait concluant, jusqu'à ce que je commence
à utiliser une map comme container.

Voici le code que j'ai écrit, mais je n'arrive pas à résoudre mon
problème de compilation. Je cherche après un indice pour arriver à mon but.

#include <iostream>
#include <typeinfo>
#include <map>
#include <vector>
#include <functional>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <typeinfo>
#include <boost/assign/list_of.hpp>

#define DECLARE_ATTRIBUTES_WITH_ACCESSORS(_TYPE,_NAME)
private:
_TYPE m##_NAME;
public:
_TYPE _NAME() { return m##_NAME; }
void _NAME( _TYPE const & value ) { m##_NAME = value; }



Peut être NAME_ devrait être renvoyé en const ref ?
const _TYPE& _NAME() const { return m##_NAME; }


class User {
public:
typedef boost::shared_ptr< User > SharedPtr;
typedef std::vector< SharedPtr > Vector;
typedef std::map< std::string, SharedPtr > Map;

public:
User( std::string const & _login ) {
login( _login );
}

std::ostream & print( std::ostream & oss ) {
oss << "login: " << login() << std::endl
;
return oss;
}

DECLARE_ATTRIBUTES_WITH_ACCESSORS(int, age)
DECLARE_ATTRIBUTES_WITH_ACCESSORS(std::string, login)
};

int main( int argc, char ** argv ) {
try {
User::Map users > boost::assign::map_list_of
( "user1", new User( "user1" ) )
( "user2", new User( "user2" ) )
;

std::for_each(
users.begin(), users.end(),
boost::bind(
&User::print,
boost::bind( &User::Map::value_type::second, _1 ),
boost::ref( std::cout )
)
);


Pour autant que je sache, tu ne peux pas faire de bind sur les variables
membres. Il te faut un accessor:

template < class Pair>
typename Pair::second_type& pair_second(Pair& p)
{
return p.second;
}

puis:
std::for_each(
users.begin(), users.end(),
boost::bind(
&User::print,
boost::bind( &pair_second<User::Map::value_type>, _1 ),
boost::ref( std::cout )
)
);


(Pas essayé mais ça doit marcher)

Sinon, essaye:

std::for_each(
users.begin(), users.end(),
boost::bind(
boost::bind(&User::print,boost::ref( std::cout ),_2),
boost::bind(&pair_second<User::Map::value_type>, _1 )
)
);




/* std::for_each( users.begin(), users.end(), boost::bind(
&User::print, _1, boost::ref( std::cout ) ) ); */
}
catch( std::exception const & ex ) {
std::cerr << "exception: " << ex.what() << std::endl;
}
return 0;
}


Merci pour vos conseils.


Michael

Avatar
Stephane Wirtel
Peut être NAME_ devrait être renvoyé en const ref ?
const _TYPE& _NAME() const { return m##_NAME; }
Concernant les macros, c'était à la va-vite pour pouvoir me préoccuper

surtout sur l'utilisation de boost::bind

Pour autant que je sache, tu ne peux pas faire de bind sur les variables
membres. Il te faut un accessor:
D'après l'ouvrage "Beyond the C++ Standard Library - An introduction to

Boost" de Björn Karlsson, il est tout à fait possible de le faire sans
aucuns soucis.

D'ailleurs si tu enlèves mon shared_ptr pour le data_type de ma map,
cela fonctionne sans problèmes.

template < class Pair>
typename Pair::second_type& pair_second(Pair& p)
{
return p.second;
}

puis:
std::for_each(
users.begin(), users.end(),
boost::bind(
&User::print,
boost::bind( &pair_second<User::Map::value_type>, _1 ),
boost::ref( std::cout )
)
);


(Pas essayé mais ça doit marcher)

Sinon, essaye:

std::for_each(
users.begin(), users.end(),
boost::bind(
boost::bind(&User::print,boost::ref( std::cout ),_2),
boost::bind(&pair_second<User::Map::value_type>, _1 )
)
);

Merci pour ton aide, mais j'ai essayé avec la fonction templatée et sans

( en utilisant le binding de variables membres ) et le résultat est
toujours un soucis de compilation.

Par contre, peux-tu m'expliquer un peu l'idée derrière le second code
que tu m'as fait parvenir ?

std::for_each(
users.begin(), users.end(),
boost::bind(
boost::bind(&User::print,boost::ref( std::cout ),_2),
boost::bind(&pair_second<User::Map::value_type>, _1 )
)
);


Merci

Stéphane

Avatar
Michael DOUBEZ
Peut être NAME_ devrait être renvoyé en const ref ?
const _TYPE& _NAME() const { return m##_NAME; }
Concernant les macros, c'était à la va-vite pour pouvoir me préoccuper

surtout sur l'utilisation de boost::bind

Pour autant que je sache, tu ne peux pas faire de bind sur les
variables membres. Il te faut un accessor:
D'après l'ouvrage "Beyond the C++ Standard Library - An introduction to

Boost" de Björn Karlsson, il est tout à fait possible de le faire sans
aucuns soucis.



Ok. C'est bon à savoir.

D'ailleurs si tu enlèves mon shared_ptr pour le data_type de ma map,
cela fonctionne sans problèmes.



Tu veux dire en utilisant un pointeur ?


Par contre, peux-tu m'expliquer un peu l'idée derrière le second code
que tu m'as fait parvenir ?

std::for_each(
users.begin(), users.end(),
boost::bind(
boost::bind(&User::print,boost::ref( std::cout ),_2),
boost::bind(&pair_second<User::Map::value_type>, _1 )
)
);


Rien de bien extraordinaire, le binding est fait en deux temps au lieu
d'un. J'ai pas une grande habitude de boost.bind alors des fois je suis
prudent dans son utilisation.

Michael


Avatar
Stephane Wirtel
Tu veux dire en utilisant un pointeur ?
Oui, tout à fait.


Encore merci,

Avatar
Michael DOUBEZ
Bonjour à tous,

Voici quelques jours que j'essaie de comprendre boost::bind,
pour cela, j'ai réalisé un petit exercice à base de vector de pointeur
d'instance, et l'exemple semblait concluant, jusqu'à ce que je commence
à utiliser une map comme container.

Voici le code que j'ai écrit, mais je n'arrive pas à résoudre mon
problème de compilation. Je cherche après un indice pour arriver à mon but.

#include <iostream>
#include <typeinfo>
#include <map>
#include <vector>
#include <functional>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <typeinfo>
#include <boost/assign/list_of.hpp>

#define DECLARE_ATTRIBUTES_WITH_ACCESSORS(_TYPE,_NAME)
private:
_TYPE m##_NAME;
public:
_TYPE _NAME() { return m##_NAME; }
void _NAME( _TYPE const & value ) { m##_NAME = value; }

class User {
public:
typedef boost::shared_ptr< User > SharedPtr;
typedef std::vector< SharedPtr > Vector;
typedef std::map< std::string, SharedPtr > Map;

public:
User( std::string const & _login ) {
login( _login );
}

std::ostream & print( std::ostream & oss ) {
oss << "login: " << login() << std::endl
;
return oss;
}

DECLARE_ATTRIBUTES_WITH_ACCESSORS(int, age)
DECLARE_ATTRIBUTES_WITH_ACCESSORS(std::string, login)
};

int main( int argc, char ** argv ) {
try {
User::Map users > boost::assign::map_list_of
( "user1", new User( "user1" ) )
( "user2", new User( "user2" ) )
;

std::for_each(
users.begin(), users.end(),
boost::bind(
&User::print,
boost::bind( &User::Map::value_type::second, _1 ),


Je suis truffe. Il ne doit pas y avoir de spécialisation
bind()(shared_ptr<>()&)

Le code suivant fonctionne sous g++:

std::for_each(
users.begin(), users.end(),
boost::bind(
&User::print,
boost::bind(&User::SharedPtr::get,boost::bind(
&User::Map::value_type::second, _1 )),
boost::ref( std::cout )
)
);


boost::shared_ptr<T>() n'a pas de cast automatique vers T*.


boost::ref( std::cout )
)
);

/* std::for_each( users.begin(), users.end(), boost::bind(
&User::print, _1, boost::ref( std::cout ) ) ); */
}
catch( std::exception const & ex ) {
std::cerr << "exception: " << ex.what() << std::endl;
}
return 0;
}


Merci pour vos conseils.


Michael

Avatar
Stephane Wirtel
Merci Michael,

Je suis truffe. Il ne doit pas y avoir de spécialisation
bind()(shared_ptr<>()&)

Le code suivant fonctionne sous g++:

std::for_each(
users.begin(), users.end(),
boost::bind(
&User::print,
boost::bind(&User::SharedPtr::get,boost::bind(
&User::Map::value_type::second, _1 )),
boost::ref( std::cout )
)
);
'ai essayé ton code et cela semble fonctionner sans problème.


J'ai essayé ton exemple et cela fonctionne.


boost::shared_ptr<T>() n'a pas de cast automatique vers T*.


Mais j'ai tout de même une question, si tu regardes le code qui est en
commentaire, j'utilisais un User::Vector comme container, et ce code
fonctionnait parfaitement sous g++. Et comme tu as pû le constater, mon
User::Vector est vecteur de shared_ptr, donc, semblerait-il qu'il y ait
tout ce qui est nécessaire pour résoudre ce problème de manière plus
élégante ?

/* std::for_each( users.begin(), users.end(), boost::bind( &User::print, _1, boost::ref( std::cout ) ) ); */




Qu'en penses-tu ?

Stéphane

Encore merci pour ton exemple


Avatar
Mathias Gaunard

#define DECLARE_ATTRIBUTES_WITH_ACCESSORS(_TYPE,_NAME)
private:
_TYPE m##_NAME;
public:
_TYPE _NAME() { return m##_NAME; }
void _NAME( _TYPE const & value ) { m##_NAME = value; }

class User {
public:
typedef boost::shared_ptr< User > SharedPtr;
typedef std::vector< SharedPtr > Vector;
typedef std::map< std::string, SharedPtr > Map;

public:
User( std::string const & _login ) {
login( _login );
}

std::ostream & print( std::ostream & oss ) {
oss << "login: " << login() << std::endl
;
return oss;
}

DECLARE_ATTRIBUTES_WITH_ACCESSORS(int, age)
DECLARE_ATTRIBUTES_WITH_ACCESSORS(std::string, login)
};

int main( int argc, char ** argv ) {
try {
User::Map users > boost::assign::map_list_of
( "user1", new User( "user1" ) )
( "user2", new User( "user2" ) )
;

std::for_each(
users.begin(), users.end(),
boost::bind(
&User::print,
boost::bind( &User::Map::value_type::second, _1 ),
boost::ref( std::cout )
)



Avec lambda, tu peux pas tout simplement mettre un * devant pour
résoudre le problème ?

Avatar
Stephane Wirtel
Avec lambda, tu peux pas tout simplement mettre un * devant pour
résoudre le problème ?


pas encore essayé, mais je n'étais qu'à boost::bind, je vais y regarder.

Merci de l'indice

Avatar
Michael DOUBEZ
Merci Michael,

Je suis truffe. Il ne doit pas y avoir de spécialisation
bind()(shared_ptr<>()&)
boost::shared_ptr<T>() n'a pas de cast automatique vers T*.


Mais j'ai tout de même une question, si tu regardes le code qui est en
commentaire, j'utilisais un User::Vector comme container, et ce code
fonctionnait parfaitement sous g++. Et comme tu as pû le constater, mon
User::Vector est vecteur de shared_ptr, donc, semblerait-il qu'il y ait
tout ce qui est nécessaire pour résoudre ce problème de manière plus
élégante ?

/* std::for_each( users.begin(), users.end(), boost::bind(
&User::print, _1, boost::ref( std::cout ) ) ); */



Tu as raison. Le problème viens de la valeur retournée par le binding de

membre.

Le code suivant fonctionne:

std::for_each(
users.begin(), users.end(),
boost::bind(
&User::print,
boost::bind<User::SharedPtr&>(
&User::Map::value_type::second, _1 ),
boost::ref( std::cout )
)
);


Michael



Avatar
Stephane Wirtel
Tu as raison. Le problème viens de la valeur retournée par le binding de
membre.

Le code suivant fonctionne:

std::for_each(
users.begin(), users.end(),
boost::bind(
&User::print,
boost::bind<User::SharedPtr&>(
&User::Map::value_type::second, _1 ),
boost::ref( std::cout )
)
);


Michael


Merci, cela fonctionne nickel, et effectivement cela me semble moins

redondant dans le code.

Dommage qu'il n'y ait pas une FAQ avec ce genre d'exercice.

Stéphane