Sur AS/400, un programme de n'importe quel langage peut appeler un pgm de n'importe quel langage de manière dynamique. Ceci est particulièrement vrai pour RPG,RPGIV, COBOL et CL. Le lien avec le pgm appelé est dynamique, c'est à dire : - que le programme appelant ne le connaît pas avant l'exécution. - le pgm appelé est recherché quand l'ordre CALL est rencontré. + adressage du pgm + vérification des droits + chargement en mémoire, réservation d'espace pour les variables. + seules les variables passées en tant que paramètres sont partagées. - retour à l'instruction qui suit le CALL à la fin du pgm appelé. |
pgm appelant (A) ........................ : mémoire : : nocli : 00006 : : nomcli: Durant : :----------------------: : : : CALL PGMB : : PARM NOCLI : : : : : : : : : : : :......................: |
pgm appelant (A) Pgm B addressé (suivant *LIBL) chargé en mémoire si les droits sont corrects. ........................... ........................ : mémoire : : mémoire : : : : nocli : 00006 : : nomcli: : : nomcli: Durant : : nocli.adr : : :----------------------: :-------------------------: : : : *ENTRY PLIST : : CALL PGMB : : PARM nocli: : PARM NOCLI : : : : : : (LINKAGE SECTION : : : : en cobol) : : : : : : : : : : : : : :......................: : : : : :.........................: pgm appelé (B) |
pgm appelant (A) Pgm B reçoit l'adresse du paramètre et non sa valeur. ........................... ........................ : mémoire : : mémoire : : : 12345> : nocli : 00006 : : nomcli: : : nomcli: Durant : /------> nocli.adr : x'12345' : :----------------------: / :-------------------------: : :/ : *ENTRY PLIST : : CALL PGMB /: : PARM nocli: : PARM NOCLI / : : : : : : : : : : : : : : : : : : : : : : : :......................: : : : : :.........................: pgm appelé (B) |
pgm appelant (A) Il travaille donc sur la mémoire du pgm appelant. ........................... ........................ : mémoire : : mémoire : : : 12345> : nocli : 00001 <----\ : : nomcli: : : nomcli: Durant \ : nocli.adr : x'12345' : :----------------------: \ :-------------------------: : : \ : *ENTRY PLIST : : CALL PGMB : \ : PARM nocli: : PARM NOCLI : \ : : : : \ : : : : \ : : : : \ : : : : \---- eval NOCLI = 1 : : : : : :......................: : : : : :.........................: pgm appelé (B) |
pgm appelant (A) UNIQUEMENT pour les paramètres reçus. ........................... ........................ : mémoire : : mémoire : : : ###### : nocli : 00001 : : nomcli: Dupont' <----------| : nomcli: Durant : : nocli.adr : x'12345' : | :----------------------: :-------------------------: | : : : *ENTRY PLIST : | : CALL PGMB : : PARM nocli: | : PARM NOCLI : : : | : : : : | : : : : | : : : : | : : : eval NOCLI = 1 : | : : : eval NOMCLI -----| :......................: : = 'Dupont': : : :.........................: pgm appelé (B) |
pgm appelant (A) A la fin du pgm appelé, retour à l'instruction qui suit le CALL. ........................... ........................ : mémoire : : mémoire : : : nocli : nocli : 00001 : : nomcli: Dupont' : = 1. : nomcli: Durant : : nocli.adr : x'12345' : :----------------------: :-------------------------: : : : *ENTRY PLIST : : CALL PGMB : : PARM nocli: : PARM NOCLI : : : : .... <-------------\ : : : .... : \ : : : : \ : : : : \ : eval NOCLI = 1 : : : \ : eval NOMCLI --: :......................: \ : = 'Dupont': \ eval *inlr=*on : :.........................: pgm appelé (B) |
Le pgm appelé peut rendre la main à l'appelant sans pour autant être désactivé. en RPG : RETURN en COBOL : EXIT PROGRAM. ATTENTION: ne fonctionne que si le pgm appelant est un COBOL. GOBACK en cas de doute. ou passer par un pgm COBOL "intermédiaire". en CL : cela est malheureusement impossible (RETURN = ENDPGM) [si cela est possible faire en sorte que ce soit le pgm CL qui appelle (d'entrée de jeu).] |
Si le pgm n'est pas désactivé: toute la phase de chargement est inhibée. sa mémoire reste intacte (telle quelle lors du RETURN/EXIT PROGRAM) il reste positionné sur ces fichiers (lecture séquentielle) en RPG , le cycle ne passe pas par la phase d'ouverture des fichiers. en COBOL, conditionner OPEN avec un flag (puisque la mémoire est intacte) Pour désactiver les pgms appelés (et fermer les fichiers) utiliser le pgm suivant: "MENAGE" (CLP) PGM ################################ RCLRSC lvl(*CALLER) # RCLACTGRP si DFTACTGRP(*no) # ENDPGM ################################ ou passez directement la commande sur la ligne de commandes |
si les deux programmes utilisent des fichiers écran (DSPF) les fichiers écrans sont suspendus et le système peut être "perdu" lors d'un réaffichage. Penser alors à indiquer RSTDSP(*YES) sur les commandes CRTDSPF ou CHGDSPF Afin que le système sauvegarde la totalité de l'image avant suspension du DSPF et la restaure avant réaffichage. Cette manipulation n'est, bien entendu, pas gratuite, particulièrement avec des terminaux éloignés. C'est pourquoi la valeur par défaut est à *NO sur la commande de création. |
RPG-IV propose une nouvelle manière de gérer les appels en format libre : 1/ déclaration préalable du programme appellé et des paramètres ==> un PROTOTYPE (type de déclaration PR) DProgrammeB PR EXTPGM('PGMB') D 6 0 ou dcl-PR ProgrammeB EXTPGM(PGMB'); *N char(6); end-pr; Sont définis : + le nom du programme, le nom externe(réel) pouvant être différent. + les paramètres transmis (nombre et types) les paramètres transmis supportent alors des mots-clés, comme : CONST ce paramètre peut ne PAS etre une variable(constante,calcul) VALUE passage de parmètre par valeur (pas de valeur retour) et d'autres... |
L'appel lui-même en spécif C ou en FORMAT LIBRE ==> nouveau code opération CALLP. (faculatif en format libre) /free ProgammeB(nocli) ; /end-free 2/ la réception des paramètres peut elle même être réalisée a/ soit, comme avant (*ENTRY PLIST) b/ ou en spécif D, avec protoype ET une interface de procédure ( PI ) DProgrammeB PR EXTPGM('PGMB') D 6 0 DProgrammeB PI D nocli 6 0 c/ avec DCL-PI en free-form RPG (voir exemple plus loin) |
Vous pouvez aussi appeler des procédures, car RPG-IV admet maintenant plusieurs procédures (au sens ILE) par source. Une procédure ressemble à : - un sous-programme : elle est codée dans le même source elle a des temps de réponse proches elle "voit" les fichiers ouverts par le pgm principal. - à un programme indépendant : elle occupe un niveau d'invocation (influence sur la gestion des erreurs) elle ne connait que ces propres variables. - ni à l'un, ni à l'autre, car : vous choisissez si elle utilisable par un autre pgm. elle peut se présenter sous la forme d'une fonction |
Déclaration d'une procédure ou d'une fonction [OBLIGATOIRE] la délaration se fait par le biais d'un prototype comme vu plus haut. Définition d'une procédure ou d'une fonction Une nouvelle spécif "P" pour définir le début et la fin de la fonction (proche de BEGSR / ENDSR) EXPORT autorise l'utilisation de cette procédure par des modules externes (sinon cette procédure est privée c'est à dire utilisable uniquement dans le même source) Des Spécifs D pour définir les paramètres recus (PI à la place de PR) Et le système VERIFIE que l'interface (PI) est indentique au prototype Des Spécifs D pour définir les variables locales des ordres de traitement (spécif C ou /free) |
structure générale d'un pgm RPG-IV : H Spécif H générale |contient NOMAIN s'il n'y a pas de pgm principal F Déclaration de fichiers |(ouvertures globales) D variables globales | D proc1 PR | prototype procédure 1 D proc2 PR | prototype procédure 2 I variables des fichiers | (globales) C traitement | programme principal O sorties | sorties du programme principal P proc1 B -| D PI | définition de l'interface D | variables locales C | traitement P E -| P proc2 B -| D PI | 2ème procédure (idem) D | C | P E -| |
Et tout cela nous ammène vers les fonctions, une fonction est une forme particulière de procédure qui : ° retourne une valeur (la définition est donnée sur les lignes PR ET PI) ° peut donc directement être utilisée en lieu et place de la valeur qu'elle retourne (comme une fonction intégrée %xxx) ° DOIT être déclarée par un prototype (CALL|CALLP sont invalides) Le prototype devant être déclaré aussi bien dans le source écrivant la fonction, que dans tous les sources qui l'utilisent, il est conseillé de placer le prototype dans un source à part et de copier ce source par /COPY fichier,membre On rejoint ansi une philosophie "langage C" ou toutes les fonctions sont externes au langage et ou il faut copier les fichiers de déclaration par les fameux "include stdio.h", etc... |
Exemple: /-- cette fonction retourne une DATE. / DFindemois PR D D D DATFMT(*ISO) P Findemois B * Définition de l'Interface de procédure (PI) * doit être conforme avec le prototype (vérifié à la compilation) D PI D D Datein D DATFMT(*ISO) * variables locales D Wdate S D //exemple : 15 avril + 1 mois => 15 Mai / -15 jours => 30 Avril /free wdate = DATEIN + %MONTHS(1); wdate = wdate - %days(%SUBDT(wdate:*D)) return wdate; /end-free P Findemois E |
Exemple (en free_from): /-- cette fonction retourne une DATE. dcl-PR Findemois DATE; *N DATE(*ISO); end-pr; dcl-proc Findemois; // Définition de l'Interface de procédure (DCL-PI) // doit être conforme avec le prototype (vérifié à la compilation) dcl-PI *N DATE; dateIN DATE(*ISO); end-pi; // variables locales; dcl-s Wdate DATE; //exemple : 15 avril + 1 mois => 15 Mai / -15 jours => 30 Avril wdate = DATIN + %MONTHS(1); wdate = wdate - %days(%SUBDT(wdate:*D)); return wdate; end-proc; |
les Appels peuvent être aussi statiques (==> phase de liage) LIEN STATIQUE: unité d'utilisation = procédure - partie de code à point d'entrée unique - réalise une fonction, une action - n'est PAS un objet OS/400 V3r10 + RPG : une procédure par source V3r60 : x procédures par source + COBOL : x procédures par source + C/400 : x procédure(s) par source Appel d'une procédure = "call bound" (CALLB en RPG, CALL PROCEDURE en COBOL, CALLPRC en CL) les procédures apparaissent dans la liste d'invocation (DSPJOB/opt 11) |
unité de compilation = module - résultat d'une compil. (base = un membre source) - contient une ou plusieurs procédures (code compilé) - c'est un objet OS/400 *MODULE - n'est PAS EXECUTABLE commande CRTRPGMOD unité d'exécution = programme - objet de type *PGM - lien dynamique (appel par CALL) commande CRTPGM AVANTAGES: - meilleures performances - meilleure intégration de langages différents - introduction des fonctions en C ET RPG-IV INCONVENIENTS: - duplication du code = moins de souplesse, duplication en mémoire = maintenance des applicatifs compliquée !!! |
CRTxxxMOD CRTPGM PGM(PROG01) + MODULE(A B C) ..fichier source............ : .RPG................... : : : : : ############### : : membre A : :-> # MODULE A # -- ####################### : :.....................: : ############### ! # PROG01 : *PGM # : : ! ####################### : .COBOL................. : ############### ! # # # # : : membre B : :-> # MODULE B # > # # # P1 # : :.....................: : ############### ! # A # B ######## : .C..................... : ! # # # # : : membre C | P1 | P2 : :-> ############### ! # # # P2 # : :.....................: : # MOD.C P1 P2 # -- # # # # :..........................: ############### ####################### la création de PROG02 à partir des modules A,E,F,G entraînerait la duplication du module A dans cet autre programme. |
Pour diminuer les problèmes de duplication de code pour les procédures souvent utilisées, ILE introduit un nouveau type d'objet: *SRVPGM (programme de service,équivalent des DLL Windows ou OS/2) Objet contenant un ensemble de modules (donc de procédures) Le lien est fait par référence (dynamique) à l'appel du pgm principal, ensuite l'appel d'une procédure est statique. (les modules système sont des *SRVPGM = bibliothèques de fonctions) |
*SRVPGM: CRTxxxMOD CRTSRVPGM SRVPGM(SRV01) + MODULE(B C) EXPORT(*ALL) ..fichier source............ : .RPG................... : : : : : ############### : : membre A : :-> # MODULE A # : :.....................: : ############### : : ################ : .COBOL................. : ############### -- # # # : : membre B : :-> # MODULE B # ! # # P1 # : :.....................: : ############### > # B ######## : .C..................... : ! # # # : : membre C | P1 | P2 : :-> ############### ! # # P2 # : :.....................: : # MOD.C P1 P2 # -- # # # :..........................: ############### ################ |
*SRVPGM: CRTxxxMOD CRTPGM PGM(PROG01) MODULE(A) BNDSRVPGM(SRV01) ..fichier source............ ################# : .RPG................... : # Prog01 # : : : : ############### # (ref : SRV01) # : : membre A : :-> # MODULE A # ------> ################# : :.....................: : ############### / : : ################ : .COBOL................. : ############### # # # : : membre B : :-> # MODULE B # # # P1 # : :.....................: : ############### # B ######## : .C..................... : # # # : : membre C | P1 | P2 : :-> ############### # # P2 # : :.....................: : # MOD.C P1 P2 # # # # :..........................: ############### ################ |
On peut modifier un module dans un programme existant sans re-linker : UPDPGM PGM(PROG01) MODULE(A) On peut modifier un programme de service de la même manière : UPDSRVPGM SRVPGM(SRV01) MODULE(B) Attention, si l'on ajoute un module dans le programme de service il y a alors un contrôle (type level-check) avec les programmes qui utilisent. il faut recompiler les programmes |
Pour faciliter le liage, il existe un nouveau type d'objet *BNDDIR (Binding directory) = répertoire de liage Commandes associées : CRTBNDDIR ADDBNDDIRE DLT " RMV " " DSP " WRK " " WRK " Il référence un ensemble de modules et de pgms de service dont peut avoir besoin une application, évitant de lister tous les modules à lier. Le programme de liage ira chercher dans cette "directory" toutes les références non résolues. (noms de modules et de fonctions non trouvés) ADDRDBDIRE REPERTOIRE SRV01 puis CRTPGM PGM(PROG01) MODULE(A) BNDDIR(REPERTOIRE) |