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 1 year has passed since last update.

[C++] 関数ポインタの練習

Last updated at Posted at 2021-04-23

もくじ

やりたいこと

関数ポインタというものに久しぶりに触る機会があった。
昔から「むずかしいもの」というイメージが関数ポインタにはあったので、思い出しがてら練習をしたときのメモ。

作ったもの

構成

exe側で用意した関数を、dll側でなにかの条件判定をして、条件を満たしたときに呼んでやる、みたいな練習をしようと考えた。

で、思いついたのが世界のナベアツだったので、「条件」はそれにした。
下図がざっくりイメージ。

<ファイル構成>

成果物ファイル名 説明
WindowsProject.exe ダイアログベースのcppアプリ
DllTest.dll 上のexeから呼ばれるdll

<構成図>
image.png

<画面イメージ>
image.png

<コードのやることイメージ>

  • 起動時、exeの持っている関数をdllに登録する。
  • Button1を押したら、dllが秒数のカウントを開始。
  • カウント中の秒数があの条件を満たしていたらあほになる。満たしてなかったら普通になる。

コード

WindowsProject1.cpp
#include <windows.h>
#include <string>
#include "framework.h"
#include "WindowsProject1.h"
#include "resource.h"
#include "DllTest.h"

HINSTANCE hInst;
HWND hDlgWnd;

// 世界のナベアツ 3の倍数と3がつく数字の時だけアホになります
// https://www.youtube.com/watch?v=wjXoqcrLBbA

// このコード モジュールに含まれる関数の宣言を転送します:
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    hInst = hInstance;
    DialogBox(hInst, L"MyTestDlgBase_Main", NULL, (DLGPROC)MyDlgProc);
    return (int)0;
}

// あほに数える関数
void SanNoBaisuu(int sec)
{
    SendMessage(GetDlgItem(hDlgWnd, IDC_LIST1), LB_INSERTSTRING, 0, (LPARAM)(std::to_wstring(sec) + L"ぁ~~ん").c_str());
}

// 真面目に数える関数
void SanNoBaisuuDehaNai(int sec)
{
    SendMessage(GetDlgItem(hDlgWnd, IDC_LIST1), LB_INSERTSTRING, 0, (LPARAM)std::to_wstring(sec).c_str());
}

// ダイアログプロシージャ
BOOL CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg) {
    case WM_INITDIALOG:
        // 起動時一回通るところ
        hDlgWnd = hDlg;
        RegisterCountingMethods(SanNoBaisuu, SanNoBaisuuDehaNai); // 関数を登録
        break;

    case WM_COMMAND:
        switch (LOWORD(wp)) {
            case IDC_BUTTON1:
                // ボタンを押したときに
                StartCounting();    // 世界のナベアツ カウントスタート
                break;
        }
        return FALSE;
    case WM_CLOSE:
        StopCounting();             // ナベアツ終了
        EndDialog(hDlg, 0);
        return TRUE;
    }
    return FALSE;
}
DllTest.cpp
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <combaseapi.h>
#include <wchar.h>
#include <thread>
#include <string>

#define VC_DLL_EXPORTS
#include "DllTest.h"

std::thread th_b;
int threadStop = 0;

// 関数ポインタ
void (*pfunc1)(int) = nullptr;// あほ
void (*pfunc2)(int) = nullptr;// 普通

// 関数を登録するための関数
void __cdecl RegisterCountingMethods(void (*pf1)(int), void (*pf2)(int))
{
    pfunc1 = pf1;
    pfunc2 = pf2;
}

// 3の倍数と3がつく数字かどうかを判定する関数
BOOL JudgeSaaaan(int sec)
{
    auto secStr = std::to_string(sec);
    int pos = secStr.find("3");
    return pos != std::string::npos || sec % 3 == 0;
}

// 別スレッド起動し、カウント開始
// 現在の時計の秒数が3の倍数の時はexe側からもらったアホになる関数を呼び、
// そうでない場合はexe側からもらった普通に数える関数を呼ぶ
void __cdecl StartCounting()
{
	th_b = std::thread([]
        {
            while (!threadStop)
            {
                // 時刻を取得
                auto t = time(nullptr);
                auto tmv = tm();
                localtime_s(&tmv, &t);

                // 関数呼び分け
                if (JudgeSaaaan(tmv.tm_sec)) pfunc1(tmv.tm_sec);
                else                         pfunc2(tmv.tm_sec);

                Sleep(1000);
            }
        });
}

// カウント停止
void __cdecl StopCounting()
{
    threadStop = 1;
    th_b.join();
}

関数ポインタの例

関数ポインタは、**「別のヤツに自分の処理をやってもらう」もしくは「自分のタイミングで別のヤツの処理をやってやる」**ということをするために使えるのかな、ということで、それをイメージしたくて上記のような実験アプリを作ったが、基本の、関数ポインタ自体の書き方は下記のとおり。

関数ポインタの書き方

 (*関数ポインタの名前)(引数)
 *関数ポインタの名前(引数,,,,);←名前の前後のカッコがない。これはダメ!普通の関数定義になる

関数の名前だけを書くと、関数のアドレスになり、関数ポインタに代入することができる。

例(普通の書き方)

// 関数ポインタ
int (*pfp1)(int, int);
std::wstring(*pfp2)();

// 関数本体
int func1(int a, int b) { return 10; }
std::wstring func2() { return std::wstring(L"abc"); }

// 関数本体のアドレスを関数ポインタに登録
pfp1 = func1;
pfp2 = func2;

// 呼び出し
int ret1 = pfp1(1, 2);
std::wstring ret2 = pfp2();

例(ラムダ式の書き方1)

// 関数ポインタにラムダ式を入れる
int (*pfp1)(int, int) = [](int a, int b) -> int {return 20; };
std::wstring(*pfp2)() = []() ->std::wstring {return std::wstring(L"def"); };

// 呼び出し
int ret1 = pfp1(1, 2);
std::wstring ret2 = pfp2();

例(ラムダ式の書き方2)

// 関数ポインタにラムダ式を入れてautoで受ける
auto pfp1 = [](int a, int b) -> int {return 20; };
auto pfp2 = []() ->std::wstring {return std::wstring(L"def"); };

// 呼び出し
int ret1 = pfp1(1, 2);
std::wstring ret2 = pfp2();

コード置き場所

上のコードは主だったところのみ。全部は下記にある。
https://github.com/tera1707/Cpp/tree/master/CppDllTemplate

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?