IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Tcl/Tk Discussion :

[API-C][Tcl] Problème interfaçage


Sujet :

Tcl/Tk

  1. #1
    Membre du Club
    Homme Profil pro
    Inscrit en
    Février 2008
    Messages
    18
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 18
    Points : 50
    Points
    50
    Par défaut [API-C][Tcl] Problème interfaçage
    Salut

    J'ai développé certaines fonctions en C et je souhaite les rendre accessibles (manipulables) depuis Tcl.
    Après quelques recherches, je suis tombé sur une page du wiki Tcl très intéressante sur ce sujet (après avoir vu au préalable celle-ci particulièrement pour la section Tcl Extension Building en intro, du reste je n'utilise pas SWIG).
    Le texte est très général alors avant de me lancer sur mes objets en question (assez complexes et donc avec pas mal de méthodes à interfacer), je me suis dit de faire un exemple plus simpliste :
    • les objets sont des entiers de type int
    • il y a 1 méthode sans argument pour lire la valeur de l'objet
    • et une autre méthode avec 1 argument pour écrire la valeur de l'objet
    • j'ai rajouté une dernière méthode (sans argument) pour incrémenter la valeur de l'objet

    Je travaille sous windows dans code::blocks 16.01 avec MSYS2 duquel j'utilise mingw64 en guise de compilateur (gcc version 7.2.0 Rev1 Built by MSYS2 project, cible x86_64-w64-mingw32) et je lie la bibliothèque statique Tcl 8.6.7 d'ActiveTcl à ce projet code::blocks qui génère à son tour une bibliothèque dynamique (si besoin je peux en renseigner le fichier .cbp) que je charge enfin dans la console tkcon d'ActiveTcl. À ce niveau là,
    • je peux instancier des entiers int,
    • je peux lire leur valeur
    • mais (l'interpréteur Tcl de) tkcon plante dès que je tente de les réécrire.

    J'ai joins mon code C en dessous et je suis assez convaincu que le plantage est causé par la tentative à objv[2] dans le paragraphe concernant l'écriture. Pour m'assurer que ce soit pas un problème avec les int que j'alloue en mémoire, j'ai rajouté une méthode pour incrémenter la valeur (et modifie donc la valeur en mémoire) et là, ça ne plante pas. Donc ça confirme le tout : que le problème semble bel et bien lié à la lecture du 3ième argument et non pas à une libération implicite de l'espace mémoire de l'entier.

    Je sais pas trop comment débeuguer. J'ai essayé dans MSYS2 de lancer l'interpréteur dans gdb mais il faudrait charger ma .dll et ça je ne peux le faire qu'après avoir lancer le démarrage de tclsh.exe et donc à ce moment là je ne pourrais plus définir de breakpoints. Donc du coup je me focalise sur la relecture du code source qui se trouve en dessous. Mais j'avoue que là je sèche un peu (si j'ai bien localisé la cause du plantage, je n'en comprends toujours pas la raison) donc si quelqu'un aurait une petite idée pourquoi mon code plante, je serais intéressé et merci d'avance pour l'entraide.

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
     
    int
    NombreObjCmd(ClientData clientData, Tcl_Interp *interp,
                  int objc, Tcl_Obj *CONST objv[])
    {
        char *text = NULL;
        Tcl_DString *dynstr = NULL;
        int *val = NULL;
        Tcl_Obj *intobj = NULL;
     
        int *intval = (int*) clientData;
     
    #if 0
        /* inutile... l'idée était d'incrémenter
         * le compteur de référence ce qui doit
         * empêcher la libération des objets passer
         * lors de l'appel vu qu'on semble taper une
         * erreur de ségmentation en appelant objv[2] */
        int i;
        for(i=0;i<objc;i++) Tcl_IncrRefCount(objv[i]);
    #endif // 0
     
        /* nombre d'arguments transmis */
        if (objc < 2) {
            Tcl_WrongNumArgs(interp,0,objv,
                           "Usage : attribut ?valeur?");
            return TCL_ERROR;
        }
     
        /* parsage des arguments : récupérer le mode d'appel */
        if ((text = Tcl_GetStringFromObj(objv[1], NULL)) == NULL)
            return TCL_ERROR;
     
        /* mode écriture */
        if (strncmp(text,"ecrire",6) == 0) {
     
            /* en mode écriture, il faut forcément
             * indiquer la valeur que l'on veut écrire */
            if (objc != 3) {
                Tcl_WrongNumArgs(interp,0,objv,
                               "Usage : ecrire valeur");
                return TCL_ERROR;
            }
    #if 1
            /* lorsqu'un argument est transmis pour la valeur,
             * il faut ensuite le décoder */
            if ((Tcl_GetIntFromObj(interp, objv[2], val) != TCL_OK) ||
                ((text = Tcl_GetStringFromObj(objv[2],NULL)) == NULL))
                return TCL_ERROR;
    #else
            /* une ségmentation erronée semble arriver avec objv[2] */
            *intval += 1;
            /* acquérir la valeur en mémoire et
             * l'exprimer en chaine de caractères */
            if ((intobj = Tcl_NewIntObj(*intval)) == NULL)
                return TCL_ERROR;
            if ((text = Tcl_GetStringFromObj(intobj,NULL)) == NULL)
                return TCL_ERROR;
     
    #endif // 0
    #if 1
            if ((dynstr = attemptckalloc(sizeof(Tcl_DString))) == NULL)
                return TCL_ERROR;
            Tcl_DStringInit(dynstr);
     
            /* affecter la nouvelle valeur en
             * mémoire et la retourner en sortie */
            *intval = *val;
            Tcl_DStringAppend(dynstr, text, -1);
            Tcl_DStringResult(interp,dynstr);
            ckfree(dynstr);
    #endif // 0
            return TCL_OK;
        }
     
        /* mode lecture */
        else if (strncmp(text,"lire",4) == 0) {
     
            /* acquérir la valeur en mémoire et
             * l'exprimer en chaine de caractères */
            if ((intobj = Tcl_NewIntObj(*intval)) == NULL)
                return TCL_ERROR;
            if ((text = Tcl_GetStringFromObj(intobj,NULL)) == NULL)
                return TCL_ERROR;
     
            if ((dynstr = attemptckalloc(sizeof(Tcl_DString))) == NULL)
                return TCL_ERROR;
            Tcl_DStringInit(dynstr);
     
            /* communication de la valeur */
            Tcl_DStringAppend(dynstr, text, -1);
            Tcl_DStringResult(interp,dynstr);
            ckfree(dynstr);
    //        ckfree(intobj);
     
            return TCL_OK;
        }
     
        /* mode incrémentation */
        else if (strncmp(text,"incr",4) == 0) {
     
            /* affecter la nouvelle valeur en mémoire */
            *intval += 1;
            *intval += 1;
     
            /* acquérir la valeur en mémoire et
             * l'exprimer en chaine de caractères */
            if ((intobj = Tcl_NewIntObj(*intval)) == NULL)
                return TCL_ERROR;
            if ((text = Tcl_GetStringFromObj(intobj,NULL)) == NULL)
                return TCL_ERROR;
     
            /* affecter la nouvelle valeur en
             * mémoire et la retourner en sortie */
            if ((dynstr = attemptckalloc(sizeof(Tcl_DString))) == NULL)
                return TCL_ERROR;
            Tcl_DStringInit(dynstr);
     
            Tcl_DStringAppend(dynstr, text, -1);
            Tcl_DStringResult(interp,dynstr);
            ckfree(dynstr);
     
            return TCL_OK;
        }
     
        /* mode inconnu */
        else {
            return TCL_ERROR;
        }
    }
     
     
    int
    InstanceurNombreObjCmd(ClientData clientData, Tcl_Interp *interp,
                  int objc, Tcl_Obj *CONST objv[])
    {
        /* variables locales */
        char *text = NULL;
        Tcl_DString *dynstr = NULL;
        int *intval = NULL;
        Tcl_Command cmd = NULL;
     
        /* nombre d'arguments transmis */
        if (objc != 2) {
            Tcl_WrongNumArgs(interp,0,objv,
                           "Usage : nombre nouveau");
            return TCL_ERROR;
        }
     
        /* parsage des arguments : récupérer le mode d'appel */
        if ((text = Tcl_GetStringFromObj(objv[1], NULL)) == NULL)
            return TCL_ERROR;
     
        /* unique mode d'appel supporté : nouveau */
        if (strncmp(text,"nouveau",7) != 0)
            return TCL_ERROR;
     
        /* allouer en mémoire l'objet en question */
        /* -> approprié par Tcl_Alloc = aucun risque
         * de libération prématurée ??? */
        if ((intval = attemptckalloc(sizeof(int))) == NULL)
            return TCL_ERROR;
        *intval = 0; /* RàZ */
     
        /* mettre en forme le contenu de la chaine de caractères
         * retournée lors de l'appel de cette commande :
         * - 3 caractères pout le mot "int",
         * - 16 chiffres héxadécimaux pour l'adresse de la
         * variable de type int en architecture 64 bits et
         * - 1 pour le marqueur de fin de chaine de caractères */
        if ((text = attemptckalloc(sizeof(char)*(3+16+1))) == NULL) {
            ckfree(intval);
            return TCL_ERROR;
        }
        sprintf(text, "int%p", intval);
     
        /* passer la chaine crée à une dstring qui en FAIT une COPIE
         * du contenu et par conséquent libérer la mémoire superflue */
        if ((dynstr = attemptckalloc(sizeof(Tcl_DString))) == NULL) {
            ckfree(intval);
            ckfree(text);
            return TCL_ERROR;
        }
        Tcl_DStringInit(dynstr);
        Tcl_DStringAppend(dynstr, text, -1);
        ckfree(text);
     
        /* créer une nouvelle commande dont le nom correspond
         * au contenu de la dstring */
        if ((text = Tcl_DStringValue(dynstr)) == NULL)
            goto ErrorFree;
        cmd = Tcl_CreateObjCommand(interp, text, NombreObjCmd,
            (ClientData) intval, (Tcl_CmdDeleteProc *) NULL);
        if (cmd == NULL) goto ErrorFree;
     
        /* renvoyer en sortie le contenu de la dstring puis
         * en libérer la mémoire pour conclure */
        Tcl_DStringResult(interp,dynstr);
        ckfree(dynstr);
     
        return TCL_OK;
     
        ErrorFree:
            ckfree(intval);
            ckfree(dynstr);
            return TCL_ERROR;
     
    int
    Tcl_wrap_obj_Init(Tcl_Interp *interp) {
     
        Tcl_Command cmd;
     
        cmd = Tcl_CreateObjCommand(interp, "nombre", InstanceurNombreObjCmd,
            (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
        if (cmd == NULL) return TCL_ERROR;
     
        return TCL_OK;
    }

  2. #2
    Membre du Club
    Homme Profil pro
    Inscrit en
    Février 2008
    Messages
    18
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 18
    Points : 50
    Points
    50
    Par défaut
    Finalement j'ai pu avancer car la console DOS de windows m'a bel et bien permis de lancer l'interpréteur tclsh duquel j'ai pu charger ma dll et surtout voir aussi les messages envoyés sur la sortie standarde.

    Du coup le problème n'était pas lié à l'argument objv[2] comme je le pensais initialement.
    Les fonctions Tcl_GetInt() ou Tcl_GetIntFromObj() marchent un peu comme sscanf() grosso modo et je leur passais bien un pointeur mais null jusqu'ici ce qui explique les problèmes que j'ai rencontré.

    Je croyais en fait que les arguments objv[i] contenaient l'espace mémoire auquel mon pointeur transmis ferait ensuite référence mais ces arguments ne contiennent initialement que les chaines de caractères et rien de plus (objv[i]->typePtr vaut NULL avant d'appeler Tcl_GetIntFromObj()).

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
     
    int
    NombreObjCmd(ClientData clientData, Tcl_Interp *interp,
                  int objc, Tcl_Obj *CONST objv[])
    {
        char *text = NULL;
        Tcl_Obj *intobj = NULL;
     
        int val;
        Tcl_DString dynstr;
     
        int *intval = (int*) clientData;
     
        /* nombre d'arguments transmis */
        if (objc < 2) {
            Tcl_WrongNumArgs(interp,0,objv,
                           "Usage : attribut ?valeur?");
            return TCL_ERROR;
        }
     
        /* parsage des arguments : récupérer le mode d'appel */
        if ((text = Tcl_GetStringFromObj(objv[1], NULL)) == NULL)
            return TCL_ERROR;
     
        /* initialisation du message à renvoyer */
        Tcl_DStringInit(&dynstr);
     
        /* mode écriture */
        if (strncmp(text,"ecrire",6) == 0) {
     
            /* en mode écriture, il faut forcément
             * indiquer la valeur que l'on veut écrire */
            if (objc != 3) {
                Tcl_WrongNumArgs(interp,0,objv,
                               "Usage : ecrire valeur");
                return TCL_ERROR;
            }
     
            /* lorsqu'un argument est transmis pour la valeur,
             * il faut ensuite le décoder */
            if ((Tcl_GetIntFromObj(interp, objv[2], &val) != TCL_OK) ||
                ((text = Tcl_GetStringFromObj(objv[2],NULL)) == NULL))
                return TCL_ERROR;
     
            /* affecter la nouvelle valeur en mémoire */
            *intval = val;
     
            /* et la retourner en sortie */
            goto RetourOk;
        }
     
        /* mode lecture */
        else if (strncmp(text,"lire",4) == 0) {
     
            /* acquérir la valeur en mémoire */
            if ((intobj = Tcl_NewIntObj(*intval)) == NULL)
                return TCL_ERROR;
            /* et l'exprimer en chaine de caractères */
            if ((text = Tcl_GetStringFromObj(intobj,NULL)) == NULL)
                return TCL_ERROR;
     
            /* retourner la valeur en sortie */
            goto RetourOk;
        }
     
        /* mode incrémentation */
        else if (strncmp(text,"incr",4) == 0) {
     
            /* affecter la nouvelle valeur en mémoire */
            *intval += 1;
     
            /* acquérir la valeur en mémoire */
            if ((intobj = Tcl_NewIntObj(*intval)) == NULL)
                return TCL_ERROR;
            /* et l'exprimer en chaine de caractères */
            if ((text = Tcl_GetStringFromObj(intobj,NULL)) == NULL)
                return TCL_ERROR;
     
            /* retourner la valeur en sortie */
            goto RetourOk;
        }
     
        /* mode décrémentation */
        else if (strncmp(text,"decr",4) == 0) {
     
            /* affecter la nouvelle valeur en mémoire */
            *intval -= 1;
     
            /* acquérir la valeur en mémoire et
             * l'exprimer en chaine de caractères */
            if ((intobj = Tcl_NewIntObj(*intval)) == NULL)
                return TCL_ERROR;
            if ((text = Tcl_GetStringFromObj(intobj,NULL)) == NULL)
                return TCL_ERROR;
     
            /* retourner la valeur en sortie */
            goto RetourOk;
        }
     
        /* mode suppression */
        else if (strncmp(text,"suppr",5) == 0) {
     
            /* acquérir le nom de la commande sous
             * forme de chaine de caractères */
            if ((text = Tcl_GetStringFromObj(objv[0],NULL)) == NULL)
                return TCL_ERROR;
     
            /* pour la supprimer ensuite */
            Tcl_DeleteCommand(interp,text);
            /* mémoire libérée automatiquement 
             * par Tcl_CmdDeleteProc = Tcl_Free */
     
            return TCL_OK;
        }
     
        /* mode inconnu */
        else {
            return TCL_ERROR;
        }
     
        RetourOk:
            Tcl_DStringAppend(&dynstr, text, -1);
            Tcl_DStringResult(interp,&dynstr);
     
            return TCL_OK;
    }
     
     
    int
    InstanceurNombreObjCmd(ClientData clientData, Tcl_Interp *interp,
                  int objc, Tcl_Obj *CONST objv[])
    {
        /* variables locales */
        char *text = NULL;
        Tcl_DString dynstr;
     
        int *intval = NULL;
        Tcl_Command cmd = NULL;
     
        /* nombre d'arguments transmis */
        if (objc != 2) {
            Tcl_WrongNumArgs(interp,0,objv,
                           "Usage : nombre nouveau");
            return TCL_ERROR;
        }
     
        /* parsage des arguments : récupérer le mode d'appel */
        if ((text = Tcl_GetStringFromObj(objv[1], NULL)) == NULL)
            return TCL_ERROR;
     
        /* initialisation du message renvoyé */
        Tcl_DStringInit(&dynstr);
     
        /* unique mode d'appel supporté : nouveau */
        if (strncmp(text,"nouveau",7) != 0)
            return TCL_ERROR;
     
        /* allouer en mémoire l'objet en question */
        if ((intval = attemptckalloc(sizeof(int))) == NULL)
            return TCL_ERROR;
        *intval = 0; /* RàZ */
     
        /* mettre en forme le contenu de la chaine de caractères
         * retournée lors de l'appel de cette commande :
         * - 3 caractères pout le mot "int",
         * - 16 chiffres héxadécimaux pour l'adresse de la
         * variable de type int en architecture 64 bits et
         * - 1 pour le marqueur de fin de chaine de caractères */
        if ((text = attemptckalloc(sizeof(char)*(3+16+1))) == NULL)
            goto ErrorFree;
        sprintf(text, "int%p", intval);
     
        /* passer la chaine crée à une dstring qui en FAIT une COPIE
         * du contenu et par conséquent libérer la mémoire superflue */
        Tcl_DStringAppend(&dynstr, text, -1);
        ckfree(text);
     
        /* créer une nouvelle commande dont le nom
         *  correspond au contenu de la dstring */
        if ((text = Tcl_DStringValue(&dynstr)) == NULL)
            goto ErrorFree;
        cmd = Tcl_CreateObjCommand(interp, text, NombreObjCmd,
            (ClientData) intval, (Tcl_CmdDeleteProc *) Tcl_Free);
        if (cmd == NULL) goto ErrorFree;
     
        /* renvoyer en sortie le contenu de la dstring */
        Tcl_DStringResult(interp,&dynstr);
     
        return TCL_OK;
     
        ErrorFree:
            ckfree(intval);
            return TCL_ERROR;
    }

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème de boutons radio pour récuperer les infos
    Par marsupilami34 dans le forum Général JavaScript
    Réponses: 1
    Dernier message: 06/09/2005, 11h47
  2. problèmes avec "include file" pour une page asp
    Par chipsense dans le forum ASP
    Réponses: 1
    Dernier message: 02/09/2005, 15h22
  3. Réponses: 5
    Dernier message: 31/08/2005, 10h05
  4. problème de requète SQL pour formulaire
    Par en_stage dans le forum Requêtes et SQL.
    Réponses: 15
    Dernier message: 21/06/2005, 12h21
  5. [tomcat] [jsp] Problème avec driver OCI pour oracle
    Par nanardcompanie dans le forum Tomcat et TomEE
    Réponses: 3
    Dernier message: 01/07/2004, 09h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo