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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
|
#ifndef Multiple_Dispatch_Via_Visitor_Wrapper_h
#define Multiple_Dispatch_Via_Visitor_Wrapper_h
/************************************************************************
Project : Multiple Dispatch Wrappers
File : multiple_dispatch.h ,
Single header, sans fonctionnalité avancée
Author : HARBULOT Julien
Copyright (C) 2014 HARBULOT Julien Edmond René
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
email : info [at] julienh.fr
************************************************************************
* Système de multiple dispatch à N arguments (pour tout N > 0)
*
* Utilisation :
* Chaque classe dont on voudra reconnaître le type implémente le
* pattern visitor [aka : elle possède une methode accept(...) ]
* Les wrappers prennent un nombre quelconque d'arguments et une action.
* Ils s'occupent de retrouver le type de tous les arguments via leurs
* méthodes accept(...), puis apellent l'action demandée.
*
* Version : 4 - std::tuple
************************************************************************/
#include <tuple>
//========================================================================
//================================ LIBRARY ===============================
//========================================================================
namespace dispatch{
namespace multiple_dispatch_impl{
//====================================================================
//====================================================================
//
// === Family ===
//
// A partir du type BaseClass, un dispatcher doit retrouver le bon type dérivé.
// Pour cela, il doit connaitre la liste des types dérivés (DerivedList)
// Et il doit pouvoir visiter la hiérarchie en héritant de VisitorBase
// Ces données constituent une famille :
template <class VisitorBase, class BaseClass, class... DerivedList>
struct Family{
template <class... Ts> struct L;
using Visitor = VisitorBase;
using Base = BaseClass;
using Deriveds = L<DerivedList...>;
//using ToList = L<VisitorBase, BaseClass, DerivedList...>;
};
//====================================================================
//====================================================================
//
// === Multiple Dispatch class ===
//
template
<
class Action, class Data_t,
size_t nFamilyToProcess, size_t curFamilyIndex, // MaxN et CurN
class... Families
>
struct MultiDisp;
//
// On écrit : methodeGenerique(BaseClass1* d1, BaseClass2* d2, BaseClass3* d3, ...)
// Et le MultiDisp appelle : ActionSpecifique(Derivee1A* d1a, Derivee2A*, d2a, Derivee3B*, d3b)
//
// Pour ce faire, le MultiDisp doit retrouver le type dérivé de ses nFamilyToProcess arguments.
// Or, pour retrouver un type dérivé au sein d'une hiérarchie (Family), le MultiDisp
// implémente les fonctions visit() d'un curVisitor dont il hérite, action pour laquelle il doit connaitre
// toutes les classes dérivées possibles (DerivedList) de la hiérarchie.
// Principe de résolution :
// On a des pointeurs vers les classes mères d'une part (dans Data_t).
// On a une liste de familles d'autre part (dans le parameter_pack).
//
// Le type des pointeurs ne change qu'au dernier moment (Data_t ne change pas).
// Ce qui change, c'est notre parameter_pack qui fonctionne comme une file :
// On enlève une famille à traiter au début (Step 1) et on ajoute le type trouvé a la fin (Step 2).
// Ensuite, il suffit de caster chaque pointeur vers le type trouvé qui lui correspond (Condition d'arrêt).
//
// Pour savoir où on en est dans la file, on utilise deux index : CurN (index courant) et MaxN (nb familles)
// Lorsque CurN == MaxN, on a traité toute la file
// et le parameter_pack ne contient plus que les types trouvés.
// Nomenclature :
// Une famille est representée par un parameter_pack de la facon suivante :
// Family<curVisitor , curBase , DerivedList...>
// L'avantage d'une telle représentation est de pouvoir utiliser le filtrage de motif sur DerivedList :
// Family<DerivedList...> = Family<DerivedHead, DerivedTail...>
//Step 1 :
// On itère sur les derivées de la famille F1 pour implémenter les fonctions visit(...)
// A chaque étape, la liste <DerivedTail...> décroit.
template
<
class A, class D_t,
size_t MaxN, size_t CurN,
class curVisitor, class curBase, class DerivedHead, class... DerivedTail,
class... otherFamilies
>
struct MultiDisp<A, D_t, MaxN, CurN, Family<curVisitor, curBase, DerivedHead, DerivedTail...> , otherFamilies...>
: public MultiDisp<A, D_t, MaxN, CurN, Family<curVisitor, curBase, DerivedTail...>, otherFamilies...> //on enleve DHead
{
protected:
using Parent = MultiDisp<A, D_t, MaxN, CurN, Family<curVisitor, curBase, DerivedTail...>, otherFamilies...>;
public:
using Parent::Parent;
using Parent::visit;
void visit(DerivedHead*){
// On arrive dans cette fonction après avoir déclanché la visite (Step 2).
// A ce stade, on a trouvé le type du pointeur correspondant à la famille CurN.
// Le type trouvé (DerivedHead) est alors stocké à la fin de la file.
// La recherche se poursuit pour les familles suivantes.
// (Rappel : cas d'arrêt quand MaxN = CurN, (cf Condition d'arret)
MultiDisp<A, D_t, MaxN, CurN+1, otherFamilies..., DerivedHead> next{ Parent::data };
next.action();
}
};
//Step 2 :
// Fin de l'itération sur la famille courante : plus de dérivées.
// On va utiliser le pattern visitor pour trouver le bon type dérivé.
// Pour ce faire, on hérite du bon VisitorBase (curVisitorBase) et on visite le
// bon pointeur (dont on connait l'index CurN).
//
// La procédure se poursuit quand on arrive dans la bonne fonction visit(DerivedHead*)
// (voir plus haut).
template
<
class A, class Data_t,
size_t MaxN, size_t CurN,
class curVisitorBase, class curBase,
class... otherFamilies
>
struct MultiDisp<A, Data_t, MaxN, CurN, Family<curVisitorBase, curBase> , otherFamilies...>
: public curVisitorBase
{
protected:
Data_t const& data;
public:
MultiDisp(Data_t const& data) : data(data) {}
void action(){
std::get<CurN>(data)->accept(this);
}
};
//Condition d'arrêt :
// Quand CurN == MaxN, toutes les familles ont été visitées.
// Comme à chaque fois les types trouvés ont été placés à la fin de la file,
// le parameter_pack <Families...> est devenu <KnownTypes...>.
// Il nous reste à appeler l'action sur tous les arguments, après
// les avoir castés vers le bon type.
template
<
class Action, class Data_t,
size_t nbTypes,
class... KnownTypes
>
struct MultiDisp<Action, Data_t, nbTypes, nbTypes, KnownTypes...>{
protected:
Data_t const& data;
public:
MultiDisp(Data_t const& data) : data(data) {}
void action(){
actionHelper(std::make_index_sequence<nbTypes>{});
}
private:
template <size_t... I>
void actionHelper(std::index_sequence<I...>){
Action action;
action(dynamic_cast<KnownTypes*>(std::get<I>(data))...); //@TODO
}
};
// ===================================
// Interface / gestion des pointeurs :
//
// Les pointeurs sont gérés par un std::tuple<BaseClass*...>
// Pour éviter la duplication, c'est cette classe qui garde les pointeurs en mémoire.
// On s'occupe aussi d'initialiser les index.
template <class Action, class... Families>
struct Dispatcher{
private:
using Data_t = std::tuple<typename Families::Base* ...>;
using DispatcherImpl = MultiDisp<Action, Data_t, sizeof...(Families), 0, Families...>;
Data_t data;
public:
Dispatcher(Action* a, typename Families::Base* ... to_solve)
: data{to_solve...} {
}
void operator()(){
DispatcherImpl{data}.action();
}
};
} //namespace implementation
using multiple_dispatch_impl::Family;
using multiple_dispatch_impl::Dispatcher;
} //namespace public
#endif // defined Multiple_Dispatch_Via_Visitor_Wrapper_h */ |
Partager