概要
opensource COBOLはCOBOLプログラムをC言語へと変換して、GCC等のCコンパイラでコンパイルし実行可能としています。
そのため、C言語で作られたアプリケーションとは親和性が高く、容易に連携させることができると想定されます。
今回はこれらの連携について試してみます。
COBOLの外部アプリケーション呼び出し
CALL文
そもそもCOBOLの外部アプリケーションで呼び出す場合には、CALL
命令を利用します。
使い方は以下のような形です。
******************************************************************
IDENTIFICATION DIVISION.
******************************************************************
PROGRAM-ID. CALL01.
AUTHOR. nor51010.
******************************************************************
ENVIRONMENT DIVISION.
******************************************************************
DATA DIVISION.
******************************************************************
WORKING-STORAGE SECTION.
01 F01 PIC 9(6).
01 RET PIC 9(4) COMP-X.
******************************************************************
PROCEDURE DIVISION.
******************************************************************
MAIN-RTN.
MOVE 123456 TO F01.
CALL "CALL02" USING F01
GIVING RET.
DISPLAY "RETURN-CODE:" RET.
MAIN-EXT.
STOP RUN.
CALL
の次に呼び出すアプリケーション名を指定し、USING
の後ろに引数を記載します。
また、GIVING
で返り値を取得することができます。
引き続いて呼ばれる側です。呼ばれる側の例は以下のようになります。
******************************************************************
IDENTIFICATION DIVISION.
******************************************************************
PROGRAM-ID. CALL02.
AUTHOR. nor51010.
******************************************************************
ENVIRONMENT DIVISION.
******************************************************************
DATA DIVISION.
******************************************************************
WORKING-STORAGE SECTION.
******************************************************************
LINKAGE SECTION.
01 F01 PIC 9(6).
******************************************************************
PROCEDURE DIVISION USING F01.
******************************************************************
MAIN-RTN.
DISPLAY F01.
MOVE 10 TO RETURN-CODE.
MAIN-EXT.
GOBACK.
呼び出される側では、引数としてもらう変数をLINKAGE SECTION
に記載します。
また、PROCEDURE DIVISION
の後ろにUSING
で受ける変数名を記載します。
これにより、呼び元のアプリケーションから引数の値を受け取れます。
また、RETURN-CODE
に値を設定することで、呼び出し元のアプリケーションに返り値を渡すことができます。
この値は数値のみとなります。
参照渡し、値渡し
引数を渡すアプリケーションを作成する際、避けては通れない問題として、「参照渡し」、「値渡し」があります。
COBOLでは非常に単純化されてわかりやすくなっています。
USING
の後ろにBY VALUE
を指定していれば値渡し、省略されているかBY REFERENCE
が指定されていれば参照渡しになります。
これらの指定は呼び出し元、呼び出し先で一致している必要があります。
* 参照渡し
CALL "CALL02" USING BY REFERENCE F01.
* 値渡し
CALL "CALL02" USING BY VALUE F01.
* 参照受け取り
PROCEDURE DIVISION USING BY REFERENCE F01.
* 値受け取り
PROCEDURE DIVISION USING BY VALUE F01.
これらは他のプログラミング言語の概念と同様と考えて問題ありません。
- 参照渡しイメージ
void
CALL01_ ()
{
int f01, ret;
f01 = 123456;
ret = CALL02_ (&f01);
printf ("%d\n", ret);
}
int
CALL02_ (int *f01)
{
printf ("%d\n", *f01);
return 10;
}
- 値渡しイメージ
void
CALL01 ()
{
int f01, ret;
f01 = 123456;
ret = CALL02 (f01);
printf ("%d\n", ret);
}
int
CALL02 (int f01)
{
printf ("%d\n", f01);
return 10;
}
※注意点:opensource COBOLでは値渡しを選択できるのは数値項目のみとなっています。
文字列項目(X, N)は参照渡しのみが可能です。
C言語との連携
CアプリケーションのCALL
これまでを踏まえてC言語と連携してみます。まずは引数無しの連携として、COBOLからC言語の呼び出しを実装します。
COBOLアプリケーションは通常のCALL文で実装します。
******************************************************************
IDENTIFICATION DIVISION.
******************************************************************
PROGRAM-ID. CALL03.
AUTHOR. nor51010.
******************************************************************
ENVIRONMENT DIVISION.
******************************************************************
DATA DIVISION.
******************************************************************
WORKING-STORAGE SECTION.
******************************************************************
PROCEDURE DIVISION
******************************************************************
MAIN-RTN.
CALL "cfunction".
MAIN-EXT.
GOBACK.
C言語側は該当の関数を実装するだけです。ここでファイル名は関数名と合わせておきます。
#include <stdio.h>
int cfunction ()
{
printf ("cfunction called.\n");
return 0;
}
あとはコンパイルして連携します。
連携方法としては動的ライブラリとして参照する場合、と静的リンクをしてしまう方法があります。
- 静的リンク
Cアプリケーションから.oファイルを作成し、COBOLのコンパイル時にリンクします。
cobcの引数に.oファイルを引き渡すことでリンクを行ってくれます。
※Cのコンパイル時には-fPIC
が必要となります。
[user@localhost test]$ gcc -fPIC -c cfunction.c
[user@localhost test]$ cobc -x CALL03.cbl cfunction.o
[user@localhost test]$ ./CALL03
cfunction called.
- 動的リンク1
静的リンクではCアプリケーションが作り直しになる度、全コンパイルが必要となり手間がかかります。
opensource COBOLでは動的ライブラリの呼び出しもできるため、そちらも試してみます。
[user@localhost test]$ gcc --shared -fPIC -o cfunction.so cfunction.c
[user@localhost test]$ cobc src/CALL03.cbl
[user@localhost test]$ cobcrun CALL03
cfunction called.
ただし、この場合では、Cから作成した.soファイルの名前がファンクション名と異なると呼び出せずエラーとなります。
そのため、通常はリンクする必要があります。
-l
オプションでリンクするためにCからコンパイルした動的ライブラリは先頭にlibをつける必要があります。
[user@localhost test]$ gcc --shared -fPIC -o libcfunction.so cfunction.c
[user@localhost test]$ cobc -L. -lcfunction src/CALL03.cbl
[user@localhost test]$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
[user@localhost test]$ cobcrun CALL03
cfunction called.
リンクさえしてしまえば、COBOLから簡単にCアプリケーションを呼び出せました。
引数の連携
次はCアプリケーションに引数を渡してみようと思います。
opensource COBOLからの引数は以下の対応表のようになります。
※()は代表的な型
COBOLデータ型 | 渡し方 | Cデータ型 |
---|---|---|
数値型(PIC 9) | 値渡し | int |
文字列型(PIC X) | 参照渡し | char * |
バイナリ型(COMP-X) | 値渡し | short,int,long(COBOLのサイズによる) |
バイナリ型(COMP-X) | 参照渡し | short *,int *,long *(COBOLのサイズによる) |
以上から、基本的に数値は値渡し、文字列は参照渡しとし、返り値または文字列領域に値を返すことで連携ができます。
幸い、COBOLは文字列から数値型への変換も容易なので、そのような返し方もできるでしょう。
注意点として、COBOLの文字列は、必ずしも末尾がNULL
(\0
)になっているとは限りません。
メモリ破壊などの要因になりますので、FILLERを追加するなど、何等かの手段で末尾がLOW-VALUE
になるようにしたほうがよいです。
例は以下のようになります。
******************************************************************
IDENTIFICATION DIVISION.
******************************************************************
PROGRAM-ID. CALL04.
AUTHOR. nor51010.
******************************************************************
ENVIRONMENT DIVISION.
******************************************************************
DATA DIVISION.
******************************************************************
WORKING-STORAGE SECTION.
01 F01 PIC 9(03).
01 G01.
03 F02 PIC X(20).
03 FILLER PIC X(1) VALUE LOW-VALUE.
******************************************************************
PROCEDURE DIVISION.
******************************************************************
MAIN-RTN.
MOVE 123 TO F01.
MOVE "hello world!" TO F02.
CALL "cfunction2" USING BY VALUE F01
BY REFERENCE F02.
MAIN-EXT.
GOBACK.
#include <stdio.h>
int
cfunction2 (int f01, char *f02)
{
printf ("cfunction2 called.\n");
printf ("f01:%d\n", f01);
printf ("f02:%s\n", f02);
return 0;
}
実行例は次のとおりです。
[user@localhost test]$ gcc -shared -fPIC -o libcfunction2.so cfunction2.c
[user@localhost test]$ cobc -L. -lcfunction2 CALL04.cbl
[user@localhost test]$ cobcrun CALL04
cfunction2 called.
f01:123
f02:hello world!
正しくCアプリケーションに引数が渡せていることがわかります。
連携例
Cと連携できることはわかりましたが、これで何ができるかというのが重要かと思います。
この利点は、opensource COBOLで対応されていないシステムコールを呼び出したり、外部ライブラリとアクセスするためのラッパーを作ることで、オープン環境の便利機能をCOBOLから利用可能になるといった点です。
forkするアプリケーションを呼び出してCOBOLなのに並列処理をしてみたりと夢が広がりますが、まずは現実的な例で実現してみます。
COBOLのログをsyslogに出してみる
COBOLからDISPLAYを使ったり、ファイルに書き出したりしてログを出力することはよくありますが、他のアプリケーションとログを一括で扱うためにはsyslogに書き出せると便利です。
このため、syslogに出力するCアプリケーションを作成し、ログの出力をそちらに任せてみます。
- Cプログラム
syslog.h
を読み込み、COBOLから受け取った文字列をsyslog
用の関数に入れるだけのラッパーです。
syslog関数についての詳細はマニュアル等を参照してください。
出力先は取り急ぎLOCAL3
としてます。
#include <syslog.h>
void c_openlog (const char *, int, int);
void c_syslog (int, const char *);
void c_closelog (void);
void
csyslog (const char *ident, int cobpriority, const char *output)
{
int priority;
if (cobpriority == 0) {
priority = LOG_ERR;
} else if (cobpriority == 0) {
priority = LOG_INFO;
} else {
priority = LOG_DEBUG;
}
c_openlog (ident, LOG_PID, LOG_LOCAL3);
c_syslog (priority, output);
c_closelog ();
}
void
c_openlog (const char *ident, int option, int facility)
{
openlog (ident, option, facility);
}
void
c_syslog (int priority, const char *output)
{
syslog(priority, "%s\n", output);
}
void
c_closelog (void)
{
closelog ();
}
- COBOL
COBOLプログラムからはログのレベルと出力文字列を引き渡します。
******************************************************************
IDENTIFICATION DIVISION.
******************************************************************
PROGRAM-ID. SYSLOG01.
AUTHOR. nor51010.
******************************************************************
ENVIRONMENT DIVISION.
******************************************************************
DATA DIVISION.
******************************************************************
WORKING-STORAGE SECTION.
01 F01 PIC 9(01).
01 G01.
03 F02 PIC X(20).
03 FILLER PIC X(1) VALUE LOW-VALUE.
******************************************************************
PROCEDURE DIVISION.
******************************************************************
MAIN-RTN.
MOVE 0 TO F01.
MOVE "ERROR MESSAGE." TO F02.
CALL "csyslog" USING BY REFERENCE "SYSLOG01"
BY VALUE F01
BY REFERENCE F02.
MOVE 1 TO F01.
MOVE "INFO MESSAGE." TO F02.
CALL "csyslog" USING BY REFERENCE "SYSLOG01"
BY VALUE F01
BY REFERENCE F02.
MAIN-EXT.
GOBACK.
実行する前にsyslogの設定ファイルを修正し、LOCAL3の出力を定義します。
渡しの環境で編集した例を記載します。
LOCAL3のログを/var/log/my.logに出力します。
<前略>
#nor51010 add
local3.* /var/log/my.log
実際に実行してみます。
[user@localhost test]$ gcc -shared -fPIC -o libcsyslog.so csyslog.c
[user@localhost test]$ cobc -L. -lcsyslog SYSLOG01.cbl
[user@localhost test]$ cobcrun SYSLOG01
[root@localhost log]# cat /var/log/my.log
Jul 31 11:38:58 localhost SYSLOG01[24882]: ERROR MESSAGE.
Jul 31 11:38:58 localhost SYSLOG01[24882]: INFO MESSAGE.
以上でCOBOLからsyslogへの出力を対応することができました。
最後に
opensource COBOLはその性質上、Cアプリケーションとの連携が簡単にできるため、オープン系の機能を利用するための拡張が容易にできることがわかりました。
クラウド向けのライブラリなどと連携することで、コンテナクラウドへの親和性が一気に高まると思いますので、いろいろできそうですね。
また、Cからopensource COBOLの呼び出しについてもそのうち触れたいと思います。