Cette classe permet de spécifier un ensemble d'entités, c'est-à-dire de définir cet ensemble sans en expliciter les éléments a priori.
- Directive d'inclusion : #include EntitySpec.h
L'invocation de sa méthode prédéfinie dite d'expansion (Expand) permet de générer de façon dynamique l'ensemble d'entités correspondant à la spécification dans le contexte courant. Les éléments de la liste générée seront nécessairement des instances, directes ou indirectes, d'une même sous-classe de Entity, appelée ici classe de référence. Pour que la liste générée contienne des entités de différentes classes, la classe de référence doit en être une sur-classe commune.
L'utilisateur crée une instance de EntitySpec pour chaque spécification différente dans son domaine. Cette instance porte alors la spécification. Il peut aussi créer une sous-classe EntitySpec, et coder dans son constructeur la spécification. Il suffit alors d'instancier la sous-classe, par un simple appel au constructeur. Les constructeurs des sous-classes seront placées, typiquement, dans UserEntitySpec.cc.Trois manières de spécifier un ensemble d'entités
La définition de l'ensemble d'entités est donnée par l'utilisateur sous l'une des trois formes suivantes, mutuellement exclusives et liées à deux valeurs différentes du mode de la spécification.
Un premier complément de la spécification d'un ensemble d'entités est l'entité de référence. En effet, on peut vouloir sélectionner parmi les instances de la classe de référence, celles qui ont un certain lien fonctionnel ou structurel avec une autre, appelée l'entité de référence. Le moteur de DIESE passe cette information au prédicat de sélection ou à la fonction de construction sous la forme d'un argument prédéfini, de type Entity bien entendu.
- un prédicat de sélection qui, appliqué à une instance de la classe de référence, renvoie vrai si l'instance doit figurer dans l'ensemble. La méthode d'expansion invoque successivement ce prédicat pour toutes les instances (directes et indirectes) de la classe de référence. Le mode affecté est RETURN_INSTANCES. On peut souhaiter que, parmi les instances de la classe de référence satisfaisant le prédicat de sélection, toutes figurent dans la liste expansée (on construit alors la spécification avec le mot-clé ALLENT), ou bien seulement au plus les n premières rencontrées (avec le mot-clé NFIRST et un effectif n strictement positif). La méthode d'expansion n'invoque plus le prédicat dès que n entités ont été sélectionnées.
- une fonction de construction qui crée elle-même une instance de la classe de référence. Le mode affecté est INSTANTIATE. La méthode d'expansion invoque n fois cette fonction, n étant la valeur de l'effectif, crée la liste des instances créées et la retourne.
- une fonction de construction qui crée elle-même la liste des n instances de la classe de référence, n étant la valeur de l'effectif. Le mode affecté est RETURN_INSTANCES ou INSTANTIATE. La méthode d'expansion invoque une seule fois cette fonction, et retourne la liste de ces instances.
Un second complément sont les spécifications de domaines de valeurs (voir la classe DescValueSpec à la page 'La classe de base des spécifications de domaines de valeurs'). L'utilisateur ajoute des spécifications de domaines de valeurs à la spécification d'entités en invoquant la méthode AddDescValueSpec.
- Si le mode est RETURN_INSTANCES, la méthode d'expansion commence par exploiter, en conjonction, les spécifications de domaines de valeurs pour déterminer si une instance peut être retenue sur la base de ses valeurs courantes pour certains descripteurs. Le prédicat de sélection intervient ensuite comme une contrainte supplémentaire. Lorsqu'il utilisera des spécifications de domaines de valeurs, l'utilisateur mentionnera cependant fréquemment un prédicat renvoyant toujours vrai.
- Si le mode est INSTANTIATE, les spécifications de domaines de valeurs permettent à la méthode d'expansion de donner des valeurs particulières aux attributs des instances créées. Pour un attribut donné, la valeur affectée est choisie arbitrairement dans l'ensemble de valeurs spécifié (par la méthode GetValueInDomain).
Les étapes du codage
Les éléments de code qui permettent la sélection ou la création d'entités sur la base d'une spécification sont les suivants. Dans cet exemple, on s'intéresse à des instances de la classe A_CLASS.
- L'éventuelle création d'un domaine de valeurs pour un ou plusieurs descripteurs de A_CLASS. Avec la ligne suivante, on impose que le descripteur de symbole DESC_1 ait sa valeur à l'intérieur (IN) de l'intervalle [1 5] :
DescIntValueSpec* mySelectorDomain = new DescIntValueSpec(DESC_1, IN, 1, 5);- L'éventuelle création d'une fonction de sélection (dans le cas d'une sélection d'entités existantes) ou d'une fonction de construction (pour des instances qui n'existent pas encore). On place cette fonction dans UserEntitySpec.cc. Dans le code ci-dessous, on sélectionne les entités qui, en plus de satisfaire d'éventuelles contraintes spécifiées comme ci-dessus, ont 1 comme valeur du descripteur de symbole DESC_2, en excluant une instance désignée, passée en argument :
bool mySelectorPredicate(EntitySpecMethod* pM) { Entity* pEref = pM->GetEntityArgValue(REFERENCEENTITY_ARGUMENT); Entity* pE = pM->GetEntityArgValue(CANDIDATEENTITY_ARGUMENT); if((pE->GetIntValue(DESC_2) == 1) && (pE != pEref)) return TRUE; else return FALSE; }Noter que cette fonction est le corps d'une méthode EntitySelector (sous-classe de EntitySpecMethod) dont une instance est un descripteur fonctionnel de la classe EntitySpec.
- La création d'une instance de EntitySpec, en précisant la classe de référence, celle des entités renvoyées :
EntitySpec pES = new EntitySpec(A_CLASS);- L'éventuel attachement des domaines de valeurs et/ou de la fonction (de sélection ou de construction) créés :
pES->AssignSelectorPredicate(mySelectorPredicate); pES->AddDescValueSpec(mySelectorDomain);- La sélection, puis la récupération du résultat s'opèrent par :
Entity* pEref = GetNamedEntity(A_CLASS, "aClass_245"); pES->SetEntityArgValue(ENTITY_SELECTOR, REFERENCEENTITY_ARGUMENT, pEref); pES->Expand(); pEntityTab* result = pES->GetExpansion(); Entity* pE_0 = result->get_nth(0); int val1 = pE_0->GetIntValue(DESC_1); int val2 = pE_0->GetIntValue(DESC_2); printf("\npour '%s' DESC_1 = %d DESC_2 = %d", pE_0->InstanceName(), val1, val2);>> pour 'aClass_247' DESC_1 = 4 DESC_2 = 1Noter qu'un corps n'a pas été donné à la méthode QuantityEvaluator, fixant un nombre maximum. On sélectionne donc toutes les entités satisfaisant la spécification.
Spécifications alternatives
Soit la fonction suivante de détermination du nombre maximum d'instances a créer ou sélectionner :
int myQuantityEvaluator(EntitySpecMethod* pM) { int result; ... // valuation du nombre requis maximum return result; }Exemple de fonction d'instanciation :
EntitySpec pES = new EntitySpec(A_CLASS); pES->AssignEntityInstantiator(myEntityInstantiator); BasicEntity* myEntityInstantiator(EntitySpecMethod* pM) { Entity* result; Entity* pEref = pM->GetEntityArgValue(REFERENCEENTITY_ARGUMENT); UserClassId id = pM->GetIntArgValue(CLASSID_ARGUMENT); result = App_NewEntityFromID(id); result->SetIntVarValue(DESC_1, pEref->GetIntVarValue(DESC_1)); return result; }Exemple de fonction de création de liste d'instances existantes :
EntitySpec pES = new EntitySpec(A_CLASS); pES->AssignEntityListInstantiator(myEntityListInstantiator); pES->AssignQuantityEvaluator(myQuantityEvaluator); pEntityTab* myEntityListInstantiator(EntitySpecMethod* pM) { EntitySpec* pES = pM->DescribedEntitySpec(); pEntityTab* result = pES->GetExpansion(); result->erase(); //Entity* pEref = pM->GetEntityArgValue(REFERENCEENTITY_ARGUMENT); UserClassId id = pM->GetIntArgValue(CLASSID_ARGUMENT); int N = pM->GetIntArgValue(LISTSIZE_ARGUMENT); pEntityTab* instanceList = GetEntityList(id); // les instances existantes for(int k=0;k<N;k++) { Entity* pE_k = instanceList->get_nth(k); result->push_back(pE_k); } return result; }Dans ce code ci-dessus, la valeur passée par l'argument de symbole LISTSIZE_ARGUMENT a été établie par le moteur en invoquant la méthode QuantityEvaluator, puisqu'un corps lui a été donné ( par AssignQuantityEvaluator).
Exemple de fonction de création de liste de nouvelles instances :
EntitySpec pES = new EntitySpec(A_CLASS); pES->AssignEntityListInstantiator(myEntityListInstantiator); pES->IntQuantity(3); pEntityTab* myEntityListInstantiator(EntitySpecMethod* pM) { EntitySpec* pES = pM->DescribedEntitySpec(); pEntityTab* result = pES->GetExpansion(); result->erase(); //Entity* pEref = pM->GetEntityArgValue(REFERENCEENTITY_ARGUMENT); UserClassId id = pM->GetIntArgValue(CLASSID_ARGUMENT); int N = pM->GetIntArgValue(LISTSIZE_ARGUMENT); for(int k=0;k<N;k++) { Entity* pE_k = App_NewEntityFromID(id); // création nouvelle instance result->push_back(pE_k); } return result; }Dans le code ci-dessus, la valeur passée par l'argument de symbole LISTSIZE_ARGUMENT résulte du message IntQuantity adressé à pES, avec la valeur 3.
Note importante : dans les deux fonctions de création de liste, ci-dessus, on reconstruit la liste qui est le membre de la classe EntitySpec renvoyé par le service GetExpansion. Avant de la reconstruite, on prend soin de la vider (par erase).