ILE CL で C 標準関数を使用する (メモリ操作とポインタ)

RPG で C ランタイム関数を使用する」で見たように、ILE 言語では相互に関数を利用しあうことができます。
CL も例外ではありません。

V5R4 から CL でもポインタを使用できるようになりましたが、どう扱うのがよいのかいろいろ調べているところです。
たとえば DCL 文の ADDRESS パラメータとか、%ADDRESS 組み込み関数や %OFFSET 組み込み関数などが使用できそうですが、RPG の %alloc みたいなものはないようにも見えます。

RPG の %alloc みたいなことをしたい時にはどうするのか、と調べている時に、C の標準関数である malloc が利用できそうなことがわかりました。開放する時には free が使用できるはずです。

ということで、実験してみました。


テストプログラム (1)

プログラムの全文です。

システム API を使用して、引数として指定したオブジェクトの作成日付を表示させる、というプログラムです。

             PGM        PARM(&OBJ &TYPE)                              
             DCL        VAR(&OBJ) TYPE(*CHAR) LEN(20)                 
             DCL        VAR(&TYPE) TYPE(*CHAR) LEN(7)                 
             DCL        VAR(&RCPTR) TYPE(*PTR)                        
             DCL        VAR(&RC) TYPE(*CHAR) STG(*BASED) LEN(256) +   
                          BASPTR(&RCPTR)                              
             DCL        VAR(&RCSIZE) TYPE(*INT) VALUE(256)            
             DCL        VAR(&BYTERTN) TYPE(*INT) STG(*DEFINED) +      
                          LEN(4) DEFVAR(&RC)                          
             DCL        VAR(&BYTEAVL) TYPE(*INT) STG(*DEFINED) +      
                          LEN(4) DEFVAR(&RC 5)                        
             DCL        VAR(&CRTDATE) TYPE(*CHAR) STG(*DEFINED) +     
                          LEN(6) DEFVAR(&RC 66)                       
             CALLPRC    PRC('malloc') PARM((&RCSIZE *BYVAL)) +        
                          RTNVAL(&RCPTR)                              
             CALL       PGM(QUSROBJD) PARM(&RC &RCSIZE 'OBJD0100' +    
                          &OBJ &TYPE)                                  
             IF         COND(&BYTERTN = 0) THEN(GOTO CMDLBL(END))      
             SNDUSRMSG  MSG(&CRTDATE) MSGTYPE(*INFO) TOMSGQ(*EXT)      
 END:        CALLPRC    PRC('free') PARM((&RCPTR *BYVAL))              
             ENDPGM

API の仕様

今回、例に利用している API は QUSROBJD です。
System API プログラミングの基礎 (1) - 2つの結果取得方法 -」の 2つの結果取得方法のうち "Receiver variable" を使うものになります。

Retrieve Object Description (QUSROBJD) API


  Required Parameter Group:

1 Receiver variable Output Char(*)
2 Length of receiver variable Input Binary(4)
3 Format name Input Char(8)
4 Object and library name Input Char(20)
5 Object type Input Char(10)

  Optional Parameter Group 1:

6 Error code I/O Char(*)

  Optional Parameter Group 2:

7 Auxiliary storage pool (ASP) control Input Char(*)

  Default Public Authority: *USE

  Threadsafe: Yes

↑を CL で呼び出すために、↓のようにしています。

CALL PGM(QUSROBJD) PARM(&RC &RCSIZE 'OBJD0100' &OBJ &TYPE)

検索される情報のフォーマットは OBJD0100 というもので、↓のようになっています。

OBJD0100 Format

The following information is returned for the OBJD0100 format. For detailed descriptions of the fields in the table, see Field Descriptions.
Offset Type Field
Dec Hex
0 0 BINARY(4) Bytes returned
4 4 BINARY(4) Bytes available
8 8 CHAR(10) Object name
18 12 CHAR(10) Object library name
28 1C CHAR(10) Object type
38 26 CHAR(10) Return library
48 30 BINARY(4) Start of changeObject auxiliary storage pool (ASP) numberEnd of change
52 34 CHAR(10) Object owner
62 3E CHAR(2) Object domain
64 40 CHAR(13) Creation date and time
77 4D CHAR(13) Object change date and time

プログラムの作成

CL モジュール自体は特別に指定はせず普通に CRTCLMOD コマンドで作成できます。

C の関数である malloc/free を使用するため、CRTPGM 時に BNDDIR(QC2LE) を指定します。

実行

とりあえずテストプログラムの段階なので、20桁のオブジェクト名と 7桁のオブジェクト・タイプを直接指定して、プログラムをコールします。

結果はこんなふうに表示されます。

実行後の画面です。

結果の確認

作成日付が 05/10/26 ですから、051026 という数字は正しい結果なわけですね。


テストプログラム (2)

メモリの操作

メモリを操作して値を取得してくるような処理を追加してみました。

             PGM        PARM(&OBJ &TYPE)                                        
             DCL        VAR(&OBJ) TYPE(*CHAR) LEN(20)                           
             DCL        VAR(&TYPE) TYPE(*CHAR) LEN(7)                           
             DCL        VAR(&RCPTR) TYPE(*PTR)                                  
             DCL        VAR(&RC) TYPE(*CHAR) STG(*BASED) LEN(256) +             
                          BASPTR(&RCPTR)                                        
             DCL        VAR(&RCSIZE) TYPE(*INT) VALUE(256)                      
             DCL        VAR(&BYTERTN) TYPE(*INT) STG(*DEFINED) +                
                          LEN(4) DEFVAR(&RC)                                    
             DCL        VAR(&BYTEAVL) TYPE(*INT) STG(*DEFINED) +                
                          LEN(4) DEFVAR(&RC 5)                                  
             DCL        VAR(&CRTDATE) TYPE(*CHAR) STG(*DEFINED) +               
                          LEN(6) DEFVAR(&RC 66)                                 
             DCL        VAR(&VALPTR) TYPE(*PTR)                                 
             DCL        VAR(&OWNER) TYPE(*CHAR) STG(*BASED) LEN(10) +           
                          BASPTR(&VALPTR)                                       
             CALLPRC    PRC('malloc') PARM((&RCSIZE *BYVAL)) +                  
                          RTNVAL(&RCPTR)                                        
             CALL       PGM(QUSROBJD) PARM(&RC &RCSIZE 'OBJD0100' +             
                          &OBJ &TYPE)                                           
             IF         COND(&BYTERTN = 0) THEN(GOTO CMDLBL(END))               
             SNDUSRMSG  MSG(&CRTDATE) MSGTYPE(*INFO) TOMSGQ(*EXT)               
             CHGVAR     VAR(&VALPTR) VALUE(&RCPTR)                              
             CHGVAR     VAR(%OFS(&VALPTR)) VALUE(%OFS(&VALPTR) + 52)            
             SNDUSRMSG  MSG(&OWNER) MSGTYPE(*INFO) TOMSGQ(*EXT)                 
 END:        CALLPRC    PRC('free') PARM((&RCPTR *BYVAL))                       
             ENDPGM

追加したのは以下の処理です。
ポインタのオフセットを操作して返り値の中の情報を取ってくるようにしています。

             DCL        VAR(&VALPTR) TYPE(*PTR)                                 
             DCL        VAR(&OWNER) TYPE(*CHAR) STG(*BASED) LEN(10) +           
                          BASPTR(&VALPTR)                                       

             CHGVAR     VAR(&VALPTR) VALUE(&RCPTR)                              
             CHGVAR     VAR(%OFS(&VALPTR)) VALUE(%OFS(&VALPTR) + 52)            
             SNDUSRMSG  MSG(&OWNER) MSGTYPE(*INFO) TOMSGQ(*EXT)                 

先のメッセージに続いて、所有者がメッセージされるようになります。

ポインタ変数の状況確認

メモリの動きを見てみましょう。
デバッガを使用して変数の内容を見ていきます。

malloc が呼ばれる前のポインタ変数には値が入っていません。

malloc の実行後に値が入ってきていることがわかります。

実行結果

こちらも正しい結果が得られていることは、「テストプログラム (1)」の「オブジェクト記述の表示」結果から確認できますね。

考慮点

free する位置、たとえば、結果がなかったときの GOTO 先で必ず free を行う、とか、free する時には正しいアドレスを指定する、つまり、ちゃんと一番最初にメモリをアロケイトした時のアドレスで free を行う、とか、まぁ基本的ですがうっかりしてしまいがちな考慮点はあります。

が、i5/OS ではポインタの操作を間違えてシステム領域を壊してしまった、とかそういったことはありませんので、他プラットフォームでのポインタの使用に比較するとはるかに安全です。

そういった観点からも、プログラミングの初心者にとっては ILE 環境、ILE RPG というのはとてもいい入り口のような気がしますね。
一番の問題は IBM をはじめとした System i の関係者がそのことを理解しているか、ということなのでは ...... うーむオソロシイ ......

[Top Pageに戻る]

Ads by TOK2