Partage
  • Partager sur Facebook
  • Partager sur Twitter

Généraliser des structures en paramètre

Prgramme Pro C / Oracle C API

    24 février 2023 à 11:29:10

    Bonjour,

    Je développe actuellement pour mon client divers programmes interagissant avec une base Oracle.
    Leur framework me force à passer par des programme Pro C.

    Je me voit assez régulièrement obligé de passer par des variables de type VARCHAR2, d'après la documentaion d'Oracle, le pre-processeur les étant sous forme de structures.
    Exemple:

    VARCHAR2 commentaire[256];
    // devient:
    struct commentaire
    {
        int len;
        char arr[256];
    };
    
    VARCHAR2 nom[25];
    '// devient
    struct nom
    {
        int len;
        unsigned char arr[25];
    };

    Les structures résultantes sont relativement proche (d'un point de vue humain), mais pas identiques.
    En conséquence, pour convertir une donnée de type VRACHAR2 en chaine et vice versa, j'utilise les instructions suivantes:

    char data[256];
    VARCHAR2 commentaire[256];
    
        // convertion varchar --> string
    strcpy(data, (char*)commentaire.arr);
    
        // convertion string --> varchar
    commentaire.len = strlen(data);
    strcpy(commentaire.arr, (unsigned char*)data);

    Je souhaiterais factoriser, par contre, vu que les structures résultantes n'ont pas le même nom, ca coince.

    Un collègue pense à passer par des pointeurs void*, mais c'est dégueulasse.

     Je suis preneur de toute solution correcte.

    Merci.

    • Partager sur Facebook
    • Partager sur Twitter
      24 février 2023 à 13:44:53

      Bonjour,

      attention sur le strcpy de la ligne 5, cela ne fonctionne que si tu as un zero terminal ; il vaudrait mieux utiliser un strncpy avec la bonne longueur de ton varchar et aussi prévoir un +1 pour ledit zéro terminal dans ton char[].

      Ce que tu peux faire est déclarer une structure un peu «générique»

      struct foo {
          int len;
          unsigned char arr[];
      };

      Et utiliser de struct foo * à la place d'un void * … La norme C te garantit (sauf demande particulière faite au compilo) ça (au besoin je te donnerai les références). Cela peut ne pas fonctionner car cela va dépendre non seulement des options de compilation fournies au compilo mais également du compilo et du standard C qu'il implémente.

      • Partager sur Facebook
      • Partager sur Twitter
        24 février 2023 à 15:03:29

        Rectification la doc oracle https://docs.oracle.com/cd/A57673_01/DOC/api/doc/PC_22/ch03a.htm#dev-varchar-pt

        dit qu'une déclaration de VARCHAR déclare une variable structure (de type anonyme), et non un type.

        Declaring VARCHAR Variables

        Think of a VARCHAR as an extended C type or predeclared struct. For example, the precompiler expands the VARCHAR declaration
        VARCHAR username[20];

        into the following struct with array and length members:

        struct 
        { 
         unsigned short len;
         unsigned char arr[20];
        } username;
        

        Du coup, c'est une variable de type structure anonyme, ce qui pose un souci si on veut la passer en paramètre. (voir à la fin) Donc retour aux void* ...

        > attention sur le strcpy de la ligne 5, cela ne fonctionne que si tu as un zero terminal ;

        Ce qui n'est pas le cas. Voir document oracle, qui dit qu'on peut ajouter un caractère nul à la fin pour manipuler comme une chaine C. En oubliant de dire qu'il faudrait un octet en plus....


        Donc pour copier un varchar src vers un varchar dst de taille maximale max, ça va être une boucle de copie d'un membre arr à l'autre, de n caractères, avec n = le minimum de

        • la taille du tableau destination
        • la longueur occupée dans le tableau source.

        Voila mes (bonnes ?) oeuvres du  jour, j'ai fait des macros

        • VC(size) pour définir une variable VARCHAR2
        • VCcpy(dst, src) pour copier le contenur d'un varchar2 dans un autre (en donnant leurs adresses)
        #include <stdio.h>
        #include <string.h>
        
        #include <assert.h>
        
        #define VC(size) struct {int len;  unsigned char arr[size]; }
        
        #define min(a,b) ((a)<(b) ? (a) : (b))
        
        void varchar2cpy(void *dst, int dst_size,
        				 void *src)
        {
        	struct {int len;  unsigned char arr[]; }
        		*d = dst,
        		*s = src;
        
        	int len = min(dst_size, s->len);
        	
                // EDIT - ÉTOURDERIE CORRIGÉE
        	//for (int i = 0, len =  min(dst_size, s->len); i < len; i++) {
                for (int i = 0; i < len; i++) {
        
        		d->arr[i] = s->arr[i];
        	}
        	d->len = len;
        }
        
        // on suppose que les paramètres sont des adresses
        #define VCcpy(dst, src) varchar2cpy(dst, sizeof((*dst).arr), src)
        
        void test1() {
        	printf("# test1()\n");
        	
        	VC(8) commentaire = {.len = 0, .arr = "0123456"}; 
        	VC(5) nom = {.len = 3, .arr = "joeX"};
        
        	//varchar2cpy(&commentaire, sizeof(commentaire.arr), &nom);
        	VCcpy(&commentaire, &nom);
        	
        	assert(commentaire.len == 3);
        	assert(strncmp(commentaire.arr, "joe", 3) == 0);
        }
        
        void test2() {
        	printf("# test2() - affectation tronquée\n");
        	
        	VC(8) commentaire = {.len = 6, .arr = "0123456"}; 
        	VC(5) nom = {.len = 3, .arr = "joeX"};
        
        	//varchar2cpy(&nom, sizeof(nom.arr), &commentaire);
        	VCcpy(&nom, &commentaire);
        
        	assert(nom.len == 5);
        	assert(strncmp(nom.arr, "01234", 5) == 0);
        }
        
        
        
        int main() {
        	test1();
        	test2();
        	printf("OK\n");
        } 
        


        --- complément ; le problème avec les structures anonymes en paramètres

        En compilant

        void f( struct { int a;}  *p ) {
        }
        
        
        void g() {
        	struct { int a;}  s;
        	f(&s);
        }
        
        

        on se fait bien engueuler (type pointeur incompatible)

        a.c:1:9: attention: le struct anonyme est déclaré à l'intérieur d'une liste de paramètres et ne sera pas visible en dehors de cette définition ou déclaration
            1 | void f( struct { int a;}  *p ) {
              |         ^~~~~~
        a.c: Dans la fonction « g »:
        a.c:7:4: attention: passage de l'argument 1 de « f » depuis un type pointeur incompatible [-Wincompatible-pointer-types]
            7 |  f(&s);
              |    ^~
              |    |
              |    struct <anonyme> *
        a.c:1:28: note: « struct <anonyme> * » attendu mais l'argument est de type « struct <anonyme> * »
            1 | void f( struct { int a;}  *p ) {
              |         ~~~~~~~~~~~~~~~~~~~^

        ---

        Ces ânes de chez Oracle ils auraient quand même pu faire générer des noms de types

        VARCHAR2 toto[10];
        
        =>
        
        #ifndef ORACLE_VARCHAR10
        #define ORACLE_VARCHAR10
        
        struct varchar10 {
           int len;
           unsigned char arr[10];
        };
        #endif
        
        struct varchar10 toto;
        
        

        par leur préprocesseur à la con. j'vous jure, pas deux sous d'idées.






        -
        Edité par michelbillaud 24 février 2023 à 19:59:19

        • Partager sur Facebook
        • Partager sur Twitter
          24 février 2023 à 18:31:07

          Je ne suis pas bon du tout en macro-programmation, mais tu me donnes des idées à essayer.

          Le but est de faire des convertirons string ==> varchar (et vice-versa).

          Je fais mes tests, et je revient vers vous (probablement pas avant lundi).

          • Partager sur Facebook
          • Partager sur Twitter
            24 février 2023 à 19:53:08

            Les macros, j'évite autant que possible.

            Ici c'est quand même une nécessité pour ne pas avoir à passer explicitement la taille max du varchar destination, qui est connue à la compilation mais pas à l'exécution.

            Petite correction sur le code précédent, où il y avait une étourderie

            void varchar2cpy(void *dst, int dst_size,
                             void *src)
            {
                struct {
                    int len;
                    unsigned char arr[];
                }
                *d = dst,
                 *s = src;
            
                int len = min(dst_size, s->len);
            
                for (int i = 0; i < len; i++) {          // plus simple !
                    d->arr[i] = s->arr[i];
                }
                d->len = len;
            }


            L'affectation d'une chaine à un varchar est du même goût

            void varchar_from_string(void *dst, int dst_size, const char *str)
            {
                struct {
                    int len;
                    unsigned char arr[];
                }
                *d = dst;
                int len =  min(dst_size, strlen(str));
                for (int i = 0; i < len; i++) {
                    d->arr[i] = str[i];
                }
                d->len = len;
            }
            
            #define VCfill(dst, str) varchar_from_string(dst,  sizeof((*dst).arr), str)
            

            Les tests qui vont avec

            void test3()
            {
                printf("# test3 ()- copie de chaine\n");
                VC(5) nom;
            
                VCfill(&nom, "toto");
                assert(nom.len == 4);
                assert(strncmp(nom.arr, "toto", 4) == 0);
            }
            
            
            void test4()
            {
                printf("# test4 ()- copie tronquée de chaine\n");
                VC(5) nom;
            
                VCfill(&nom, "abracadabra");
                assert(nom.len == 5);
                assert(strncmp(nom.arr, "abrac", 5) == 0);
            }

            Le remplissage d'une chaîne C à partir du contenu d'un varchar est du genre

            strncpy(string, varchar.arr, varchar.len);
            


            Evidemment en ayant pris un string de taille >= à sizeof(varchar.arr) + 1;




            -
            Edité par michelbillaud 24 février 2023 à 19:59:55

            • Partager sur Facebook
            • Partager sur Twitter

            Généraliser des structures en paramètre

            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
            • Editeur
            • Markdown