第四報:怪しい関数内関数

 C言語では関数内関数は定義できません。関数はファイルスコープでしか定義できない。それがC言語の文法です。Pascal では作れ、FORTRAN でも文関数くらいなら作れるというのに、何でかC言語では作れません。何で作れないようにしたのかは分かりませんが、そうなっているので仕方がありません。

 このことはC++でも同じ事のはずなのですが、何とC++では工夫すれば関数内関数が作れてしまいます。C++講座の構造体の章を書いていたら、ふと思いついてしまいました。でやってみたら、何とできるじゃありませんか。びっくりしましたが、聞いてみると結構気づいている人もいるみたいです。

 方法は以下の通りです。

 以上です。「ローカル関数は作れません」というエラーが出るものと思っていましたが、コンパイル通りました。動作も問題ありませんでした。

 では、実際に作ってみた例を。

// InnerFn1.cpp
#include <stdio.h>
#include <conio.h>
#include <io.h>
#include <direct.h>
#include <sys\stat.h>

// デフォルトで public にするため struct にしてます
#define innerfn     struct InnerFn

// ディレクトリ以外のファイルの数を返す関数
int GetNumOfFiles(const char* pszPath)
{
    innerfn
    {
        // 関数内関数
        static int Sub_GetNumOfFiles(const char* pszPath)
        {
            int         nFiles = 0;  // ファイル数
            _finddata_t find;        // ファイル検索構造体
            long        hFind;       // ファイル検索ハンドル

            // カレントディレクトリの移動
            _chdir(pszPath);

            // 検索
            hFind = _findfirst("*.*", &find);
            if(hFind != -1)
            {
                do
                {
                    // ".", ".." を無視
                    if(find.name[0] == '.')
                        continue;

                    // ディレクトリなら再帰
                    if(find.attrib & _A_SUBDIR)
                        nFiles += Sub_GetNumOfFiles(find.name);
                    else
                        nFiles++;
                }
                while(_findnext(hFind, &find) == 0);
            }

            // 親ディレクトリに戻る
            _chdir("..");

            // ファイル数を返す
            return nFiles;
        }
    };

    char         szCurDir[_MAX_PATH];  // カレントディレクトリの退避領域
    int          nFiles;               // ファイル数
    struct _stat stat;                 // _stat 構造体

    // ファイルの存在確認
    if(_stat(pszPath, &stat) == -1)
        return -1;

    // ただのファイルの時は1を返す
    if(stat.st_mode & _S_IFDIR == 0)
        return 1;

    // カレントディレクトリの保存
    _getcwd(szCurDir, sizeof szCurDir);
    // ディレクトリの時は関数内関数を呼ぶ
    nFiles = InnerFn::Sub_GetNumOfFiles(pszPath);
    // カレントディレクトリの復帰
    _chdir(szCurDir);

    return nFiles;
}

int main()
{
    int  nFiles;
    char szPath[512];

    printf("ファイル名を入力して下さい > ");
    gets(szPath);
    nFiles = GetNumOfFiles(szPath);
    if(nFiles == -1)
        printf("パスが見つかりません");
    else
        printf("ファイル数は %d です。", nFiles);

    getch();
    return 0;
}

 何とも怪しい書式です(笑)。この場合インライン展開されませんが、インライン展開の条件を満たしていればインライン展開されてしまいます(まぁ、ここら辺はあまり問題ないでしょう)。

 何はともあれ、これで関数内関数の定義ができました。実際動作も正常ですし、デバッガもきちんと動作します。インテリセンスはうまくいかないみたいですけど、これは何が原因で変になるかわからないので(笑)、これが原因ではないかも知れません。クラスビューにも表示されませんが、クラスビューなんぞ使わないので(笑)、これも問題ありません。

 しかし、この怪しい書式を使う気は正直あまりしません。よくよく考えてみるとローカルクラスのメンバ関数ということで作れて当たり前なのですが、パッと見分かりにくく、実用はあまりすすめられませんね。でも、小さな関数くらいなら問題ないでしょうか?

 さぁ、あなたはこの関数内関数、使いますか?


追記

 インテリセンスがうまくいかないのはマクロのせいでした。直接 sturct InnerFn と書いてやれば OK です。ただ、クラスビューに載らないのは仕様のようです。


目次に戻る

トップページに戻る

Last update was done on 2002.6.10