OVH Cloud OVH Cloud

reinterpret_cast

16 réponses
Avatar
Vincent Richard
Bonjour,

Soit le programme suivant :

extern int f(int* x);

typedef int (*my_f)(const int* x);

int main()
{
const int x =3D 1234;

int r =3D ((my_f)(f))(&x);
}

A savoir :

* il m'est impossible de modifier la d=E9claration de la fonction f()
* cette derni=E8re ne modifie pas son param=E8tre
* je ne veux pas "caster" le param=E8tre effectif "x"

Je cherche =E0 obtenir le m=EAme comportement avec un cast "=E0 la C++".
Un static_cast<> ne suffisant pas, je m'oriente vers ceci :

int r =3D (reinterpret_cast <my_f>(f))(&x);

Le comportement est-il le m=EAme ? Est-ce garanti de fonctionner sur
n'importe quel compilateur et/ou machine ?

(pour le pourquoi de cette question : j'utilise en fait une biblioth=E8que
externe dans laquelle f est parfois d=E9finie comme je l'ai indiqu=E9e, et
sur d'autres syst=E8mes avec le param=E8tre en 'const'. Je souhaite avoir
un code identique dans tous les cas.)

Merci d'avance pour vos r=E9ponses, et bonne ann=E9e !

Vincent

10 réponses

1 2
Avatar
drkm
Vincent Richard writes:

Je cherche à obtenir le même comportement avec un cast "à la C++".
Un static_cast<> ne suffisant pas, je m'oriente vers ceci :

int r = (reinterpret_cast <my_f>(f))(&x);

Le comportement est-il le même ?


Il me semble que la sémantique de reinterpret_cast<> est celle du
cast à la C lorsque static_cast<> et const_cast<> ne sont pas
d'application.

Heu, je ne sais pas si j'ai été clair, là. L'ensemble des
conversions permises par static_cast<>, plus celui de const_cast<>,
plus celui de reinterpret_cast<> == l'ensemble des conversions de
l'opérateur de conversion à la C. Comme les deux premiers ne
s'appliquent pas dans ce cas, je dirais qu'il s'agit bien de la même
conversion.

Quant à la légalité et aux garanties de cette conversion (et surtout
de l'appel qui est fait sur son résultat), je ne sais pas.

--drkm

Avatar
Gabriel Dos Reis
Vincent Richard writes:

| Bonjour,
|
| Soit le programme suivant :
|
| extern int f(int* x);
|
| typedef int (*my_f)(const int* x);
|
| int main()
| {
| const int x = 1234;
|
| int r = ((my_f)(f))(&x);
| }
|
| A savoir :
|
| * il m'est impossible de modifier la déclaration de la fonction f()
| * cette dernière ne modifie pas son paramètre
| * je ne veux pas "caster" le paramètre effectif "x"
|
| Je cherche à obtenir le même comportement avec un cast "à la C++".
| Un static_cast<> ne suffisant pas, je m'oriente vers ceci :
|
| int r = (reinterpret_cast <my_f>(f))(&x);
|
| Le comportement est-il le même ? Est-ce garanti de fonctionner sur
| n'importe quel compilateur et/ou machine ?

Chaque fois que tu utilises reinterpret_cast<> il faut regarder la
doc du compilo.

Cependant, de manière générale, un static_cast<> ou un
reinterpret_cast<> construit une nouvelle valeur (lorsque le résultat
est un rvalue). Cela veut dire que dans le cas présent, il faudrait que
la valeur de reinterpret_cast <my_f>(f) corresponde à l'adresse d'une
fonction. Laquelle ?

| (pour le pourquoi de cette question : j'utilise en fait une bibliothèque
| externe dans laquelle f est parfois définie comme je l'ai indiquée, et
| sur d'autres systèmes avec le paramètre en 'const'. Je souhaite avoir
| un code identique dans tous les cas.)

T'est-il possible d'enrober l'appel à f() ? Genre :

static inline int my_f(const int* x) {
#ifdef F_TAKES_CONST_ARG
f (x);
#else
f ((int*) x);
#endif
}

|
| Merci d'avance pour vos réponses, et bonne année !
|
| Vincent

--
Gabriel Dos Reis

Avatar
Vincent Richard
Cependant, de manière générale, un static_cast<> ou un
reinterpret_cast<> construit une nouvelle valeur (lorsque le résultat
est un rvalue). Cela veut dire que dans le cas présent, il faudrait que
la valeur de reinterpret_cast <my_f>(f) corresponde à l'adresse d'une
fonction. Laquelle ?


Merci pour la réponse.
Donc, il ne vaudrait mieux pas que j'utilise ça...

T'est-il possible d'enrober l'appel à f() ? Genre :

static inline int my_f(const int* x) {
#ifdef F_TAKES_CONST_ARG
f (x);
#else
f ((int*) x);
#endif
}


La fonction en question est la fonction iconv(), déclarée de la maniè re
suivante sur mon système (Debian GNU/Linux) :

size_t iconv(iconv_t cd,
char** inbuf, size_t* inleft,
char** outbuf, size_t* outleft)

et sur d'autres système avec le deuxième argument de type 'const char**'
(qui est d'ailleurs, je crois, la définition "officielle" POSIX).

Je crois me souvenir qu'il existe des macros M4 utilisable avec 'autoconf'
pour détecter si c'est l'un ou l'autre des 2 prototypes qui est utilisé,
malheureusement, je n'utilise pas les autotools...

Jusqu'ici, j'utilisais le "hack" suivant :

typedef size_t (*iconv_const_hack)(iconv_t cd, const char** inbuf,
size_t* inbytesleft, char** outbuf, size_t* outbytesleft);

#define iconv_const ((iconv_const_hack) iconv)

puis, pour l'appel :

iconv_const(...)

Cela dit, je ne crois pas que ça soit mieux que le reinterpret_cast<>.
De plus, j'étais en train de remplacer tous les casts à la C par des
casts à la C++ dans mon projet (lisibilité, etc.)...

Merci d'avance.

Vincent

Avatar
Vincent Richard
J'ai finalement trouvé une solution, pas très propre certes, mais qui
semble fonctionner :

class IconvHack
{
public:

IconvHack(const char** ptr) : m_ptr(ptr) { }

operator const char**() { return m_ptr; }
operator char**() { return const_cast <char**>(m_ptr); }

private:

const char** m_ptr;
};

et pour l'appel :

const char* p = "...";

iconv(d, IconvHack(&p) ...);

Il semblerait que ça soit correct...

Vincent
Avatar
Gabriel Dos Reis
Vincent Richard writes:

| J'ai finalement trouvé une solution, pas très propre certes, mais qui
| semble fonctionner :
|
| class IconvHack
| {
| public:
|
| IconvHack(const char** ptr) : m_ptr(ptr) { }
|
| operator const char**() { return m_ptr; }
| operator char**() { return const_cast <char**>(m_ptr); }
|
| private:
|
| const char** m_ptr;
| };
|
| et pour l'appel :
|
| const char* p = "...";
|
| iconv(d, IconvHack(&p) ...);
|
| Il semblerait que ça soit correct...

Oui, c'est dual à la solution qui consiste à enrober la fonction.
Il faudra juste faire attention aux « const » et « * » :


char* p = strcat(...);
iconv(d, IconvHack(p), ...);

-- Gaby
Avatar
drkm
Gabriel Dos Reis writes:

Oui, c'est dual à la solution qui consiste à enrober la fonction.
Il faudra juste faire attention aux « const » et « * » :

char* p = strcat(...);
iconv(d, IconvHack(p), ...);
^^^


iconv( d , IconvHack( & p ) , ... ) ;

Je ne comprend pas ton « Il faudra juste faire attention aux "const"
et "*" ». À quoi penses-tu en particulier ?

Il faut également être certain que `iconv()' ne modifie en aucun cas
les objets pointés, non ?

--drkm

Avatar
Gabriel Dos Reis
drkm writes:

| Gabriel Dos Reis writes:
|
| > Oui, c'est dual à la solution qui consiste à enrober la fonction.
| > Il faudra juste faire attention aux « const » et « * » :
|
| > char* p = strcat(...);
| > iconv(d, IconvHack(p), ...);
| ^^^
|
| iconv( d , IconvHack( & p ) , ... ) ;

Merci !

| Je ne comprend pas ton « Il faudra juste faire attention aux "const"
| et "*" ». À quoi penses-tu en particulier ?

Le code ci-dessus ne compilera pas parce qu'il n'y a pas de conversion
implicite de « T** » vers « const T** ».
Au delà d'un « * », les « const » c'est l'emmerdement maximal.

-- Gaby
Avatar
drkm
Gabriel Dos Reis writes:

drkm writes:

| Je ne comprend pas ton « Il faudra juste faire attention aux "const"
| et "*" ». À quoi penses-tu en particulier ?

Le code ci-dessus ne compilera pas parce qu'il n'y a pas de conversion
implicite de « T** » vers « const T** ».
Au delà d'un « * », les « const » c'est l'emmerdement maximal.


Ok, je n'avais pas fait le lien. Encore maintenant, c'est une règle
qui me surprend. « Ben quoi, j'augmente la contrainte de constance,
quel est le problème ? Ha, oui ... »

--drkm

Avatar
kanze
drkm wrote:
Gabriel Dos Reis writes:

drkm writes:

| Je ne comprend pas ton « Il faudra juste faire attention
| aux "const" et "*" ». À quoi penses-tu en particulier ?

Le code ci-dessus ne compilera pas parce qu'il n'y a pas de
conversion implicite de « T** » vers « const T** ». Au delà
d'un « * », les « const » c'est l'emmerdement maximal.


Ok, je n'avais pas fait le lien. Encore maintenant, c'est
une règle qui me surprend. « Ben quoi, j'augmente la
contrainte de constance, quel est le problème ? Ha, oui ... »


char const c = 'a' ;
char ** p1 ;
char const ** p2 = &p1 ; // C'est le char** -> char const**
// qui est en fait illégal.
*p2 = &c ;
**p1 = 'b' ;

On n'aime pas pouvoir modifier un const sans qu'il y ait un
const_cast quelque part.

--
James Kanze GABI Software http://www.gabi-soft.fr
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
drkm
writes:

drkm wrote:

Ok, je n'avais pas fait le lien. Encore maintenant, c'est
une règle qui me surprend. « Ben quoi, j'augmente la
contrainte de constance, quel est le problème ? Ha, oui ... »


char const c = 'a' ;
char ** p1 ;
^^

char const ** p2 = &p1 ; // C'est le char** -> char const**
^^^

// qui est en fait illégal.
*p2 = &c ;
**p1 = 'b' ;


Ce serait pas plutôt :

char const c = 'a' ;
char * p1 ;
char const * * p2 = & p1 ;
* p2 = & c ;
* p1 = 'b' ;

?

Mais ce que je voulais dire, c'est que je me surprends encore à
avoir la première réaction de « Ben j'augmente la contrainte de
constance, puisqu'il y a un 'const' en plus ». Avant le « Ha oui,
dans '* p2 = & c ;', l'opérande effective de gauche est en fait de
type 'char *' et celui de droite 'char const *' ».

--drkm


1 2