ささみのメモ帳

ゲーム会社で働く、無能プログラマーが自分のためのメモ帳として利用しています。

C/C++で基本的な関数ポインタの使い方メモ

関数ポインタとは

関数を指すためのポインタ(メモリアドレス)のことで、 C、C++C#などでは一般的に使用されます。

以下は、プログラム初心者に向きの簡単なC++での関数ポインタの利用例です。

一般的なやつ

#include <stdio.h>

// 戻り値なし、引数 int の関数ポインタの定義
typedef void (*FuncPointer)(int);

void FuncA(int x) {
    std::cout << "Function A: " << x << std::endl;
}

void FuncB(int x) {
    std::cout << "Function B: " << x << std::endl;
}

int main() {
    // 関数ポインタの宣言
    FuncPointer funcPtr;

    funcPtr = FuncA;
    funcPtr (10); // Function A: 10

    funcPtr = FuncB;
    funcPtr (20);  // Function B: 20

    return 0;
}

クラス指定するやつ

#include <stdio.h>

class ClassA
{
public:
    void Func(int x) {
        std::cout << "Class A: " << x << std::endl;
    }
};

class ClassB
{
public:
    void Func(int x) {
        std::cout << "Class B: " << x << std::endl;
    }
};

// ClassAの戻り値なし、引数 int の関数ポインタの定義
typedef void (ClassA::* FuncPointer)(int);

int main() {
    // 関数ポインタの宣言
    FuncPointer funcPtr;
    ClassA classA;

    funcPtr = &ClassA::Func;
    (classA.*funcPtr)(10); // Class A: 10

    funcPtr = &ClassB::Func;// Classが一致しないのでエラー

    return 0;
}

Pairにして配列にしてつかうやつ

bool BoolFuncA() { return true; }
bool BoolFuncB() { return false; }
void FuncA(int x) { std::cout << "Function A: " << x << std::endl; }
void FuncB(int x) { std::cout << "Function B: " << x << std::endl; }

int main() {
    // 関数ポインタペアの配列定義
    std::pair< bool (*)(void), void (*)(int)> funcPairs[] = {
        { &BoolFuncA, &FuncA },
        { &BoolFuncB, &FuncB }
    };
    // 配列を回して中身を実行
    for (auto funcPair : funcPairs)
    {
        if (funcPair.first())
        {
            funcPair.second(10);
        }
    }

    return 0;
}

ラムダ式Pairにして配列にしてつかうやつ

#include <functional>

bool BoolFuncA() { return true; }
bool BoolFuncB() { return false; }
bool BoolFuncC() { return true; }

void FuncC(int x) { std::cout << "Function C: " << x << std::endl; }

int main() {
    // ラムダ式を含めた関数ポインタペアの配列定義
    std::pair< bool(*)(void), std::function<void(int)>> funcPairs[] = {
        { &BoolFuncA, [](int x) { std::cout << "Function A: " << x << std::endl; }},
        { &BoolFuncB, [](int x) { std::cout << "Function B: " << x << std::endl; }},
        { &BoolFuncC, &FuncC }
    };

    for (auto funcPair : funcPairs)
    {
        if (funcPair.first())
        {
            funcPair.second(10);
        }
    }

    return 0;
}

R3についてのメモ

R3とは

UniRxと同様に、Rx機能をより幅広いプラットフォームで利用できるようにし、 async/awatiとの連携強化、一部機能更新等が行われたライブラリ。

UniRxについては、こちらに雑にまとめてる。 shiromisasami.hatenablog.com

具体的な利用パターン

一定時間内に来る同様の処理では初回だけ処理して他は無視する
using R3;
using System;
using UnityEngine;

public class R3Test : MonoBehaviour
{
    private Subject<(int, float)> _subject = new Subject<(int, float)>();

    private int _index = 1;
    private float _value = 1.0f;

    void Start()
    {
        //Subjectの内容設定
        _subject
        // _subjectのNextで10秒間区切りの連続のうち最初だけを通過させる
        .ThrottleFirst(TimeSpan.FromSeconds(10))
        .Subscribe(tuple =>
        {
            var (index, Value) = tuple;
            Debug.Log("Index:" + index + " float:" + Value + " Time:" + Time.time);
        })
        .AddTo(this);
    }

    void Update()
    {
        _subject.OnNext((_index, _value));
        ++_index;
        _value += 10.0f;
    }
}

Observable内で一定時間内に来る同じ処理の初回だけ処理して他は無視する
using R3;
using Cysharp.Threading.Tasks;
using System;
using UnityEngine;

public class R3Test : MonoBehaviour
{
    private int _index = 1;
    private float _value = 1.0f;

    void Start()
    {
        Observable.Create<(int, float)>(async (observer, ct) =>
        {
            observer.OnNext((_index, _value));
            ++_index;
            _value += 10.0f;
            //5秒待機
            await UniTask.Delay(TimeSpan.FromSeconds(5), cancellationToken: ct);
            observer.OnNext((_index, _value));
            ++_index;
            _value += 10.0f;
            //5秒待機
            await UniTask.Delay(TimeSpan.FromSeconds(5), cancellationToken: ct);
            observer.OnNext((_index, _value));
            observer.OnCompleted();
        })
        // Observable内のOnNextを10秒間区切りの連続のうち最初だけを通過させる
        .ThrottleFirst(TimeSpan.FromSeconds(10))
        .Subscribe(tuple =>
        {
            var (index, Value) = tuple;
            Debug.Log("Index:" + index + " float:" + Value + " Time:" + Time.time);
        })
        .AddTo(this);
    }
}

入力処理とそれに伴う値変化の通知
using R3;
using R3.Triggers;
using UnityEngine;

public class R3Test : MonoBehaviour
{
    private R3.ReactiveProperty<int> _rpInt = new(0);

    void Start()
    {
        //入力で値加算/減算
        this.UpdateAsObservable()
        .Where(_ => Input.GetKeyDown(KeyCode.A) || Input.GetKeyDown(KeyCode.D))
        .Subscribe(_ =>
        {
            if (Input.GetKeyDown(KeyCode.A))
            {
                _rpInt.Value++;
            }
            else if (Input.GetKeyDown(KeyCode.D))
            {
                _rpInt.Value--;
            }
        })
        .AddTo(this);
        //値変化による通知取得
        _rpInt.Subscribe(value =>
        {
            Debug.Log("ChangeValue:" + value);
        })
        .AddTo(this);
    }
}

入力処理とそれに伴う配列値変化の通知
using R3;
using R3.Triggers;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;

public class R3Test : MonoBehaviour
{
    private R3.ReactiveProperty<List<int>> _rpIntList = new(new List<int>(Enumerable.Repeat(0, 10)));
    private int _index = 0;

    void Start()
    {
        this.UpdateAsObservable()
        .Where(_ => Input.GetKeyDown(KeyCode.A)|| 
                    Input.GetKeyDown(KeyCode.D)||
                    Input.GetKeyDown(KeyCode.W)||
                    Input.GetKeyDown(KeyCode.S))
        .Subscribe(_ =>
        {
            //入力によりIndexのシフトまたは値の変更
            if(Input.GetKeyDown(KeyCode.W))
            {
                _index = Mathf.Min(++_index, _rpIntList.Value.Count - 1);
            }
            if (Input.GetKeyDown(KeyCode.S))
            {
                _index = Mathf.Max(--_index, 0);
            }
            if (Input.GetKeyDown(KeyCode.A))
            {
                _rpIntList.Value[_index]++;
            }
            if (Input.GetKeyDown(KeyCode.D))
            {
                _rpIntList.Value[_index]--;
            }
            //Listの中の要素変更では通知されない ※List自体の操作は通知される
            //通知を強制発行
            _rpIntList.ForceNotify();
        })
        .AddTo(this);

        _rpIntList.Subscribe(value =>
        {
            Debug.Log("ChangeValue:" + value[_index] + " Index:" + _index);
        })
        .AddTo(this);
    }
}

UniRxについてのメモ

UniRxとは

UniRx(Reactive Extensions for Unity)は、マイクロソフトによって開発された.NetFrameworkのReactive Extensions(Rx)機能をCySharp社がUnity用にしたライブラリです。
※UnityはMono(.NetFrameworkとは違う)環境をベースに動作しているため、.NetFrameworkのRxの機能をすべてサポートためです。

Rxとは、Observerパターンを取り入れたライブラリで、非同期 / イベント / 時間に関する処理をLINQ的に記述でき、プログラムをより効果的に管理するための機能を提供します。複雑なイベント処理をシンプルにし、コードの可読性や保守性を向上させることができます。

基本概念/機能

オブザーバブル(Observables): データやイベントのストリームを表し、時間の経過と共に複数の値を非同期的に生成することができます。これにより、イベント駆動型のプログラミングが容易になります。

オブザーバー(Observers): オブザーバーはオブザーバブルから送出されるデータを購読し、データが発行されるたびに特定のアクションを実行します。オブザーバーはオブザーバブルと接続され、イベントやデータの変更を監視します。

サブスクリプション(Subscriptions): オブザーバーがオブザーバブルを購読するとき、サブスクリプションが生成されます。このサブスクリプションを通じて、イベントの購読を管理し、必要ない場合には購読を解除することができます。

オペレーター(Operators): UniRxには多くのオペレーターが用意されており、これを使ってデータストリームの変換、フィルタリング、結合などが行えます。これにより、複雑なデータ処理や条件分岐を簡単に記述することが可能です。

利点

コードの簡素化: 非同期処理や複数のイベントソースを扱うコードが簡潔に書けるようになります。 エラー処理: エラーハンドリングが一元化され、エラー処理をストリーム内で簡単に行うことができます。 メモリリークの防止: イベントの購読解除を自動的に行うことで、メモリリークのリスクを減少させます。 時間操作: デバウンス、スロットル、タイムアウトなどの時間に基づいた操作を簡単に実装できます。

導入方法

以下のドキュメントより導入を行ってください。
github.com

基本的な利用方法

Obserbableの作成/設定
Obserbableすなわち、Observer(購読者)が読むもの/対象の作成をする必要があります。 Observableクラスのメソッドを利用してIObservable(インターフェース)として作成/設定します。
時間とイベントによるベースの設定があります。

以下はその種類です。

名前/記述 ディレクティブ 内容
EveryUpdate(void) UniRx 毎フレームごと(Updateタイミング)に通知
※GameObjectの状態に依存しない全体サイクルをもとにします
EveryLateUpdate(void) UniRx 毎フレームごと(LateUpdateタイミング)に通知
※GameObjectの状態に依存しない全体サイクルをもとにします
EveryFixedUpdate(void) UniRx Unity設定で指定したフレームごと(FixedUpdateタイミング)に通知
※通常と同様に物理計算系の使用に推奨
※GameObjectの状態に依存しない全体サイクルをもとにします
EveryGameObjectUpdate(void) UniRx |毎フレームごと(Updateタイミング)に通知
MainThreadDispatcherのSubjectに積まれて、メインスレッド実行になる
EveryEndOfFrame(void) UniRx 毎フレームの最後に通知
EveryApplicationFocus(void) UniRx アプリのフォーカス状態が変更されるたびに通知
戻り値がboolでフォーカス状態が判る
true : フォーカス中 false : フォーカス外
EveryApplicationPause(void) UniRx アプリが一時停止時に通知
戻り値がboolで停止状態が判る
true : 停止 false : 再開
Range(int, int) UniRx 指定した範囲の整数で順に通知する
第一引数が開始値、第二引数が回数
例 (3,4) = {3, 4, 5, 6}
Start(Func <T> or Action) UniRx 指定した処理を非同期実行し、結果を通知
Interval(TimeSpan) UniRx, System 指定した時間間隔で通知
Timer(TimeSpan) UniRx, System 指定した時間経過後に通知
引数によって単発や一定間隔での通知にできる
Timer(TimeSpan) UniRx, System 指定したフレーム経過後に通知
引数によって単発や一定間隔での通知にできる
UpdateAsObservable() UniRx.Triggers MonoBehaviourのUpdate()の呼び出しをObservableストリームとして扱い、通知する
LateUpdateAsObservable() UniRx.Triggers MonoBehaviourのLateUpdate()の呼び出しをObservableストリームとして扱い、通知する
FixedUpdateAsObservable() UniRx.Triggers MonoBehaviourのFixedUpdate()の呼び出しをObservableストリームとして扱い、通知する
OnDestroyAsObservable() UniRx.Triggers MonoBehaviourのOnDestroy()の呼び出しをObservableストリームとして扱い、通知する
ObserveEveryValueChanged
(TSource, Func<TSource, TProperty>)
UniRx 指定したプロパティが変更時に通知
Empty<Unit>() UniRx 指定した型で即座に通知
Return(Unit.Default) UniRx 指定した値で即座に通知
Throw<Unit>(new Exception()) UniRx 指定した型でエラーハンドルを発行する
Repeat(Unit.Default) UniRx ループして任意の値で通知
※終わり次第即座に次の通知を行います
Create<Unit>(observer => ...) UniRx 独自の通知条件の作成

UnitはUniRxで通知の際の値や型がない場合に使用する構造体です。
Unitの部分は任意の型、Unit.Defaultは具体的な任意な型の値を設定できます。

using System;
using UniRx;
using UniRx.Triggers;
using UnityEngine;

public class UniRxTest : MonoBehaviour
{
    void Start()
    {
        Observable.Interval(TimeSpan.FromSeconds(1));
        Observable.Timer(TimeSpan.FromSeconds(1));
        Observable.TimerFrame(1);
        Observable.EveryUpdate();
        Observable.EveryLateUpdate();
        Observable.EveryFixedUpdate();
        Observable.EveryGameObjectUpdate();
        Observable.EveryEndOfFrame();
        Observable.EveryApplicationFocus();
        Observable.EveryApplicationPause();;
        Observable.Range(0, 10);
        Observable.Start(() => { });

        this.UpdateAsObservable();
        this.LateUpdateAsObservable();
        this.FixedUpdateAsObservable();
        this.OnDestroyAsObservable();
        this.ObserveEveryValueChanged(self => self.transform.position);

        Observable.Empty<Unit>();
        Observable.Return(Unit.Default);
        Observable.Throw<Unit>(new Exception());
        Observable.Repeat(Unit.Default);
        Observable.Create<Unit>(observer =>
        {
             return Disposable.Empty;
         });
    }
}

Obserbableの処理をフィルタリング設定のオペレーターがあります。
以下はその種類です。

名前/記述 内容
Where 条件式に一致したもののみ
Distinct 重複した二つ目以降は通さない
DistinctUntilChanged 最新のものが同じだった場合通さない
Throttle/ThrtottleFrame 指定した時間(ms)/フレーム内に来たOnNextの最後だけ通す
ThrottleFirst/ThrottleFirstFrame 指定した時間(ms)/フレーム内に来たOnNextの最初だけ通す
First/FirstOrDefault 一番最初のみを通してObservableを完了
※一つも発行されなかったとき通常はエラー、Defaultはデフォルト値で通知
Single/SingleOrDefault OnNextが2つ以上発行されたらエラー
※一つも発行されなかったとき通常はエラー、Defaultはデフォルト値で通知
Last/LastOrDefault 最後の値だけ通したい
※一つも発行されなかったとき通常はエラー、Defaultはデフォルト値で通知
Take 指定した個数だけ通す
TakeWhile 条件不一致になるまで通す
TakeUntil 指定したObservableにOnNextが来るまで通す
Skip 指定した個数無視
SkipWhile 条件一致する間は無視
SkipUntil 指定したObservableにOnNextが来るまで無視
OfType 型が一致するもののみ通す※型変換も行われます
IgnoreElements OnErrorまたはOnCompletedのみを通す
using System;
using UniRx;
using UniRx.Triggers;
using UnityEngine;

public class EventBase { }
public class Event : EventBase { }
public class UniRxTest : MonoBehaviour
{
    void Start()
    {
        this.UpdateAsObservable()
            .Where(_ => Input.GetKeyDown(KeyCode.Space))
            .Distinct()
            .DistinctUntilChanged()
            .Throttle(TimeSpan.FromSeconds(1))
            .ThrottleFrame(1)
            .ThrottleFirst(TimeSpan.FromSeconds(1))
            .ThrottleFirstFrame(1)
            .TimeInterval()
            .Timeout(TimeSpan.FromSeconds(1))
            .TimeoutFrame(1)
            .First()
            .FirstOrDefault()
            .Single()
            .SingleOrDefault()
            .Last()
            .LastOrDefault()
            .Take(1)
            .TakeWhile(_ => Input.GetKey(KeyCode.Space))
            .TakeUntil(this.LateUpdateAsObservable())
            .Skip(1)
            .SkipWhile(_ => Input.GetKey(KeyCode.Space))
            .SkipUntil(this.LateUpdateAsObservable())
            .IgnoreElements();
        //型を利用して発行する場合のみ使用可能
        Subject<EventBase> subject = new Subject<EventBase>();
        subject.OfType<EventBase, Event>();
    }
}

参考サイト

qiita.com

これ書きかけです。 とりまここまで自分用に公開

UEマクロのメモ

UEを利用する上で、よく使っているマクロのメモ書き

UCLASSマクロ

クラスの細かな設定マクロ

汎用タグ

タグ名 内容
ClassGroup=("Category") クラスカテゴリの指定
Blueprintable BPの基礎クラスとして利用可能
BlueprintType BPで利用可能な型
Abstract 抽象化宣言(派生必須アクター)

UFUNCTIONマクロ

関数の細かな設定マクロ

汎用タグ

タグ名 内容
BlueprintCallable BPに公開(実行ピン必須の関数として利用できる)
BlueprintImplementableEvent BPで実装を持つ
BlueprintNativeEvent BP、C++のどちらでも実装を持つ
仮想関数ではなく、virtualなしで宣言する
 
C++での実装関数には{宣言名前}Implementationが自動で生成され、そこに処理を記述する

C++でExecute
{宣言名前}という関数も自動で生成されて、これを利用することでC++、BPどちらの処理も呼び出される
※第一引数にそのイベントを関数の発生主のUObjectポインタが必要です

BPでC++の内容を利用する場合は、該当のEventノードを右クリックしてAdd Call to Parent Functionを選択して表示されるノード(C++の処理実装)をつなげる。
BlueprintPure オブジェクトに影響なくBPで利用可能(実行ピンなしの純粋関数として利用できる)
Category( = “String”) ディテール表示時のカテゴリ(項目)
NetMulticast サーバーでこのタグをつけた関数を実行した場合、サーバーにつながっている全てのクライアントの同一の関数も同時に実行する。
Reliable 通信を確実なものにする。途中で送信失敗をしても、再送をする。
Client サーバーのみでの実行関数とし、クライアントでの実行は行われない。
サーバーでの実行を受信することになる。
この関数の処理記述をC++でする場合は関数名_Implementationをつける必要がある。※定義時は不要
|

meta(メタデータ用タグ)

タグ名 内容
(HidePin = “[Pin名]”) BPでその関数の特定のピンを非表示にする。
(DefaultToSelf = “[引数名]”) BPでその関数指定引数ピンのデフォルトをSelf(this)にする。
(BlueprintInternalUseOnly= “bool”) BPの他の関数やノードの内部実装として使われる関数の検索時のみに表示するかどうか
true:関数、ノードの内部実装でのみ表示
false:どの場面でも表示
(DisplayName="String") Editorでの表示名

UFUNCTION参考サイト

www.unrealcommunity.wiki

UPROPERTYマクロ

メンバ変数の細かな設定マクロ

汎用タグ

タグ名 内容
EditAnywhere ディテールに常に表示、編集可能
EditInstanceOnly インスタンス(生成)後ディテールに表示、編集可能
EditDefaultsOnly デフォルト(生成前)のみディテールに表示、編集可能
VisibleAnywhere ディテールに常に表示、編集不可
VisibleInstanceOnly インスタンス(生成)後ディテールに表示、編集不可
VisibleDefaultsOnly デフォルト(生成前)のみディテールに表示、編集不可
BlueprintReadWrite BPで読み書き可能
BlueprintReadOnly BPで読み込みのみ可能
BlueprintAssignable BPでイベントノードとバインド可能にする※デリゲート
Const 定数とする
Transient 一時的な格納で読み込み時は0に初期化
Category( = “String”) ディテール表示時のカテゴリ(項目)
ReplicatedUsing(= [関数名]) ネットワークを介して更新している値の更新された際実行される、任意の関数を紐づける。引数で更新前の古い値を取得することもできる。

meta(メタデータ用タグ)

タグ名 内容
(ToolTip = "String") パラメータの説明文
(AllowPrivateAccess = “bool”) プライベート変数のイベントグラフに公開可能の有無
(BindWidgetOptional) UMGUIデザイナーで作成されたWidgetと紐づけ
(BindWidgetAnimOptional) UMGUIデザイナーで作成されたアニメーションと紐づけ
(EditCondition ="[メンバbool変数 or 固定bool値]") 指定したbool型のメンバ変数がtrueの場合にアクティブ表示になる
(DisplayName="String") Editorでの表示名 ※UMETAでEnum定義での利用可
(ExposeOnSpawn=”bool”) スポーン時に公開するプロパティかの設定

UPROPERTY参考サイト

unrealcommunity.wiki

agrawalsuneet.github.io

その他のマクロ

タグ名 内容
UPARAM 関数の引数にメタデータを付与するためのマクロ
BPでの表示状態を設定することができる
UMETA Enum等のメタデータを付与するためのマクロ
BPでの表示状態を設定することができる