Boost Phoenix 2.0 tips
Lazy lazy evaluation + bind to overloaded member
Quelques tips pour Boost.Phoenix2 glanés dans la sueur et dans le sang (dans la doc aussi).
Higher higher order function
On a vite fait d'imbriquer les algorithms de Phoenix, mais il ne faut pas oublier qu'ils sont déjà des higher order functions. Leur prédicats doivent être évalués de manière lazy et même lazy lazy si je puis dire.
Un code vaut mieux qu'un long schema à main levée. Dans ce qui suit, j'ai deux std::vector de int. Je veux afficher les éléments du premier vector qui ne sont pas dans le second. Un code à la con qui a juste valeur d'exemple. Dedans y est glissée une erreur grossière. Saurez-vous la trouver ? Votre compilo saura la trouver lui, et il se fera un plaisir de générer environ 50ko de message d'erreur :)
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/home/phoenix/scope/local_variable.hpp>
#include <boost/spirit/home/phoenix/statement/if.hpp>
#include <boost/spirit/home/phoenix/scope/let.hpp>
#include <boost/spirit/home/phoenix/container.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_algorithm.hpp>
int main()
{
using boost::phoenix::arg_names::arg1;
using boost::phoenix::local_names::_a;
using boost::phoenix::ref;
using boost::phoenix::let;
using boost::phoenix::if_;
using boost::phoenix::find_if;
using boost::phoenix::end;
std::vector<int> a;
std::vector<int> b;
a.push_back(1);
a.push_back(2);
a.push_back(3);
b.push_back(2);
std::for_each(a.begin(), a.end(),
let(_a = arg1)
[
if_(end(ref(b)) == find_if(ref(b), arg1 == _a))
[
std::cout << _a << std::endl
]
]
);
return 0;
}
Le problème est que find_if attend un prédicat, dont on pourrait penser qu'il est tout à fait correcte en tant que composite phoenix arg1 == _a. Mais le bloc du find_if est déjà un composite, autrement dit une higher order function. Son prédicat doit être évalué de manière lazy lazy et non pas lazy -tout court.
La solution est fournie par phoenix: le composite lambda va permettre de transformer le prédicat récalcitrant en une higher higher order function. Le tour est joué:
std::for_each(a.begin(), a.end(),
let(_a = arg1)
[
// lambda[arg1 == _a]
if_(end(ref(b)) == find_if(ref(b), lambda[arg1 == _a]))
[
std::cout << _a << std::endl
]
]
);
Bind to overloaded member function
Qui n'a jamais été tenté de binder à gogo avec phoenix, tellement c'est facile ? Le prob survient le jour où on tente désespérément de binder une fonction qui a des overloads. le compilo se plaint avec un message du genre bind(<unresolved overloaded function type>, ..25 lignes de types phoenix..).
Example. Je cherche à binder std::map<K,V>::find. On va prendre int pour K et V. m est une instance quelconque de ce type map:
typedef std::map<int, int> map_t;
bind(&map_t::find, ref(m), arg1);
Pas bon. map_t::find a plusieurs overloads. Le compilo ne sait pas lequel choisir. Il faut le guider, après tout, c'est nous les boss dans l'histoire!
Méthode one liner:
bind(static_cast<
map_t::const_iterator (map_t::*)(int const&) const
>(&map_t::find), ref(m), arg1);
On utilise ce bon vieux static_cast.
Autre méthode, passer par un pointer sur fonction membre:
map_t::const_iterator (map_t::*find_f)(int const&) const = &map_t::find;
bind(find_f, ref(m), arg1);
Ca fait son job. On n'en demande pas plus.
Conclusion
RAS