Boujour à tous
Je me retrouve avec un problème quelque peu tordu.
Dans mon équipe, nous avons un générateur de code, qui prend une description de format de fichiers CSV en entrée.
L'idée, c'est d'abbréger fortement le temps d'écriture des validations des données, tout en offrant assez de souplesse à l'usage.
L'une de mes contraintes, c'est que ni C++11 ni Boost ne sont autorisés (j'ai dû récupérer optional lite pour avoir un optional correct)
Le code généré devrait ressembler à ceci:
Au passage, j'aimerai bien trouver un moyen d'écrire de partager la définition de type entre Bidule et Bidule_format...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 class Bidule { public: typedef types::string::value_type champ1_type; typedef types::optional_integer::value_type champ2_type; private: champ1_type champ1; champ2_type champ2; public: Bidule(champ1_type const& champ1, champ2_type const& champ2) : champ1(champ1), champ2(champ2) {} champ1_type const& getChamp1() const {return champ1;} champ1_type & getChamp1() {return champ1;} champ2_type const& getChamp2() const {return champ2;} champ2_type & getChamp2() {return champ2;} }; class Bidule_format { private: types::string champ1; types::decimal champ2; public: //les constructeurs des types permettent les réglages de validations en lecture et écriture Bidule_format() : champ1(8), champ2(4) {} //produce a value from internal state Bidule operator()() const { return Bidule( champ1(), champ2() ); } Bidule_format& clear() { champ1.clear(); champ2.clear(); return *this; } //set internal state according to value, and return itself Bidule_format & operator()(Bidule const& source) { champ1(source.getChamp1()); champ2(source.getChamp2()); return *this; } Bidule_format & from(std::istream& stream) { stream >> champ1 >> champ2; return *this; } Bidule_format const& into(std::ostream& stream) const { stream << champ1 << champ2; return *this; } };
sauf que la prédéclaration de Bidule n'est pas suffisante pour Bidule_format::operator()(), et que Bidule utiliserait Bidule_format::champ1_type::value_type
Il s'agit donc d'écrire dans une bibliothèque tout le code qui ne changerait pas d'une description à l'autre.
Et c'est là que mes soucis commencent.
J'ai une classe extractor qui me sert de base pour les différents types.
types::integer, types::optional_string, etc sont héritières de extractor.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 class extractor_base { public: virtual ~extractor() {} virtual void from(std::istream & stream) = 0; virtual void into(std::ostream & stream) const = 0; virtual void clear() =0; }; inline std::istream& operator>> (std::istream& stream, extractor_base & extractor) { extractor.from(stream); return stream; } inline std::ostream& operator<<(std::ostream& stream, extractor_base const & extractor) { extractor.into(stream); return stream; } template <typename T, bool optional = false> class extractor : public extractor_base{ public: typedef T concrete_type; typedef typename option_type<T, optional>::value_type value_type;//std::optional<T> ou T, selon que optional soit vrai ou non private: value_type v; protected: explicit extractor() {} explicit extractor(value_type const& v) : v(v) {} public: virtual ~extractor() {} value_type const& operator() () const {return v;} value_type & operator() () {return v;} void operator() (value_type const& value) {v = value;} virtual void clear() {v=value_type();} };
Sauf que j'ai deux mutualisations possibles, que je n'arrive pas à écrire correctement:
D'un coté, integer et optional_integer ont le même format.
De l'autre, les optional ont la même interaction avec les flux, et les autres types en partage une autre
Typiquement, je peux écrire
Sauf que la dedans, format() et extract() sont liées au types (les memes fonctions pour integer et optional_integer)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 template <typename Value> class strict_type : public extractor<Value, false> { public: virtual void from(std::istream & stream) { set(extract(next(stream))); } virtual void into(std::ostream & stream) const { stream << format(get()) << delimiter(); } }; template <typename Value> class optional_type : public extractor<Value, true> { public: virtual void from(std::istream & stream) { std::string token = next(stream); if (empty(token)) { set(optional::none); } else { set(extract(token)); } } virtual void into(std::ostream & stream) const { if (get()) { stream << format(*get()); } stream << delimiter(); } };
Et c'est la que je suis un peu perdu.
Comment continueriez-vous cette architecture?
J'ai au moins une contrainte sur le choix: scientific et decimal sont deux types ayant des formats différents, mais utilisant double comme type concret.
C'est ce qui m'a poussé jusque là à ne pas partir sur des spécialisations.
Je peux faire pas mal de chaos la dedans, les fonctions de formatage sont des fonctions libres (récupérées pour le moment).
Partager