H.S. Idée pour créer une sorte de "pont" entre deux interfaces.

Le
Stephane Wirtel
Bonjour,

J'ai une idée afin de pouvoir me simplifier un peu la vie avec Borland C++
Builder.

Pour ceux qui emploit cet outil, il y a une TList qui fait office de list
d'éléments.
Le soucis est qu'elle ne possède pas une interface qui me permettrait de
l'utiliser
avec les algorithmes de la STL.

Donc en gros, comment pourrais-je rajouter les iterator à une TList ?

Voici ce que cela me permettrait de faire.

TList *list (new TList ());

std::copy (
list->begin (),
list->end (),
std::ostream_iterator< SUPER_TYPE > (std::cout, " "));


Merci,

Stéphane
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
kanze
Le #272490
Stephane Wirtel wrote:

J'ai une idée afin de pouvoir me simplifier un peu la vie avec
Borland C++ Builder.


Ou n'importe quelle bibliothèque un peu ancienne. (J'ai
rencontré un problème semblable avec XViews.)

Pour ceux qui emploit cet outil, il y a une TList qui fait
office de list d'éléments. Le soucis est qu'elle ne possède
pas une interface qui me permettrait de l'utiliser avec les
algorithmes de la STL.

Donc en gros, comment pourrais-je rajouter les iterator à une
TList ?


Le modèle fassade. Je ne connais pas TList, mais j'imagine qu'il
offre un itérateur classique. Je connais deux solutions
possible : une fassade sur la classe même (qui peut être utile
dans certains cas) et une fassade sur son itérateur.

Pour la fassade sur l'itérateur, boost::iterator doit aider
beaucoup. En fait, ce que je fais sur l'itérateur, ce n'est pas
une vraie fassade, parce que les itérateurs doivent avoir une
sémantique de valeur, ce qui n'est pas le cas d'une vraie
fassade. Mais il revient à peu près au même : mon itérateur STL
contient l'itératuer du TList. (Attention quand même : si
l'itérateur classique n'a pas une sémantique de valeur, il va
falloir que tu lui force une copie profonde.)

Voici ce que cela me permettrait de faire.

TList *list (new TList ());

std::copy (
list->begin (),
list->end (),
std::ostream_iterator< SUPER_TYPE > (std::cout, " "));


Ça serait :

std::copy( TListIterator( list ),
TListIterator(),
std::ostream_iterator
Voir Boost::iterator.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Stephane Wirtel
Le #273333
kanze said the following on 17/01/2006 17:23:
Pour ceux qui emploit cet outil, il y a une TList qui fait
office de list d'éléments. Le soucis est qu'elle ne possède
pas une interface qui me permettrait de l'utiliser avec les
algorithmes de la STL.

Donc en gros, comment pourrais-je rajouter les iterator à une
TList ?


Le modèle fassade. Je ne connais pas TList, mais j'imagine qu'il
offre un itérateur classique. Je connais deux solutions
possible : une fassade sur la classe même (qui peut être utile
dans certains cas) et une fassade sur son itérateur.
Non, elle ne propose pas d'interface permettant d'utiliser directement un

itérateur classique.

Elle contient une méthode First() et Last(), par contre la collection
qui est encapsulée dans cette classe contient une surcharge de l'opérateur[]
pour avoir un 'pseudo' accès direct.

Pour la fassade sur l'itérateur, boost::iterator doit aider
beaucoup. En fait, ce que je fais sur l'itérateur, ce n'est pas
une vraie fassade, parce que les itérateurs doivent avoir une
sémantique de valeur, ce qui n'est pas le cas d'une vraie
fassade. Mais il revient à peu près au même : mon itérateur STL
contient l'itératuer du TList. (Attention quand même : si
l'itérateur classique n'a pas une sémantique de valeur, il va
falloir que tu lui force une copie profonde.)

Voici ce que cela me permettrait de faire.

TList *list (new TList ());

std::copy (
list->begin (),
list->end (),
std::ostream_iterator< SUPER_TYPE > (std::cout, " "));


Ça serait :

std::copy( TListIterator( list ),
TListIterator(),
std::ostream_iterator

De quelle manière std::copy saurait-il que le second argument
'TListIterator()' représente end () ?

Voir Boost::iterator.
Merci, je vais aller regarder tout de suite.


En fait d'après les tests d'un collègue, celui-ci a constaté que
l'implémentation TList
de Borland était presque 6 fois plus lente que l'implémentation de la STL.

Le pattern Façade semble intéressant après discuter avec mon collègue.

Encore Merci,

Stéphane


Fabien LE LEZ
Le #273319
On Wed, 18 Jan 2006 09:16:04 +0100, Stephane Wirtel

celui-ci a constaté que
l'implémentation TList
de Borland était presque 6 fois plus lente que l'implémentation de la STL.


Si elle offre un opérateur [], c'est normal : un [] rapide est
incompatible avec une liste chaînée.

loic.actarus.joly
Le #273292

Ça serait :

std::copy( TListIterator( list ),
TListIterator(),
std::ostream_iterator

Tu utilises là une syntaxe proche des stream_iterator. Une autre
syntaxe me semble possible, semblable à ce qui peut être fait pour
des tableaux natifs :

std::copy( begin( list ),
end(list),
std::ostream_iterator
--
Loïc

Stephane Wirtel
Le #273264
Fabien LE LEZ said the following on 18/01/2006 10:48:
On Wed, 18 Jan 2006 09:16:04 +0100, Stephane Wirtel

celui-ci a constaté que
l'implémentation TList
de Borland était presque 6 fois plus lente que l'implémentation de la STL.


Si elle offre un opérateur [], c'est normal : un [] rapide est
incompatible avec une liste chaînée.

Justement, c'est pour cela que j'aimerais régler le problème.


A la fin, j'ai créé une classe qui hérite de std::deque et qui encapsule une
TList
et qui fait une copie du pointeur dans un std::deque.

De cette manière, je profite d'une collection de la STL.

En gros, le code donne ceci.

struct NotOwner {
template< typename T> void operator () (T ptr) {
}
};

struct Owner {
template< typename T > void operator () (T ptr) {
delete ptr;
ptr = 0;
}
};

template <
typename ParamType,
typename Policy = NotOwner >
class DsList :
public std::deque< ParamType >
{
public:
DsList (TList *pList = 0);
TList *transformToTList () throw (Exception, std::exception);
virtual void clear ();
virtual ~DsList ();

private:
template< typename Param_FirstType, typename Param_SecondType > static
bool Compare () {
return (typeid (Param_FirstType) == typeid (Param_SecondType));
};

TList *mList;
};


template< typename ParamType, typename Policy >
DsList< ParamType, Policy >::DsList (TList *pList)
: mList (pList)
{
if (pList != 0) {
for (unsigned long int i = 0, count = pList->Count; i < count; ++i) {
this->push_back (static_cast< ParamType > (pList->Items[i]));
}
}
}

template< typename ParamType, typename Policy >
TList * DsList< ParamType, Policy >::transformToTList ()
throw (Exception, std::exception)
{
std::auto_ptr< TList > tlist (new TList ());
for (DsList< ParamType > :: const_iterator it = this->begin (); it ! this->end (); ++it) {
tlist->Add (*it);
}
return tlist.release ();
}

/**
* Efface le contenu de la std::deque.
* Si la policy concernant le propriétaire du contenu est 'Owner',
* libération des ressources mémoires complètes.
*/
template< typename ParamType, typename Policy >
void DsList<ParamType, Policy>::clear ()
{
if (Compare<Policy, Owner> () == true) {
std::for_each (this->begin (), this->end (), Policy ());
mList->Clear ();
}
std::deque< ParamType >::clear ();
}

/**
* Destructeur de DsList.
*/
template< typename ParamType, typename Policy >
DsList<ParamType, Policy>::~DsList ()
{
if (Compare<Policy, Owner> () == true) {
std::for_each (this->begin (), this->end (), Policy ());
mList->Clear ();
}
}


Et son utilisation se fait de cette manière.
struct StructForTest {
AnsiString first_field;
int second_field;
StructForTest (AnsiString pFirstField = "", int pSecondField = 0) throw
(Exception)
: first_field (pFirstField), second_field (pSecondField)
{
}
friend std::ostream & operator << (std::ostream &outStream, const
StructForTest &pTest);
};

std::ostream & operator << (std::ostream &outStream, const StructForTest &pTest) {
outStream
<< "First Field : " << pTest.first_field.c_str () << std::endl
<< "Second Field : " << pTest.second_field << std::endl;
return outStream;
}

int main(int argc, char* argv[])
{
std::auto_ptr< TList > borlandList;
try {
borlandList.reset (new TList ());
borlandList->Add (new StructForTest ("Toto", 0));
borlandList->Add (new StructForTest ("Stef", 1));
std::cout << borlandList->Count << std::endl;

{
/// ce bloc a été créé pouvoir si la destruction de la DsList
/// libérait bien la mémoire de la TList.

DsList< StructForTest *, Owner > list (borlandList.get ());
DsList< StructForTest *, Owner > :: const_iterator it;
for ( it = list.begin (); it != list.end (); ++it) {
std::cout << **it << std::endl;
}
}
std::cout << borlandList->Count << std::endl;

}
catch (const Exception &pException) {
std::cerr << "Exception : " << pException.Message << std::endl;
}
catch (const std::exception &pException) {
std::cerr << "Exception : " << pException.what () << std::endl;
}
catch (...) {
std::cerr << "Exception : Inconnue" << std::endl;
}
std::cin.get ();
return 0;
}



Qu'en pensez-vous ?
Je suis à l'écoute de toutes remarques et conseils, ceci afin de m'améliorer.

Merci de votre aide,

Stéphane


kanze
Le #273241
wrote:

Ça serait :

std::copy( TListIterator( list ),
TListIterator(),
std::ostream_iterator

Tu utilises là une syntaxe proche des stream_iterator.


Ce n'est pas une question de syntaxe, mais de semantique.

Une autre syntaxe me semble possible, semblable à ce qui peut
être fait pour des tableaux natifs :

std::copy( begin( list ),
end(list),
std::ostream_iterator

Le problème, c'est, donné un itérateur classique, comment
implémenter le == dont la STL a besoin.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Loïc Joly
Le #273154
wrote:

Une autre syntaxe me semble possible, semblable à ce qui peut
être fait pour des tableaux natifs :



std::copy( begin( list ),
end(list),
std::ostream_iterator


Le problème, c'est, donné un itérateur classique, comment
implémenter le == dont la STL a besoin.


Excuse moi, je ne vois pas trop de quoi tu parles. Tu peux expliciter ?

--
Loïc


kanze
Le #273095
Loïc Joly wrote:
wrote:

Une autre syntaxe me semble possible, semblable à ce qui
peut être fait pour des tableaux natifs :

std::copy( begin( list ),
end(list),
std::ostream_iterator

Le problème, c'est, donné un itérateur classique, comment
implémenter le == dont la STL a besoin.


Excuse moi, je ne vois pas trop de quoi tu parles. Tu peux
expliciter ?


Prenons un itérateur classique. On a, grosso modo, trois
fonctions : appelons-les next(), courant() et isDone(). Mettons
que l'itérateur utilise un sémantique de valeur, ce qui facilite
le travail. Notre mission, c'est de créer une façade qui permet
à l'utiliser avec la STL. Quelque chose du genre :

template< typename ClassicalIterator >
class STLIterator
{
public:
typedef std::iterator_traits< ClassicalIterator >::value_type
value_type ;
// ...
typedef std::forward_iterator_tag
iterator_category ;

STLIterator( ClassicalIterator init ) ;
value_type& operator*() const ;
STLIterator& operator++() ;
STLIterator operator++( int ) ;
bool operator==( STLIterator const& other) const
;
bool operator!=( STLIterator const& other) const
;

private:
ClassicalIterator myImpl ;
} ;

L'implémentation de l'operator*() et les operator++() doivent
être évident à n'importe qui :

template< typename ClassicalIterator >
STLIterator< ClassicalIterator >::value_type&
STLIterator< ClassicalIterator >::operator*() const
{
return myImpl.current() ;
}

template< typename ClassicalIterator >
STLIterator< ClassicalIterator >&
STLIterator< ClassicalIterator >::operator++()
{
myImpl.next() ;
return *this ;
}

template< typename ClassicalIterator >
STLIterator< ClassicalIterator >
STLIterator< ClassicalIterator >::operator++( int )
{
STLIterator result( *this ) ;
next() ;
return result ;
}

Ce qui n'est pas évident, c'est comment implémenter == et !=.
Parce qu'on ne peut pas comparer deux itérateurs classiques.
(C'est peut-être un défaut des itérateurs classiques, mais c'est
comme ça.) En plus, donné une collection qui ne supporte que
l'itération classique, ce n'est pas sûr qu'on peut obtenir un
itérateur de fin.

La solution que j'utilise, c'est de bien créer un itérateur
quelque part où isDone() est vrai. Si c'est moi l'auteur de
l'itérateur classique, c'est ce que donne le constructeur par
défaut, et c'est trivial. Sinon, et qu'il n'y a pas de
constructeur par défaut, on peut toujours créer une collection
vide (statique), et en utiliser l'itérateur qu'on en obtient.
C'est ce qui me sert de l'itérateur de fin.

Ensuite, je m'assure que tous les itérateurs où isDone() renvoie
faux compare égaux. Quelque chose du genre :

template< typename ClassicalIterator >
bool
STLIterator< ClassicalIterator >::operator==(
STLIterator const& other ) const
{
return isDone() == other.isDone()
&& ( ! isDone() || &current() == &other.current() ) ;
}

Dans la pratique, j'ai souvent laissé tomber la deuxième ligne
de l'expression, et j'ai renvoyé simplement isDone() ==
other.isDone(). Formellement, ça viole les contraintes d'un
forward iterator, mais pratiquement, je constate que chaque fois
que l'algorithme exige un forward iterator, plutôt que
simplement un input iterator, c'est uniquement parce qu'il copie
l'itérateur pour récommencer plus tard au même endroit, et que
toutes les comparaisons portent en fait sur la fin.

Enfin, il y a deux variants des itérateurs classiques qui
peuvent poser des problèmes. Le premier, c'est que l'itérateur
fusionne la fonction next() et la fonction currant() en une
fonction qui renvoie la valeur courante ET avance l'itérateur.
C'était le cas des itérateurs de l'USL, et c'est le cas des
itérateurs en Java. Jusqu'ici, je n'ai pas eu à contorner ce
problème en C++, mais je sais qu'en Java, ça a rendu
l'implémentation des itérateurs filtrants (un autre type de
façade) nettement plus difficile. L'autre, c'est que l'itérateur
n'a pas une sémantique de copie. C'est le cas des itérateurs de
l'OSE, par exemple -- heureusement, dans leurs cas, il y a bien
une fonction clone() qui fait une copie profonde, et qui peut
servir ici dans le constructeur de copie et l'affectation.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Gabriel Dos Reis
Le #273050
"kanze"
| Loïc Joly wrote:
| > > wrote:
|
| > >>Une autre syntaxe me semble possible, semblable à ce qui
| > >>peut être fait pour des tableaux natifs :
|
| > >>std::copy( begin( list ),
| > >> end(list),
| > >> std::ostream_iterator |
| > > Le problème, c'est, donné un itérateur classique, comment
| > > implémenter le == dont la STL a besoin.
|
| > Excuse moi, je ne vois pas trop de quoi tu parles. Tu peux
| > expliciter ?
|
| Prenons un itérateur classique.


Il me sembe avoir au moins autant de définition de « itérateurs classiques »
que de religions.

-- Gaby
Fabien LE LEZ
Le #273049
On 20 Jan 2006 10:33:20 +0100, Gabriel Dos Reis

Il me sembe avoir au moins autant de définition de « itérateurs classiques »
que de religions.


... ou de définitions de la POO ?

Publicité
Poster une réponse
Anonyme