OVH Cloud OVH Cloud

Je cale. Help !

20 réponses
Avatar
Pierre Maurette
Bonjour,

Je tourne en rond depuis deux heures sur un problème qui doit être
évident. Code réduit qui plante (exception à l'exécution):

#include <stdio.h>
#include <stdbool.h>
#include <limits.h>

/*
#if !defined(__STDC__)
#error Standard ANSI C compiler required
#endif
*/


#if defined(__cplusplus)
#error C++ compiler not allowed
#endif

typedef struct
{
int serial;
int data;
unsigned long* ul;
}
coin;

unsigned long MoyenneCoins(coin* Debut, size_t nbr)
{
size_t indice;
unsigned long result = 0, dummy;
bool overflow = false;
for(indice = 0; !overflow && (indice < nbr); indice++)
{
dummy = result;
result += *((Debut + indice)->ul);
overflow = (result < dummy);
}
overflow = overflow && (result > ULONG_MAX / nbr);
puts(overflow ? "Overflow" : "OK");
return overflow ? ULONG_MAX : result / nbr;
}

#define TAILLE 4
int main()
{
coin tabCoin[TAILLE];
unsigned long testValue = UCHAR_MAX;
tabCoin[1].ul = &testValue;
tabCoin[2] = tabCoin[1];
tabCoin[3] = tabCoin[2];
printf("%lu\n", MoyenneCoins(tabCoin, TAILLE));
return 0;
}


Merci de m'aider...

--
Pierre Maurette

10 réponses

1 2
Avatar
Thierry Chappuis

[...]
Comment je fais si je désire utiliser du code écrit en C dans un
projet C++, et que j'ai pas mal d'appels à malloc (c'est finalement
relativement courant)? Dois-je caster malloc pour être certain que mon
code compile avec un compilateur C++ ou alors forcer la compilation
avec un compilo C avec:

#if defined(__cplusplus)
#error C++ compiler not allowed
#endif


Notez accessoirement que ces trois lignes ne changeront rien si vous
avez effectivement des malloc() non castés ;-)


Si, elles empêchent de compiler avec un compilateur C++. Je fait
essentiellement de la simulation numérique dans un milieu scientifique
(ingénieurie chimique) où je travaille seul sur mes programme. Donc,
moi non plus, je n'ai pas d'expérience du travaille partagé. Ma ligne
de conduite est donc de compiler mes fichier .c avec un compilateur C
(l'utilisation par erreur d'un compilateur C++ génére une erreur, car
définit la macro __cplusplus).

Dans chaque fichier d'entête, je place les lignes:

#ifdef __cplusplus
extern "C" {
#endif

/* Déclaration des points d'entrée */

#ifdef __cplusplus
}
#endif

Si la compilation des fichiers .c et des fichier .cpp est effectuée
avec des compilateurs C et C++ respectivement, je n'ai aucun problème.

Ma question était relative à la remarque de Jean-Marc Bourguet
concernant l'utilsation de #error ... dans les fichiers .c lorsque la
macro __cplusplus est définie. Ceci me laisse penser que dans le monde
professionnel où on travaille en équipe sur des projets beaucoup plus
gros que les miens, il est fréquent de compiler du code C avec un
compilateur C++. Je sais que c'est hors sujet dans le contexte de ce
post, mais je voulais profiter d'un retour d'expérience de Jean-Marc
Bourguet pour en apprendre plus à ce sujet.

Ensuite, à vous de choisir une stratégie. Sous réserve, puisque je n'ai
pas d'expérience de travail partagé, il me semble qu'un jeu de
fonctions en C (ou autre langage d'ailleurs) validées devra simplement
être lié au projet C++. Ainsi, leur maintenance reste centralisée, et
vous ne gérez pas vos fonctions globales dans le projet C++. C'est ce
qui se passe avec les API Windows.


Ca ressemble à ce que je pratique.

Ensuite, il y a le copié-collé de code C dans des méthodes C++. Ça
m'arrive assez fréquemment, à partir soit de code personnel, soit de
code glané sur la toile, soit souvent d'exemples (MSDN pour ne pas le
citer). Là, il y a ce que je voudrais faire et ce que malheureusement
ce que je fais trop souvent.
Le code originalement C va compiler en C++, au prix de quelques
précautions comme le cast des malloc(). Mais il va casser certaines
règles de style fortes de C++. Déjà le malloc(), et son cast "à l a C"
qui n'arrange rien. Ça va devenir nauséabond si vous free-ez des
pointeurs obtenus par new, ou si vous delete-ez d'autre obtenus par
malloc(). Donc, un peu de refactoring s'impose. A ce moment-là, autant
ne pas caster les malloc() et/ou ne pas faire le #include qui va bien.
Notez que le realloc() ne peut pas se C++-iser de façon triviale.
Sur le malloc() je suis assez strict. En revanche les trucs du MSDN, je
les copie-colle gaillardement. A une époque, je changeais puérilement
les noms, ce qui est extrêmement couillon si on y réfléchit deux
secondes.


Lorsqu'on fait de copie-collé de code C dans du code C++ (c'est
certainement pas recommandable mais je suis conscient qu'on a pas
toujours le choix dans un contexte professionnel), on ne copie pas des
instruction telles que

#if defined(__cplusplus)
#error C++ compiler not allowed
#endif

Merci pour vos retour d'expérience à ce sujet

P.S. Si c'est complètement HS, n'hésitez pas à me dire. Mon but
n'était pas de détourner ce post de son sujet initial.

Meilleures salutations

Thierry

--
Pierre Maurette



Avatar
Pierre Maurette
[...]
#if defined(__cplusplus)
#error C++ compiler not allowed
#endif


Notez accessoirement que ces trois lignes ne changeront rien si vous
avez effectivement des malloc() non castés ;-)


Si, elles empêchent de compiler avec un compilateur C++.


J'avais exceptionnellement placé un rigolard. Bien sûr, il est
préférable de mettre ces trois lignes. Mais s'il y a " effectivement
des malloc() non castés " dans le code, il ne compilera de toutes
façons pas en C++. C'est d'ailleurs l'origine de l'apparition de ce
malloc() non casté dans cette branche du fil. Quand j'ai un doute (sur
la macro __cplusplus ?), et j'en ai encore parfois face à un
comportement inattendu, ce malloc() le lève. Comme le ferait:

void* p = NULL;
int* q = p;

[...]

P.S. Si c'est complètement HS, n'hésitez pas à me dire. Mon but
n'était pas de détourner ce post de son sujet initial.


Vous avez vu quel était le sujet initial ? Votre intervention est en
charte, au moins...
Et puis il me semble que le trafic infernal sur fr.comp.lang.c peut
supporter du HS, voire la plaisanterie de mauvais goût.

--
Pierre Maurette



Avatar
Jean-Marc Bourguet
Pascal Bourguignon writes:

Le plus simple, c'est de compiler avec un compilateur C,
et de mettre dans chaque header C:

#ifdef __cplusplus
extern "C"{
#endif

...

#ifdef __cplusplus
}
#endif


Ca ne change rien aux regles du C++. Ca change simplement la convention
d'appel utilisee pour les fonctions qui sont definies dans ce bloc.

--
Jean-Marc

Avatar
Laurent Deniau
Jean-Marc Bourguet wrote:
Pascal Bourguignon writes:


Le plus simple, c'est de compiler avec un compilateur C,
et de mettre dans chaque header C:

#ifdef __cplusplus
extern "C"{
#endif

...

#ifdef __cplusplus
}
#endif



Ca ne change rien aux regles du C++. Ca change simplement la convention
d'appel utilisee pour les fonctions qui sont definies dans ce bloc.


Et aussi la semantique de inline, de const, de struct, etc...

a+, ld.


Avatar
Thierry Chappuis

[...]
#if defined(__cplusplus)
#error C++ compiler not allowed
#endif


Notez accessoirement que ces trois lignes ne changeront rien si vous
avez effectivement des malloc() non castés ;-)


Si, elles empêchent de compiler avec un compilateur C++.


J'avais exceptionnellement placé un rigolard. Bien sûr, il est
préférable de mettre ces trois lignes. Mais s'il y a " effectivement
des malloc() non castés " dans le code, il ne compilera de toutes
façons pas en C++. C'est d'ailleurs l'origine de l'apparition de ce
malloc() non casté dans cette branche du fil. Quand j'ai un doute (sur
la macro __cplusplus ?), et j'en ai encore parfois face à un
comportement inattendu, ce malloc() le lève. Comme le ferait:

void* p = NULL;
int* q = p;


Merci pour votre réponse. Un message de la forme "C++ compiler not
allowed" a toutefois le mérite d'être explicite.

Thierry




Avatar
Jean-Marc Bourguet
"Thierry Chappuis" writes:


Pierre Maurette writes:

#if defined(__cplusplus)
#error C++ compiler not allowed
#endif


Quel est l'objectif? Ça n'a un interet que si on a identifie
l'utilisation d'une difference de comportement entre le C et le C++ et
qu'on l'indique. Sinon, le seul effet est d'em... le pauvre type qui
va devoir se tapper l'ecriture d'un script pour pour virer ces
lignes-la de tous les fichiers quand il faudra compiler le code avec un
compilateur C++. Murphy va naturellement frapper et l'utilisation de
ce script va faire manquer le fichier unique ou la precaution etait
necessaire et documentee.


Comment je fais si je désire utiliser du code écrit en C dans un projet
C++, et que j'ai pas mal d'appels à malloc (c'est finalement relativement
courant)? Dois-je caster malloc pour être certain que mon
code compile avec un compilateur C++ ou alors forcer la compilation
avec un compilo C avec:

#if defined(__cplusplus)
#error C++ compiler not allowed
#endif


Si tu as des appels a malloc dont tu ne castes pas le resultat, c'est
inutile: la compilation va s'arreter sur les malloc... je ne vais pas
entrer dans le debat de savoir s'il faut ou non caster le resultat de
malloc en C.

L'article suivant http://www.cpax.org.uk/prg/writings/casting.php
explique qu'il faut éviter de compiler du code C avec un compilateur
C++. Votre commentaire à ce sujet m'intéresse!


Je ne suis pas d'accord avec ses commentaires. Qu'on le veuille ou non, le
C et le C++ partagent un sous-ensemble commun important, qui est presque --
mais pas tout a fait -- equivalent a C90. Donc il y a deux choses qui sont
sensees:
- passer du code C en C++ et continuer a le faire evoluer en C++;
- fournir du code compilable a la fois en C et en C++ parce qu'il doit
etre utilise dans des projets C et C++

Une garde du genre de celles ci-dessus va gener l'execution d'une de ces
choses mais ne l'empecher en rien. Elle ne va meme pas provoquer un examen
du code si elle n'est pas accompagnee d'un commentaire specifique au code
protege sur une difference entre le C et le C++, ou si elle est presente
systematiquement dans tous les fichiers et qu'un tel commentaire est
present dans un ou deux de ceux-ci (apres avoir modifie manuellement 3
fichiers, on ecrit un script pour virer les gardes).

Donc je ne vois que deux utilites:

- empecher une compilation accidentelle du C par un compilateur C++ --
et dans ce cas j'aurais tendance a chercher a ameliorer
l'environnement pour eviter le probleme (mais on ne controle pas
toujours l'environnement)

- prevenir la compilation volontaire de code C en C++ parce qu'il
utilise une difference qui n'est pas detectee a la compilation.
J'aurais tendance a ne pas utiliser cette difference dans un premier
temps; mais pour que ce soit alors utile, il faut d'une part indiquer
quelle est cette difference et pourquoi on l'utilise plutot que
d'ecrire un code plus generique, d'autre part qu'un humain passe sur
le code donc que ces gardes ne soient utilisees que dans ce cas la.

A+

--
Jean-Marc



Avatar
Thierry Chappuis

"Thierry Chappuis" writes:


Pierre Maurette writes:

#if defined(__cplusplus)
#error C++ compiler not allowed
#endif


Quel est l'objectif? Ça n'a un interet que si on a identifie
l'utilisation d'une difference de comportement entre le C et le C++ et
qu'on l'indique. Sinon, le seul effet est d'em... le pauvre type qui
va devoir se tapper l'ecriture d'un script pour pour virer ces
lignes-la de tous les fichiers quand il faudra compiler le code avec un
compilateur C++. Murphy va naturellement frapper et l'utilisation de
ce script va faire manquer le fichier unique ou la precaution etait
necessaire et documentee.


Comment je fais si je désire utiliser du code écrit en C dans un pr ojet
C++, et que j'ai pas mal d'appels à malloc (c'est finalement relative ment
courant)? Dois-je caster malloc pour être certain que mon
code compile avec un compilateur C++ ou alors forcer la compilation
avec un compilo C avec:

#if defined(__cplusplus)
#error C++ compiler not allowed
#endif


Si tu as des appels a malloc dont tu ne castes pas le resultat, c'est
inutile: la compilation va s'arreter sur les malloc... je ne vais pas
entrer dans le debat de savoir s'il faut ou non caster le resultat de
malloc en C.

L'article suivant http://www.cpax.org.uk/prg/writings/casting.php
explique qu'il faut éviter de compiler du code C avec un compilateur
C++. Votre commentaire à ce sujet m'intéresse!


Je ne suis pas d'accord avec ses commentaires. Qu'on le veuille ou non, le
C et le C++ partagent un sous-ensemble commun important, qui est presque --
mais pas tout a fait -- equivalent a C90. Donc il y a deux choses qui so nt
sensees:
- passer du code C en C++ et continuer a le faire evoluer en C++;
- fournir du code compilable a la fois en C et en C++ parce qu'il doit
etre utilise dans des projets C et C++

Une garde du genre de celles ci-dessus va gener l'execution d'une de ces
choses mais ne l'empecher en rien. Elle ne va meme pas provoquer un exam en
du code si elle n'est pas accompagnee d'un commentaire specifique au code
protege sur une difference entre le C et le C++, ou si elle est presente
systematiquement dans tous les fichiers et qu'un tel commentaire est
present dans un ou deux de ceux-ci (apres avoir modifie manuellement 3
fichiers, on ecrit un script pour virer les gardes).

Donc je ne vois que deux utilites:

- empecher une compilation accidentelle du C par un compilateur C++ --
et dans ce cas j'aurais tendance a chercher a ameliorer
l'environnement pour eviter le probleme (mais on ne controle pas
toujours l'environnement)

- prevenir la compilation volontaire de code C en C++ parce qu'il
utilise une difference qui n'est pas detectee a la compilation.
J'aurais tendance a ne pas utiliser cette difference dans un premier
temps; mais pour que ce soit alors utile, il faut d'une part indiquer
quelle est cette difference et pourquoi on l'utilise plutot que
d'ecrire un code plus generique, d'autre part qu'un humain passe sur
le code donc que ces gardes ne soient utilisees que dans ce cas la.

A+

--
Jean-Marc


Merci beaucoup pour toutes ces précisions détaillées. Meilleures
salutations

Thierry




Avatar
Jean-Marc Bourguet
"Thierry Chappuis" writes:

Ma question était relative à la remarque de Jean-Marc Bourguet
concernant l'utilsation de #error ... dans les fichiers .c lorsque la
macro __cplusplus est définie. Ceci me laisse penser que dans le monde
professionnel où on travaille en équipe sur des projets beaucoup plus
gros que les miens, il est fréquent de compiler du code C avec un
compilateur C++. Je sais que c'est hors sujet dans le contexte de ce
post, mais je voulais profiter d'un retour d'expérience de Jean-Marc
Bourguet pour en apprendre plus à ce sujet.


Je travaille sur un projet (genre dizaines de millions de lignes de code
commence il y a 20 ans avec quelques centaines de programmeurs qui ont
travaille et pour certains travaillent encore dessus) commence en C auquel
du C++ a ete ajoute. Il y a quelques annees, une partie du code C (pas
tout, mais c'est la partie sur laquelle je travaille; je n'ai pas
d'estimation de la proportion de ce qui a ete converti) a ete convertie
pour etre compilee en C++ et est compilee en C++ depuis.

J'ai aussi connaissance de librairies qui sont compilees en C ou en C++
suivant le langage dans lequel le projet qui les lie est ecrit. Une
technique pour malloc/free dans ces projets c'est d'utiliser

#define NEW(type) (((type)*)malloc(sizeof(type)))
#define ARRAYNEW(type,sz) (((type)*)malloc((sz)*sizeof(type)))

A+

--
Jean-Marc

Avatar
Gaël Tessier
Pierre Maurette wrote:
unsigned long MoyenneCoins(coin* Debut, size_t nbr)
{
size_t indice;
unsigned long result = 0, dummy;
bool overflow = false;
for(indice = 0; !overflow && (indice < nbr); indice++)
{
dummy = result;
result += *((Debut + indice)->ul);
overflow = (result < dummy);
}
overflow = overflow && (result > ULONG_MAX / nbr);
puts(overflow ? "Overflow" : "OK");
return overflow ? ULONG_MAX : result / nbr;
}


Indépendamment du problème déjà évoqué de tabCoin[0] non intialisé, il
me semble que le test "(result > ULONG_MAX / nbr)" n'est pas
approprié. On ne divise jamais result pas nbr, contrairement à ce que
suggère le nom de la fonction.

--
Gaël Tessier
http://www.tessier-net.org

Avatar
Pierre Maurette
Pierre Maurette wrote:
unsigned long MoyenneCoins(coin* Debut, size_t nbr)
{
size_t indice;
unsigned long result = 0, dummy;
bool overflow = false;
for(indice = 0; !overflow && (indice < nbr); indice++)
{
dummy = result;
result += *((Debut + indice)->ul);
overflow = (result < dummy);
}
overflow = overflow && (result > ULONG_MAX / nbr);
puts(overflow ? "Overflow" : "OK");
return overflow ? ULONG_MAX : result / nbr;
}


Indépendamment du problème déjà évoqué de tabCoin[0] non intialisé, il
me semble que le test "(result > ULONG_MAX / nbr)" n'est pas
approprié. On ne divise jamais result pas nbr, contrairement à ce que
suggère le nom de la fonction.


Oui, il s'agissait de noyer le poisson, et surtout il ne peut pas y
avoir d'overflow sur result dans cette fonction. On pourrait faire:

unsigned long MoyenneCoins(coin* Debut, size_t nbr)
{
size_t indice;
unsigned long result = 0, reste = 0;
for(indice = 0; indice < nbr; indice++)
{
result += *((Debut + indice)->ul) / nbr;
reste += *((Debut + indice)->ul) % nbr;
}
return result + (reste / nbr);
}

Il faudrait borner nbr, ou tester le dépassement sur reste. Peut-être
un truc comme ça:

inline void AddAnItemInMoyenneCoins(unsigned long* presult
, unsigned long* preste
, size_t nbr
, unsigned long valeur)
{
unsigned long dummy = *preste;
*presult += valeur / nbr;
*preste += valeur % nbr;
if(*preste < dummy)
{
AddAnItemInMoyenneCoins(presult, preste, nbr, ULONG_MAX);
}
}

unsigned long MoyenneCoins(coin* Debut, size_t nbr)
{
size_t indice;
unsigned long result = 0, reste = 0;
for(indice = 0; indice < nbr; indice++)
{
AddAnItemInMoyenneCoins(&result, &reste, nbr, *((Debut +
indice)->ul));
}
return result + (reste / nbr);
}

--
Pierre Maurette


1 2