Bonjour à tous.
J'adore le C++, ses subtilités et ses problèmes tordus.

En l'occurence, j'ai aujourd'hui une difficulté à base de templates.

Pour rappel, le CRTP est un mode d'héritage template assez courant, qui permet, entre autre, un polymorphisme à la compilation.
J'ai pris l'habitude d'utiliser CRTP comme nom de ce parametre template. Ca aide à repérer le pattern.

Voici donc la classe de base réelle de mon problème (à ceci près que j'ai en plus un wrapper autour du stream).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
template <typename Object, typename CRTP>
struct streamable {
  void operator() (std::ostream& stream, object_type const& o) const {
    static_cast<CRTP&>(*this).write_into(stream, o);
  }
 
  void operator() (std::istream& stream, object_type const& o) const {
    static_cast<CRTP&>(*this).read_from(istream, o);
  }
};
Comme vous pouvez le noter, il y a deux operator(), correspondant à deux std::function;

Cette classe sert de base à toute une hiérarchie de templates, pour créer un système "expression template".
Cela signifie qu'au final, je peux écrire
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
struct S { int a, b; };
 
using namespace std;
int main() {
  auto streamer = streamer<S>("a=") + &S::a + ";b=" + &S::b+';';
  S s;
  cin >> streamer(s);
  swap(s.a, s.b);
  cout << streamer(s);
  return 0;
}
En gros, il s'agit d'unifier une lambda pour ostream et une pour istream.

Mon soucis, c'est de rendre ce code compatible avec C++03. (snif)
Ca fonctionne... mais auto devient :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
streamer_join<
  streamer_join<
    streamer_join<
      streamer_join< streamer_const<S, string>, streamer_member<S, int> >,
      streamer_const<S, string>
    >,
    streamer_member<S, int>
  >,
  streamer_const<S, char>
>
Du coup, ce n'est pas très pratique.
Il me faudrait un type qui soit template uniquement sur le type d'objet à manipuler, qui puisse recevoir cette classe.
J'ai bien la piste de boost::function, mais comment unir les deux interfaces?

J'ai envisagé la classe suivante
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
template <typename Object>
class Streamer {
private:
    boost::function< void(std::ostream& stream, Object const& o) > output_interface;
    boost::function< void(std::istream& stream, Object const& o) > input_interface;
public:
    template <typename CRTP>
    Streamer< streamable<Object, CRTP> const& interface): output_interface(interface), input_interface(interface) {} 
 
    //définition des deux operator() pour appeler les interface.
};
Ca fonctionnerait plutot bien, mais il y a un petit défaut: le streamable est stocké deux fois. Si vous avez une idée sur la question, je suis preneur.

Par contre, il y a un autre défaut, plus gros.
Dans ma classe streamable réelle, les opérator() sont template sur le type de stream:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
template <typename Object, typename CRTP>
struct streamable {
  template <typename OStream>
  void operator() (stream_wrapper<OStream>& stream, object_type const& o) const {
    static_cast<CRTP&>(*this).write_into(stream, o);
  }
};
Et là, subitement, je ne peux plus stocker d'objet function, puisqu'il n'y en a pas qu'une.
J'en arriverai à vouloir stocker un unique_ptr d'une copie du streamable, mais n'ayant pas de type CRTP, définir le pointeur.

Je ne vois pas d'échappatoire, et j'ai vraiment besoin de définir une variable (voire un membre d'une classe).