本シリーズのトップページ |
---|
https://qiita.com/robozushi10/items/fc185b5da0509b9a631f |
はじめに
本記事の趣旨は「01 概要」に記しているが、
13年前(=2008年) に職場のプログラムの挙動をマネて実装した
「コンソールから関数を呼び出す機構」の振り返りである.
本項では次の (5)(6)(7) について記す.
(5) (4) のヘッダファイルを include して、コンソールから関数呼び出し機能も持つコードも含めてリンクする
⬇️
(6) 作成された実行ファイル a.out を実行する
⬇️
(7) a.out の stdin から、公開関数 (static 関数以外) とその引数を入力する
⬇️
詳細
(5) について
下記については次の通りである.
(5) (4) のヘッダファイルを include して、コンソールから関数呼び出し機能も持つコードも含めてリンクする
次のようにコンパイルとリンクをして、a.out を作成する
Warning が結構出てしまうので、Warning をビルドエラー扱いにしていると動かない.
当時の職場では、静的テストの対象 (C++test) から同 Warning は検出対象外にされていたのだと思う.
$ gcc -g cfn_invoke.c priv_cfn_invoke.h test_driver.o
priv_cfn_invoke.h
次の🌟の通り「(4) readelf -s」で生成した cfn.extern.h
と cfn.tbl.h
を include している.
略
/*========*/
/* extern */
/*========*/
#include "cfn.extern.h" 🌟
/*==========*/
/* function */
/*==========*/
static fn_tbl_t
g_fn_tbl[] =
{
#include "cfn.tbl.h" 🌟
};
#endif /* __CFN_INTERP_H__ */
cfn_invoke.c
コンソールからの入力を待受けるプログラム.
parser_start
の中で、コンソール入力された文字列の解析 と 関数呼び出しをしている1
略
#include "priv_cfn_invoke.h"
略
/* 外部提供I/F */
int
Invoke_from_stdin(void)
{
parser_start(); //💥
return 0;
}
入力文字列を解析して、改行を区切りにして関数呼び出し🛑をする
/* パーサスタート */
static void
parser_start(void) //💥
{
enum { OK, NG };
e_token token = TOK_token_unknown;
e_atype type = TYPE_atype_unknown;
int retval = 0;
int argc = 0;
ainfo_t alist[MAXARG+1];
int mode = NORMAL_MODE;
char word[MAXWORD];
/* ignoresig(); */
while(1)
{
switch(token = do_lexer(word, &type, mode)) /* lexer(字句解析器)コール */
{
略
case TOK_NL: /* 改行 */
if((argc == 0) && (token != TOK_NL))
{
WARN("Illegal sequence\n");
}
else if(argc > 0)
{
retval = invoke(argc, &alist[0]);🛑
}
略
} // parser_start(void) の終了
以下は、上の invoke関数🛑の中身である.
/*
* 関数ポインタと関数の引数を取得し,引数の個数に応じた
* 関数をコールする
*/
static int
invoke(int argc, ainfo_t * alist)
{
enum {not_found = -1};
int ret = 0;
int (*func)() = NULL;
void * al[MAXARG];
memset(al, 0, sizeof(void *) * MAXARG);
dbg_dump_args(argc, alist); /* debug */
/* シンボル(関数)名からアドレスを取得する */
func = conv_sym_2_addr(alist[0].sym); //🌟 "piyo" と入力されたら、&piyo を返す関数
if(func == NULL)
{
WARN("not found %s\n", alist[0].sym);
return not_found;
}
/* 引数(alist)の型変換を行い,alに格納する */
dym_cast_args (argc, alist, al); //
/* 引数の個数別に応じた関数をコールする */
ret = invoke_by_argc(func, al, argc); //🌟 内部では piyo (al[1], al[2]) として関数呼び出しをしている.
return ret;
}
(6) について
下記については次の通りである.
作成された実行ファイル a.out を実行する
a.out を実行すると関数入力モードになる.
なお、実際のプログラムでは、stdin の待受けは別スレッドにしている.
$ ./a.out
(7) について
下記については次の通りである.
(7) a.out の stdin から、公開関数 (static 関数以外) とその引数を入力する
実行例である.
以上.
-
parser という名称なので、外部関数の呼び出しは別関数の方が良いだろう と今見ると思う ↩