関数ポインタ

■関数のアドレス

	メモリは変数の値を保存していますが、関数の情報も保存しています。
	関数は「関数名(引数)」とコーディングすることで呼び出していましたが、
	プログラム上では「関数のアドレス(引数)」として扱われおり、
	printf関数で関数名のみを指定するとその関数のアドレスを確認することができます。

	●関数のアドレス表示例:
		#include <stdio.h>
		
		void main(void)
		{
			printf("main = %d\n", main);
		}

		結果:
			main関数のアドレスが表示される

■関数ポインタ

	関数もメモリ上でアドレスを管理しているので、ポインタ変数を用意して
	変数として格納することができます。
	関数をポインタとして使用した変数のことを関数ポインタと呼びます。

	●関数ポインタの型
		戻り値の型 (*)(引数情報)

	●書式:
		戻り値の型 (*変数名)(引数情報);

	●具定例:
		// 戻り値なし、引数なしの関数型の変数pfunc
		void (*pfunc)();

	●使用例:
		void MyName(void)
		{
			printf("山田太郎\n");
		}

		void main(void)
		{
			// 関数ポインタを宣言
			void (*pfunc)();

			pfunc = MyName;
			printf("MyName = %p", MyName);
			printf("pfunc = %p", pfunc);
			pfunc();
		}

		実行結果:
			MyNameのアドレス
			pfuncの値
			山田太郎

	関数ポインタの宣言は本来関数名の部分を*つきの変数名に置き換えます。
	*と変数名は()で括る必要があります。
	()を使用しないと戻り値の型のポインタとして判断されますので、()は必ずつけてください。
	関数ポインタには関数のアドレスを代入することが可能です。

	●関数ポインタの使用方法
		関数のアドレスを代入された関数ポインタから関数を呼び出す方法は
		「関数ポインタ変数名(引数情報)」で可能です。
		古いC言語の仕様では「(*関数ポインタ変数名)(引数情報)」となっていましたが、
		現在、()と*は不要となっています。

		#include <stdio.h>

		void PrintNum(int num)
		{
			printf("num = %d\n", num);
		}

		void main(void)
		{
			void (*pfunc)(int);

			pfunc = PrintNum;

			(*pfunc)(1);
			pfunc(2);
		}

		実行結果:
			num = 1
			num = 2

	●関数ポインタの利点
		関数ポインタの利点は直接関数名を書いて呼び出さなければいけなかった所を
		間接的に呼び出すことが可能になるという点です。
		間接的な関数の切り替えは関数の配列を使用して実装可能です。
		
		・関数配列の例:
			#include <stdio.h>
			#include <memory.h>
			enum 
			{
				INIT,	// 初期化
				RUN,	// 実行
				DELETE,	// 終了
				END,
			};

			typedef struct
			{
				int m_Step;
				int m_Counter;
			} GAME_INFO;

			typedef void (*GAME_FUNC)(GAME_INFO*);

			void Init(GAME_INFO *game_info)
			{
				// 初期化処理
				game_info->m_Step = RUN;
				printf("初期化\n");
			}

			void Run(GAME_INFO *game_info)
			{
				// 実行処理
				game_info->m_Counter++;

				if (game_info->m_Counter > 30)
				{
					game_info->m_Step = DELETE;
				}
				printf("実行\n");
			}

			void Delete(GAME_INFO *game_info)
			{
				// 終了処理
				game_info->m_Step = END;
				printf("終了\n");
			}

			void main(void)
			{
				//void (*pfunc[])(GAME_INFO *) = {
				GAME_FUNC pfunc[] = {
					Init,
					Run,
					Delete,
					NULL
				};
				
				GAME_INFO gi;
				memset(&gi, 0, sizeof(GAME_INFO));
				while(pfunc[gi.m_Step] != NULL)
				{
					pfunc[gi.m_Step](&gi);
				}
			}

			実行結果:
				初期化
				実行 * 30
				終了

		上の例では関数ポインタの配列を使用してInit、Run、Deleteを切り替えています。
		本来はifやswitchで関数の指定が必要ですが、今回は関数ポインタの配列を
		使用していますのでwhileとm_Stepの値で関数の切り替えを行うことができ、
		ifやswitchを使用した場合よりも呼び出し側の構造はシンプルなものになっています。

■コールバック関数

	コールバック関数とは関数呼び出しの引数に渡す関数のことです。
	コールバック関数は、呼び出し先で何らかのイベントが発生した際に、
	そのイベントに対応する処理を指定する目的で使用されることが多いです。
	
	●イベント例:
		アプリ起動時
		ホームボタンを押してアプリがバックグラウンドに移動する時
		バックグラウンドが再度呼び出される時

	OS側がイベントを検知し、その際に呼び出されるコールバック関数が用意されているので、
	その関数に個々の対応処理をコーディングして対応します。
	※自分でイベントに応じたコールバック関数を登録する場合もあります。

	●コールバック関数の例:
		#include <stdio.h>

		float Add(float x, float y)
		{
			return x + y;
		}

		float Sub(float x, float y)
		{
			return x - y;
		}

		float Multiple(float x, float y)
		{
			return x * y;
		}

		float Division(float x, float y)
		{
			return y != 0.0f ? x / y : 0.0f;
		}

		// check_numより大きかったらtrue、小さかったらfalseを返す
		bool IsOverNum(float check_num, float x, float y, 
						float (*callback)(float, float))
		{
			float result = callback(x, y);
			if (result > check_num) {
				return true;
			}

			return false;
		}

		void main(void)
		{
			bool add = IsOverNum(25, 10, 20, Add);
			bool sub = IsOverNum(25, 10, 20, Sub);
			bool mul = IsOverNum(25, 10, 20, Multiple);
			bool div = IsOverNum(25, 10, 20, Division);

			printf("add = %s\n", add == true ? "true" : "false");
			printf("sub = %s\n", sub == true ? "true" : "false");
			printf("mul = %s\n", mul == true ? "true" : "false");
			printf("div = %s\n", div == true ? "true" : "false");
		}

		結果:
			add = true
			sub = false
			mul = true
			div = false