Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Quel opérateur de cast ?

23 réponses
Avatar
meow
Hello,

Plutot que de relire pour la dixi=E8me fois le 30 chapitres et
tutoriaux sur les cast, auxquels je n'ai toujours rien pann=E9, j'ai
d=E9cid=E9 de changer de technique : je pose la question sur un cas
concret et j'affinerai ma connaissance au fur et =E0 mesure. Dans
l'extrait de code :

Volumetric* create(Kind k){
Volumetric* v;
switch (k) {
case RAW:
v =3Dnew RawVolumetric;break;
default:
v =3DNULL;break;
}
return v;
}

il y a un v=3DNULL qui ne me semble pas tr=E8s joli. Il faudrait tout du
moins que je le caste en Volumetric*, non ? Et si oui, quel operateur
dois-je utiliser ?

--Ben

3 réponses

1 2 3
Avatar
James Kanze
Gabriel Dos Reis wrote:
Fabien LE LEZ writes:


| On Fri, 14 Jul 2006 10:09:37 +0200, Jean-Marc Bourguet
| :


| >C'est une expression entière non constante. Tu ne peux pas
| >faire quelque chose comme:


| >int i[(0, 5)];


| Ah, OK. J'imagine que c'est parce que c'est considéré comme
| la valeur de retour d'une fonction ? (En l'occurence,
| 'operator,')


Je ne sais pas. C'est une règle que C++ a hérité de C. Si on doit
considérer « operator, » comme une fonction, qu'en est-il de
« operator+ » dans « 5 + 2 » ?


La seule rationale que je peux trouver, c'est que logiquement,
la seule raison d'utiliser un opérateur virgule, c'est pour les
effets de bord dans la première sous-expression. Et s'il y a des
effets de bord, il n'y a pas de constante.

Mais j'avoue le trouver un peu faiblard comme rationale. Qu'il
n'y a normalement aucune raison d'écrire « (0, 0) », je veux
bien, mais on ne l'a pas interdit, et je ne vois pas ce qu'il a
de non-constante.

À la dernière réunion de Berlin, j'ai tenté de changer cette règle
(dans notre proposition d'expression constante généralisée) mais je me
suis vite rendu compte que cela change la sémantique de programmes
C++ actuels. Donc j'ai fait machine arrière. La question de savoir si
ces programmes sont suffisamment importants est un autre débat, un
débat dans lequel je ne voudrais pas m'engager :-)


Par exemple,


void f(void*);
void f(int);


f((5, 0)); // OK : appelle "void f(int)"
f(0); // ERROR : ambiguité


Il n'y a pas un typo là ? f(int) est un match exact, qui a
précédance sur la conversion null pointer constant en pointeur.
Et malgré le nom « null pointer constant », il y a bien une
conversion.

Si la deuxième fonction était « f(long) », en revanche...

--
James Kanze
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

Avatar
meow
Hello, j'ai du décrocher quelques jours pour coder un bidule (que je
passerai certainement encore plus de temps à débugger... :/). Bref,
j'ai du abandonner mes deux threads au beau milieu de leur
développement. J'ai lu vos réponses aux deux et je reprends celui-ci
pour lequel j'ai encore quelques question, bien qu'hors sujet initial.

+Fabien Le Lez
Note qu'à tout moment, v est un pointeur valide, sur lequel on peut
appeler delete sans problème.


Tiens... un delete sur un pointeur null passe bien... J'avais jamais
essayé, voilà
qui est bon à savoir... Merci :)

+Kanze
Volumetric*
create( Kind k )
{
Map::const_iterator iter = map.find( k ) ;
return iter == map.end()
? NULL
: iter->second->create() ;
}
L'avantage, évidemment, c'est que je peux ajouter des types sans toucher
au code existant.


Voilà un bout de code qui m'interesse mais dont je ne suis pas certain
de cerner toute la portée.
Tu mets quoi dans ce map Map<Key,Val> ?
donc, en Key tu mettrais kind, mais en Val ? Vu que tu préconises un
create() sur un objet de type Val, je supposes Val serait un type
pointeur sur Fabrique, une classe abstraite dérivée en autant de
classes fabriqueKind_i que j'ai de types Kind_i... C'est pas une
implémentation du pattern fabrication ?

Avatar
kanze
meow wrote:

[...]
+Kanze
Volumetric*
create( Kind k )
{
Map::const_iterator iter = map.find( k ) ;
return iter == map.end()
? NULL
: iter->second->create() ;
}
L'avantage, évidemment, c'est que je peux ajouter des types
sans toucher au code existant.


Voilà un bout de code qui m'interesse mais dont je ne suis pas
certain de cerner toute la portée.

Tu mets quoi dans ce map Map<Key,Val> ?


Ce que tu veux.

Comme clé, tu utilises quelque chose qui sert à identifier le
type : std::string est fréquent, mais dans certains cas, on
pourrait lui préférer un enum (mais alors, un Val[] pourrait
être plus indiquer qu'un std::map), dans d'autre des
identificateurs plus structurer, du genre DN. (Si tu ne connais
pas les DN, ce n'est pas grave. C'est juste un format
d'identificateur hièrarchique, très utilisé dans des protocols,
par exemple LDAP.)

Comme Val, c'est prèsque toujours un pointeur, soit à une
fonction (Volumetric* (*)()), soit à une « interface », du
genre :

class VolumetricFactory
{
public:
virtual ~VolumetricFactory() {}
virtual Volumetric* newInstance() = 0 ;
} ;

Ce que je fais souvent, c'est d'étendre VolumetricFactory pour
qu'elle (la classe de base) s'occupe de la régistration dans son
constructeur. Et puisque la plupart des objets dérivés seront
statique, j'utilise le modèle singleton pour la map, ce qui
donne quelque chose du genre :

class VolumetricFactory
{
public:
virtual ~VolumetricFactory() {}
virtual Volumetric* newInstance() = 0 ;
protected:
VolumetricFactory( std::string const& id )
;
} ;

class VolumetricFactoryMap
{
public:
static VolumetricFactoryMap&
instance() ;
VolumetricFactory* operator[]( std::string const& id ) const;
void enrol( VolumetricFactory const* factory,
std::string const& id ) ;
private:
typedef std::map< std::string, VolumetricFactory* >
Map ;
Map myMap ;
VolumetricFactory const*
myDefault ;

VolumetricFactoryMap() ;
} ;

class DefaultVolumetricFactory : public VolumetricFactory
{
public:
virtual Volumetric* newInstance() ;
} ;

VolumetricFactory::VolumetricFactory(
std::string const& id )
{
VolumetricFactoryMap::instance().enrol( this, id ) ;
}

VolumetricFactoryMap&
VolumetricFactoryMap::instance()
{
static VolumetricFactoryMap
theOneAndOnly ;
return theOneAndOnly ;
}

VolumetricFactory*
VolumetricFactoryMap::operator[](
std::string const& id ) const
{
Map::const_iterator result = myMap.find( id ) ;
return result == myMap.end()
? myDefault
: result->second ;
}

void
VolumetricFactoryMap::enrol(
VolumetricFactory const*
factory,
std::string const& id )
{
assert( myMap.find( id ) == myMap.end() ) ;
myMap.insert( Map::value_type( id, factory ) ) ;
}

VolumetricFactoryMap::VolumetricFactoryMap()
{
static DefaultVolumetricFactory
theDefault ;
myDefault = theDefault ;
}

VolumetricFactory*
DefaultVolumetricFactory::newInstance()
{
return NULL ;
}

Pour chaque type que tu veux supporter, tu dérives une usine de
VolumetricFactory, et tu en définis une seule instance statique.
C'est le constructeur de cette instance qui s'occupe d'insérer
l'instance dans le map. (Très souvent, je ferais de cette classe
usine une classe embriquée privée du type qu'elle doit créer,
avec la seule instance une membre statique privé.)

Note que si ton application est multithread, et que certaines
de ces instances statique d'usine se trouvent dans des objets
dynamiques (.so, .dll, etc.), il faudrait ajouter un
boost::scoped_lock en tête des deux fonctions membre de
VolumetricFactoryMap, sur un boost::mutex qu'on a ajouté comme
membre de VolumetricFactoryMap.

Finalement, l'utilisation est triviale :

Volumetric* p
= VolumetricFactoryMap::instance()[ id ]->create() ;

donc, en Key tu mettrais kind, mais en Val ? Vu que tu préconises un
create() sur un objet de type Val, je supposes Val serait un type
pointeur sur Fabrique, une classe abstraite dérivée en autant de
classes fabriqueKind_i que j'ai de types Kind_i... C'est pas une
implémentation du pattern fabrication ?


Tout à fait. C'est une implémentation qui profite de plusieurs
particularités du C++, comme les constructeurs des objets
statiques, pour faire que tout se passe à peu près
automatiquement pour le client.

--
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


1 2 3