0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[05] C言語でコンソールから関数を呼び出す機構の振り返り ... コンソールからの関数呼び出し実行

Last updated at Posted at 2021-08-07
本シリーズのトップページ
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.hcfn.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 関数以外) とその引数を入力する

実行例である.

image.png

 

以上.

  1. parser という名称なので、外部関数の呼び出しは別関数の方が良いだろう と今見ると思う

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?