Quick links: Tutorial - Examples - Files - Symbols.
Classes: Hierarchy - Index - List - Members.
Namespaces: Index - base - cs - display.
This page is available in English.

Architecture générale de la bibliothèque

Introduction

Dans cette section, l'architecture générale de la bibliothèque est décrite. Les points plus particulièrement traités concernent la classe cogitant::Environment qui est la première classe à instancier lors de l'utilisation de la bibliothèque. En effet, les instances de cette classe offrent une gestion centralisée du support, de graphes conceptuels et règles, des entrées/sorties et des opérations sur les objets. Ensuite, la classe cogitant::CogitantObject est décrite, cette classe est la racine d'héritage de la plupart des classes de la bibliothèque. Ainsi les fonctionnalités offertes par cette classe (et plus particulièrement la gestion des propriétés) se retrouvent dans la plupart des classes de Cogitant. Enfin, dans un grand nombre de cas, il est nécessaire de manipuler des collections d'objets (ensemble de sommets, ensemble de types de concepts, ensemble de graphes, etc.). Les classes permettant une gestion de ces collections (les classes conteneurs) sont décrites dans le dernier paragraphe de cette section.

Architecture de la bibliothèque

Cogitant fournit une implatantion orientée objets du modèle des graphes conceptuels [Chein et Mugnier, 1992] [Mugnier et Chein, 1996]. À chaque objet du modèle correspond une classe qui offre des méthodes qui correpondent à des opérations élémentaires sur ces objets (cogitant::Support, cogitant::ConceptType, cogitant::Graph, cogitant::Rule, cogitant::Concept, cogitant::Nesting, cogitant::Edge, cogitant::Projection, cogitant::CoreferenceClass, etc.). Les structures de données utilisées pour représenter les objets sont une implantation simple des définitions du modèle. De cette façon, un utilisateur connaissant le modèle peut rapidement comprendre comment les objets sont implantés et réaliser des extensions aux classes fournies avec Cogitant.

Afin d'offrir plus de flexibilité, les opérations complexes du modèle (projection, application de règle, etc.) ne sont pas accessibles directement comme méthodes de la classe cogitant::Graph mais sont disponibles à partir de classes particulières (sous-classes de cogitant::Operation), ce qui permet de redéfinir les traitements effectués pour calculer ces opérations, sans redéfinir les classes qui offrent une représentation des graphes ou règles.

Toutes les classes redéfinissent l'opérateur << sur un ostream, ce qui permet d'afficher rapidement l'état des objets. Cependant l'affichage produit de cette façon ne peut être utilisé pour enregistrer des objets dans des fichiers, car aucun mécanisme de lecture à ce format n'est fourni. Par contre, des opérations d'entrées sorties sont fournies aux formats BCGCT, CoGXML, CGIF, et forme linéaire.

La classe Environment

La classe cogitant::Environment permet la gestion d'un support, de graphes et règles définis sur ce support, un accès facilité aux opérations faisant intervenir supports, graphes et règles, et plus particulièrement les opérations d'entrées sorties et les opérations définies dans le modèle des graphes conceptuels. Il est possible de se passer de cette classe, mais l'utilisation d'un environnement a de nombreux avantages :

Les graphes et le support sur lequel ils sont définis sont regroupés au sein d'une même entité

De cette façon, il est impossible de tenter des opérations incorrectes faisant intervenir des graphes définis sur différents supports ou des graphes définis sur un support donné et un autre support.
Plus précisément, le support est accessible par la méthode cogitant::Environment::support() qui retourne une référence sur le support associé à l'environnement.

Des graphes et règles peuvent être stockés à l'intérieur de l'environnement

De cette façon, la destruction de l'environnement provoque la destruction des graphes, et il est inutile de se préoccuper de désallouer ces graphes un par un.
De plus, la classe Environment étant munie d'un opérateur d'affectation et d'un constructeur par recopie qui effectuent une copie des graphes et règles, il est très simple de travailler sur une copie des graphes.
Un certain nombre de fonctionnalités sont offertes pour parcourir ou rechercher les graphes et règles contenus dans l'environnement.


Les objets mémorisés dans l'environnement peuvent être des graphes conceptuels (cogitant::Graph) ou des règles (cogitant::Rule). Afin de manipuler ces deux types d'objets de façon unifiée, mais aussi parce que ces deux types d'objets ont des points communs, ces deux classes dérivent de cogitant::EnvironmentObject. De cette façon, les objets contenus dans l'environnement sont manipulés comme des pointeurs sur des cogitant::EnvironmentObject. Cet ensemble d'objets mémorisés est accessible par la méthode cogitant::Environment::objects().
À chaque objet mémorisé dans l'environnement est associé un identificateur unique (cogitant::iSet). Cet identificateur, attribué à la création de l'objet, permet d'y accéder en temps constant. Ainsi, la méthode cogitant::Environment::objects(iSet) retourne l'objet correspondant à l'identificateur passé en paramètre. Toutefois, cette méthode retourne un pointeur sur un cogitant::EnvironmentObject, et il n'est donc pas possible d'appeler directement les méthodes spécifiques à cogitant::Graph ou cogitant::Rule sur la valeur retournée. Il est certes possible d'utiliser une conversion de type si l'utilisateur est certain que l'objet en question est soit un graphe, soit une règle, mais il est préférable d'utiliser les méthodes de conversion cogitant::EnvironmentObject::asGraph() et cogitant::EnvironmentObject::asRule() qui provoquent une exception si la conversion est incorrecte (voir Gestion d'erreur). Il existe toutefois une méthode plus simple d'accéder aux objets directement en tant que graphes ou règles : les méthodes cogitant::Environment::graphs(iSet) et cogitant::Environment::rules(iSet) retournent respectivement un pointeur sur un graphe et un pointeur sur une règle. Évidemment, si l'identificateur passé ne correspond pas à un graphe (resp. une règle), une exception est levée.
La création de nouveaux graphes ou règles dans l'environnement se fait en appelant les méthodes cogitant::Environment::newGraph() et cogitant::Environment::newRule() qui créent des objets vides et retournent l'identificateur attribué à l'objet créé. Il est possible aussi de créer un graphe (resp. une règle) à partir d'un graphe (resp. une règle) existant en appelant la méthode cogitant::Environment::newGraph(iSet,bool) (resp. cogitant::Environment::newRule(iSet)) qui crée une copie de l'objet dont l'identificateur est passé en paramètre et retourne l'identificateur de l'objet créé.


Dans certains cas, il est préférable de ne pas mémoriser les graphes manipulés dans l'environnement, plus particulièrement dans le cas où il s'agit de graphes temporaires utilisés dans un calcul. Pour cela, la méthode cogitant::Environment::newExternalGraph() retourne un pointeur sur un graphe vide, créé par l'appel à cette méthode. Évidemment, un tel graphe devra être explicitement détruit (par delete) car il n'est pas attaché à l'environnement. Par contre, il est interdit de détruire par delete les graphes ou règles contenus dans l'environnement : pour détruire de tels objets, il est nécessaire d'appeler la méthode cogitant::Environment::deleteObject(iSet), cogitant::Environment::deleteGraph(iSet) ou cogitant::Environment::deleteRule(iSet). Enfin, les méthodes cogitant::Environment::addObject(EnvironmentObject*) et cogitant::Environment::detachObject(iSet) permettent respectivement d'ajouter à l'environnement un object qui avait auparavant été créé comme externe à l'environnement et de sortir de l'environnement un objet qui avait été créé à l'intérieur.

Exemple. Création d'un environnement, ajout d'un type de concept au support, création d'un graphe composé d'un sommet concept, copie de ce graphe dans l'environnement. La copie est ensuite extraite de l'environnement, pour être manipulée comme un objet dynamique, qui doit donc être détruit explicitement.

int main(int, char* [])
{
// Adding a concept type
env.support()->newConceptType("Person");
// Creating a new empty graph in the environment, identified by g1
// Adding a generic concept vertex (of type Person) to this graph
env.graphs(g1)->newGenericConcept("Person");
// Copying this graph in a new graph, identified by g2
cogitant::iSet g2 = env.newGraph(g1);
// g2 identifies a graph of the environment. This graph is now
// detached from the environment and is identified by g.
// g is not in the environment, it must therefore be manually deleted.
// On the contrary g1 is automatically deleted when the
// Environment is deleted.
delete g;
return 0;
}
Voir également
Manipulation de graphes conceptuels, Manipulation de règles et contraintes.

L'environnement offre une gestion simplifiée des entrées / sorties

Un certain nombre de méthodes sont offertes qui permettent la lecture et l'écriture de supports, graphes et règles en différents formats. Le format utilisé est choisi en fonction de l'extension du fichier lu ou écrit.
Les entrées/sorties sont gérées par l'intermédiaire d'une instance de cogitant::IOHandler, incluse dans l'environnement. Toutefois, dans le cadre d'une utilisation classique de la bibliothèque, l'utilisateur n'a pas à se préoccuper de cette classe, et peut utiliser directement les méthodes de l'environnement qui offrent des fonctionnalités d'entrées/sorties. À noter que l'utilisation de la classe IOHandler est nécessaire pour rajouter des fonctionnalités de lecture / écriture dans de nouveaux formats.
La lecture d'un support à partir d'un fichier se fait par la méthode cogitant::Environment::readSupport() à laquelle est passée une chaîne de caractères contenant le nom du fichier à charger. Ce fichier peut être interprété de différentes façons selon son extension : par défaut, CoGITaNT fournit une lecture de supports aux formats BCGCT (extension bcg, bcs), CoGXML (extension xml, cogxml). Si l'extension n'est pas reconnue, le fichier est interprété comme étant au format BCGCT. Si le fichier ne respecte pas le format ou contient des données qui ne respectent pas le modèle des graphes conceptuels, une exception est levée (voir Gestion d'erreur).
La lecture de graphes ou de règles est effectuée par la méthode cogitant::Environment::readGraphs(). Cette méthode prend comme paramètre une chaîne de caractères contenant le nom du fichier à charger. Un deuxième paramètre optionnel est un pointeur sur un vector<iSet> : si un pointeur non NULL est passé, ce vecteur contiendra après l'appel les identificateurs des objets qui auront été chargés à partir du fichier choisi. Dans le cas où le fichier chargé ne contient qu'un seul graphe, il est plus simple d'utiliser la valeur de retour de cette méthode, qui est l'identificateur du premier graphe chargé à partir du fichier (voir l'exemple ci-dessous). Par défaut, l'environnement fournit des fonctionnalités de lecture de graphes aux formats BCGCT, CoGXML et CGIF (2001, Core, Extended, extension cgif et cgf).


L'écriture du support se fait par l'appel à la méthode cogitant::Environment::writeSupport(), qui admet un paramètre, une chaîne de caractères contenant le nom du fichier à générer. Si ce fichier existe, il est écrasé par le support. Les formats reconnus par défaut pour l'écriture du support sont THC, BCGCT, CoGXML et CGIF (2001 et extended).
L'écriture de graphes (et règles) se fait par l'appel à la méthode cogitant::Environment::writeGraphs() et cogitant::Environment::writeGraph(). Ces deux méthodes prennent comme premier paramètre un nom de fichier, et comme second paramètre, respectivement un vector<iSet> contenant les identificateurs des objets à sauvegarder, et un iSet correspondant à l'identificateur de l'objet à sauvegarder. Par défaut, la sauvegarde peut être faite en format BCGCT, au format CoGXML (extension xml ou cogxml), au format CGIF (2001, Core, Extended) ou sous la forme linéaire (extension lin ou lf). Attention, seuls les formats BCGCT et CoGXML permettent de stocker la totalité des propriétés associées aux objets manipulés en mémoire : les propriétés ne sont pas sauvegardées en CGIF et forme linéaire.

Exemple. L'exemple ci-dessous illustre le chargement d'un support et d'un graphe au format BCGCT, l'affichage du graphe chargé à l'écran et la sauvegarde de ce graphe au format linéaire.

#include <iostream>
using namespace std;
int main(int, char*[])
{
env.readSupport("bcgct/bucolic/bucolic.bcs");
cogitant::iSet g1 = env.readGraphs("bcgct/bucolic/fact.bcg");
cout << *(env.graphs(g1)) << endl;
env.writeGraph("tmp.lin", g1);
return 0;
}

Pour afficher des graphes à l'écran afin de vérifier que ces graphes sont conformes à ce que l'on pense avoir créé, il est possible d'appeler l'opérateur de sortie de la classe cogitant::Graph, qui affiche la "forme interne" du graphe qui est certes précise car les identificateurs des sommets et la structure interne de l'objet sont représentés, mais qui n'est pas toujours très simple à interpréter. C'est pourquoi la méthode cogitant::Environment::writeGraph(ostream&,iSet,IOHandler::Format) peut être préférée à l'opérateur de sortie. Cette méthode prend comme paramètre un flux de sortie (habituellement cout), l'identificateur d'un graphe, et un format de fichier parmi ceux connus de la plate-forme. Si ce format n'est pas précisé, la sortie est effectuée en format linéaire qui est souvent le plus simple à interpréter dans le cas de petits graphes (non emboîtés), mais il est possible de préciser le format de sortie, en passant par exemple comme paramètre cogitant::IOHandler::BCGCT.

Exemple. Dans l'exemple ci-dessous, un support et un graphe sont chargés, puis le graphe est affiché sous forme interne, forme linéaire, BCGCT et CoGXML.

#include <iostream>
using namespace std;
int main(int, char* [])
{
env.readSupport("bcgct/bucolic/bucolic.bcs");
cogitant::iSet g1 = env.readGraphs("bcgct/bucolic/sleepandfish.bcg");
cout << "-- Internal data structures --" << endl;
cout << *(env.graphs(g1)) << endl;
cout << "-- Linar form --" << endl;
env.writeGraph(cout, g1);
cout << "-- BCGCT format --" << endl;
cout << "-- CoGXML format --" << endl;
cout << "-- CGIF format --" << endl;
return 0;
}
Voir également
Opérations d'entrées/sorties

Les opérations habituelles du modèle sont offertes à partir de l'environnement

Les opérations du modèle des graphes conceptuels (projection, application de règle, etc.) sont disponibles à travers des sous-classes de cogitant::Operation. Pour plus de détails concernant les opérations, et la façon de définir de nouvelles opérations, ou modifier les opérations existantes, consulter la section Opérations. Cependant, afin de fournir un mode d'utilisation simplifié, les principales opérations sont aussi disponibles à partir de méthodes de la classe environnement.
Ainsi, la méthode cogitant::Environment::projections(iSet,iSet,ResultOpeProjection&) calcule les projections entre deux graphes repérés par leurs identificateurs (projections du premier dans le second). Le résultat est stocké dans l'instance de cogitant::ResultOpeProjection passée. L'utilisation de cette classe (plutot qu'un simple ensemble de cogitant::Projection) pour le stockage du calcul des projections permet de configurer le type de recherche : en appelant les méthodes cogitant::ResultOpeProjection::memoProjections(bool), cogitant::ResultOpeProjection::maxSize(nSet) avant l'appel à cogitant::Environment::projections(), il est possible de choisir si seule l'existence de projections doit être recherchée, si leur nombre doit être déterminé, ou, enfin, si les projections elles-mêmes doivent être calculées et mémorisées. Voir aussi Calcul des projections.

Exemple. Le programme ci-dessous charge deux graphes et calcule les projections du premier dans le second. Le nombre de projections trouvées est affiché.

#include <iostream>
using namespace std;
int main(int, char* [])
{
env.readSupport("bcgct/bucolic/bucolic.bcs");
cogitant::iSet g1 = env.readGraphs("bcgct/bucolic/simplequery.bcg");
cogitant::iSet g2 = env.readGraphs("bcgct/bucolic/fact.bcg");
env.projections(g1, g2, proj);
cout << proj.size() << endl;
return 0;
}

D'autres opérations peuvent être calculées à partir de l'environnement :

Ces méthodes prennent comme paramètres des pointeurs sur des graphes ou règles, de cette façon, il est possible de les utiliser aussi bien sur des objets contenus dans l'environnement que sur des objets externes à l'environnement (mais définis sur le même support). Ainsi, pour les objets contenus dans l'environnement, il est nécessaire d'appeler cogitant::Environment::graphs(iSet) ou cogitant::Environment::rules(iSet) pour obtenir un pointeur sur les objets à partir de leurs identificateurs.

Voir également
Opérations

Objets et propriétés

La plupart des classes de la bibliothèque dérivent de cogitant::CogitantObject. Cette classe abstraite fournit un certain nombre de fonctionnalités dont la principale est une gestion des propriétés associées aux objets.

À chaque objet est en effet associé un ensemble de propriétés (cogitant::PropertySet) qui est un ensemble de couples (type de la propriété, valeur). Les ensembles de propriétés permettent entre autres de stocker le type d'un sommet concept, l'intitulé d'un type d'emboîtement, la signature d'un type de relation, ou toute autre information car les propriétés ne sont pas limitées à des valeurs prédéfinies. L'ensemble des propriétés associées à un objet est disponible à travers la méthode cogitant::CogitantObject::properties(). À partir d'une référence sur l'ensemble, il est possible d'ajouter des propriétés (cogitant::PropertySet::set()), de rechercher une propriété par son type (cogitant::PropertySet::read(), cogitant::PropertySet::get()) ou de supprimer une propriété (cogitant::PropertySet::remove()).

Plus précisément, un ensemble de propriétés est composé de plusieurs instances de cogitant::Property, chacune d'entre elles a un membre appelé type (cogitant::Property::type()), cette variable est un Property::Type qui repère par exemple le fait qu'il s'agit du nom d'un graphe (cogitant::Property::GRAPH_NAME) ou des coordonnées d'un objet (cogitant::Property::DISPLAY_POS_X). Évidemment, une propriété associe aussi une valeur à ce type, et la variable représentant cette valeur peut être de différents types (cogitant::Property::valueType()) : unsigned int, void*, string ou vector<iSet>. La valeur elle même est accessible par cogitant::Property::getInt(), cogitant::Property::getPtr(), cogitant::Property::getString() ou cogitant::Property::getVectorISet(). Cependant, dans le cadre d'une utilisation classique des ensembles de propriétés, il n'est pas nécessaire d'utiliser directement les méthodes de la classe cogitant::Property, mais seulement les méthodes de cogitant::PropertySet.

Exemple. Le programme ci-dessous crée un cogitant::Environment (qui est une sous-classe de cogitant::CogitantObject) et lui associe une propriété de type cogitant::Property::COMMENT de valeur "ENV". Cette propriété est ensuite cherchée et affichée, avant d'être supprimée. Enfin, une propriété ne correspondant à aucun type prédéfini est ajoutée à l'objet. De telles propriétés sont repérées par une chaîne de caractères.

#include <iostream>
using namespace std;
int main(int, char* [])
{
env.properties()->set("NEW_PROPERTY_TYPE", "A_VALUE");
return 0;
}

L'autre fonctionnalité offerte par cogitant::CogitantObject est utile pour la mise au point de programmes : il s'agit de deux méthodes retournant respectivement le nom de la classe de l'objet (cogitant::CogitantObject::className()) et une forme de l'objet pouvant être affichée (cogitant::CogitantObject::toString()). Le nom de la classe est retourné grâce aux fonctionnalités RTTI (run-time type information) de C++, et il est donc inutile de redéfinir cette méthode dans les sous-classes. La méthode toString, qui retourne par défaut le nom de la classe peut être redéfinie pour retourner une représentation textuelle de l'objet en question. De cette façon, tous les objets étant munis de cette méthode, il est très simple d'afficher les caractéristiques des objets pour la mise au point de programmes. De plus, les classes de la plateforme redéfinissent l'opérateur de sortie sur un flux qui appelle la méthode toString, ce qui permet d'afficher très simplement la chaîne de caractères associée à chaque objet.

Exemple. Le programme ci-dessous crée un cogitant::Environment, et ajoute un type de concept "Person" à l'ensemble des types de concepts. Un graphe comportant un seul sommet concept, de type "Person", est ensuite ajouté à l'environnement. L'environnement et le graphe sont ensuite affichés.

#include <iostream>
using namespace std;
int main(int, char* [])
{
env.support()->newConceptType("Person");
env.graphs(g1)->newGenericConcept("Person");
cout << env << endl;
cout << *(env.graphs(g1)) << endl;
return 0;
}

L'exécution de ce programme provoque l'affichage suivant (qui varie selon le compilateur utilisé, car le RTTI n'est pas défini de la même façon dans tous les compilateurs, en particulier pour ce qui concerne l'inclusion du namespace dans le nom de la classe) :

Q28cogitant11Environment
Q28cogitant7Support
ConceptTypes : 0:Person
PartialOrder : (0 ())
RelationTypes :
PartialOrder :
NestingTypes :
PartialOrder :
Individuals :
Q28cogitant5Graph
Nodes : 0:Q28cogitant13InternalGraph, 1:Q28cogitant7Concept 0
Edges :
0:(P *)(C 1)
1:(P 0)

Pour interpréter cet affichage, il est nécessaire de connaître la structure du support et des graphes (décrite plus loin), mais on peut remarquer que la chaîne associée à l'environnement contient une description du support et que cette description liste les ensembles contenus. On peut plus particulièrement remarquer que l'ensemble des types de concepts contient un type, d'identifiant 0 et d'intitulé Person. Quant au graphe, il est composé de noeuds et d'arêtes, et plus particulièrement de deux noeuds, le premier, d'identificateur 0 est un cogitant::InternalGraph et le second, d'identificateur 1 est un cogitant::Concept, le 0 qui suit est l'identifiant du type du sommet concept : ici le type de concept 0, c'est à dire Person.

Les classes conteneurs

Dans le modèle des graphes conceptuels, de nombreux objets sont définis à partir d'ensembles. Ainsi, un graphe conceptuel simple est formé d'un ensemble de sommets concepts et d'un ensemble de sommets relations, mais aussi d'un ensemble d'arêtes. Le support est quant à lui formé d'un ensemble de types de concepts, d'un ensemble de marqueurs individuels, etc. Pour fournir une implantation du modèle agréable à utiliser, il est donc nécessaire de définir une classe "ensemble" pouvant être utilisée dans différentes occasions. Contrairement à un langage en vogue qui ne permet que de définir des conteneurs d'Object et impose ainsi un abus de conversions explicites, C++ autorise la définition de classes réellement génériques. C'est cette solution qui a été retenue dans la classe cogitant::Set qui est une classe générique (prononcer "<em>template</em>") dont l'utilisation est inspirée des classes de la bibliothèque standard.

La classe cogitant::Set est en fait une classe abstraite qui fournit une interface à la structure de données "ensemble". En effet, le concept d'ensemble pouvant être utilisé dans diverses occasions, il est difficile de définir une implantation de la classe parfaitement adaptée à tous les usages : nous avons préféré définir une classe abstraite, dont les structures de données et méthodes peuvent être concrétisées de différentes façons. Évidemment, une classe concrète dérivant de cogitant::Set est fournie avec la bibliothèque, il s'agit de la classe cogitant::Set_Simple qui fournit une implantation simple d'ensemble. Dans cette classe, un ensemble d'éléments est composé d'un vector d'éléments. L'inconvénient de cette implantation est le redimensionnement nécessaire de la structure de données quand de nombreux ajouts sont faits (ces redimensionnements sont toutefois effectués de manière transparente à l'utilisateur de la classe, peuvent être limités par l'appel à cogitant::Set::reserve(), et sont faits "par bloc" : ce n'est pas à chaque ajout que le vecteur est redimensionné). Une telle implantation a toutefois de nombreux avantages : l'espace mémoire nécessaire est nettement inférieur à celui d'une liste, et les temps de traitements effectifs sont très satisfaisants (car la plupart des opérations ne font pas appel à de l'allocation dynamique, alors que c'est le cas pour l'ajout ou la suppression d'un élément à une liste).

Les éléments d'un ensemble sont tous repérés par un identificateur unique de type cogitant::iSet (il s'agit du même type que celui utilisé pour identifier les graphes et règles contenus dans un environnement, tout simplement parce qu'un environnement contient un Set<EnvironmentObject>). L'identificateur associé à un élément peut être obtenu par la méthode cogitant::Set::iAdd() qui prend comme paramètre un objet, l'ajoute à l'ensemble et retourne l'identificateur associé à cet objet. Évidemment, la classe étant définie par un template, le compilateur vérifie la validité des appels de méthodes : des opérations incorrectes tels que l'ajout d'un type de concept à un graphe provoquent une erreur de compilation. À partir d'un identificateur, il est possible d'accéder à l'élément (cogitant::Set::iGetContent() ou en utilisant cogitant::Set::operator[](), voir l'exemple ci-dessous) en lecture et en écriture, et supprimer l'élément de l'ensemble (cogitant::Set::iDelete()).

Exemple. Pour instancier explicitement un ensemble, il est impossible d'utiliser la classe Set (abstraite) mais il faut choisir la classe concrète correspondante (cogitant::Set_Simple, penser au fichier en-tête correspondant). Cet exemple illustre l'utilisation d'un ensemble de pointeurs sur des types de concepts : un premier type est créé dynamiquement et ajouté à l'ensemble, son identificateur est stocké dans i (affiché), un second type est ajouté mais son identificateur n'est pas mémorisé. Cet identificateur est toutefois retrouvé par l'appel à cogitant::Set::iFind(). Enfin, la dernière ligne illustre l'accès à un élément à partir de son identificateur.

#include <iostream>
using namespace std;
using namespace cogitant;
int main(int, char* [])
{
iSet i = s.iAdd(new ConceptType());
cout << i << endl;
ConceptType* ct2 = new ConceptType();
s.iAdd(ct2);
iSet j = s.iFind(ct2);
cout << s[j];
return 0;
}

Une fonctionnalité habituellement très utilisée sur les structures de données est le parcours. Avec la classe Set, il y a deux façons de parcourir les éléments contenus : la première utilise les identificateurs (cogitant::iSet), et la seconde utilise des itérateurs, dont l'usage est très proche des itérateurs de la bibliothèque standard. Un parcours avec les identificateurs se fait avec les méthodes cogitant::Set::iBegin() qui retourne l'identificateur du premier élement de l'ensemble, cogitant::Set::iNext() qui incrémente l'identificateur passé en paramètre et cogitant::Set::iEnd() qui retourne un identificateur incorrect (correspondant à l'élément "suivant" le dernier).

Exemple. Chargement d'un support et affichage de l'intitulé de tous les types de concepts chargés.

#include <iostream>
using namespace std;
using namespace cogitant;
int main(int, char* [])
{
e.readSupport("bcgct/bucolic/bucolic.bcs");
for (iSet i = TC->iBegin(); i != TC->iEnd(); TC->iNext(i))
cout << TC->iGetContent(i)->label() << endl;
return 0;
}

L'utilisation d'itérateurs avec la classe cogitant::Set se rapproche des structures de données de la bibliothèque standard, et permet aussi un parcours sélectif de certains éléments (uniquement les sommets concepts parmi tous les élements d'un graphe, par exemple) qui est détaillé dans les parties suivantes. Pour un parcours avec des itérateurs, il est nécessaire d'utiliser les méthodes cogitant::Set::begin() et cogitant::Set::end(). L'itération se fait par l'opérateur ++ de l'itérateur (cogitant::SetIterator::operator++()) et l'accès à l'élément courant se fait par l'utilisation de l'opérateur de déréferencement (cogitant::SetIterator::operator*()) de l'itérateur comme avec la bibliothèque standard.

Exemple. Chargement d'un support et affichage de l'intitulé de tous les types de concepts chargés.

#include <iostream>
using namespace std;
using namespace cogitant;
int main(int, char* [])
{
e.readSupport("bcgct/bucolic/bucolic.bcs");
for (Set<ConceptType*>::const_iterator i = TC->begin(); i != TC->end(); i++)
cout << (*i)->label() << endl;
return 0;
}