unsafeでアンマネージドな構造体でポインタアクセスとポリモーフィズムのテスト
環境
Unity2022.3.1f1
概要
構造体は値渡しとなりアクセスしづらいです。なのでunsafeでポインタを使って直接アクセスしたい。
そしてポリモーフィズム的な事をしたい。NativeArrayで一緒くたにアクセスしたい。
ポリモーフィズムの部分は具体的には以下のような事をしたいです。(Classでの例)
public abstract class Animal { public abstract void Sound(); } public class Dog : Animal { public override void Sound() { Debug.Log("ワンワン"); } } public class Cat : Animal { public override void Sound() { Debug.Log("ナーゴ"); } } var animals = new Animal[]{ new Dog(), new Cat() }; for(int i = 0; i < animals.Length; i++) animals[i].Sound();
Marshal.Alloc版(失敗)
for内のインターフェースを取得する部分でエラーになります。
IntPtrからインターフェスの参照を得る方法が見つかりませんでした。
そもそも構造体として生成されていないです。
public interface ITestAnimal { void Sound(); } public struct TestDog : ITestAnimal { public int value; public void Sound() { Debug.Log($"ワンワン:{value}"); } } public struct TestCat : ITestAnimal { public int value; public void Sound() { Debug.Log($"ナーゴ:{value}"); } } var test3Animals = new NativeArray<IntPtr>(2, Allocator.Persistent); test3Animals[0] = Marshal.AllocCoTaskMem(sizeof(TestDog)); ((TestDog*)test3Animals[0])->value = 321; test3Animals[1] = Marshal.AllocCoTaskMem(sizeof(TestCat)); ((TestCat*)test3Animals[1])->value = 654; for(int i = 0; i < test3Animals.Length; i++) { ITestAnimal animal = *((ITestAnimal*)test3Animals[i]); animal.Sound(); } for(int i = 0; i < test3Animals.Length; i++) Marshal.FreeCoTaskMem(test3Animals[i]); test3Animals.Dispose();
GCHandle.Alloc版
直接アクセスもポリモーフィズムもできるようになりました。
public interface ITestAnimal { void Sound(); } public struct TestDog : ITestAnimal { public int value; public void Sound() { Debug.Log($"ワンワン:{value}"); } } public struct TestCat : ITestAnimal { public int value; public void Sound() { Debug.Log($"ナーゴ:{value}"); } } var testAnimals = new NativeArray<GCHandle>(2, Allocator.Persistent); testAnimals[0] = GCHandle.Alloc(new TestDog(), GCHandleType.Pinned); testAnimals[1] = GCHandle.Alloc(new TestCat(), GCHandleType.Pinned); var t0 = (TestDog*)testAnimals[0].AddrOfPinnedObject(); t0->value = 123; var t1 = (TestCat*)testAnimals[1].AddrOfPinnedObject(); t1->value = 456; for(int i = 0; i < testAnimals.Length; i++) ((ITestAnimal)testAnimals[i].Target).Sound(); for(int i = 0; i < testAnimals.Length; i++) testAnimals[i].Free(); testAnimals.Dispose();
内包
内包を継承元と見立てて行ってみました。
直接アクセスもポリモーフィズムもできるようになりました。
インターフェースではないので共通の変数も持てます。
[StructLayout(LayoutKind.Sequential)] public unsafe struct Test2Animal { public enum AnimalType { Dog, Cat, } public AnimalType type; public delegate void CallBackSound(Test2Animal* pAnimal); private IntPtr sound; public void SetDelegateSound(CallBackSound func) { sound = Marshal.GetFunctionPointerForDelegate(func); } public void Sound() { var func = Marshal.GetDelegateForFunctionPointer<CallBackSound>(sound); fixed(Test2Animal* pAnimal = &this) { func.Invoke(pAnimal); } } } [StructLayout(LayoutKind.Sequential)] public unsafe struct Test2Dog { public Test2Animal animal; public int iv; public void Setup() { animal.type = Test2Animal.AnimalType.Dog; animal.SetDelegateSound(static (pAnimal) => { ((Test2Dog*)pAnimal)->Sound(); }); iv = 0; } public void Sound() { Debug.Log($"ワンワン:{iv}"); } } [StructLayout(LayoutKind.Sequential)] public unsafe struct Test2Cat { public Test2Animal animal; public float fv; public void Setup() { animal.type = Test2Animal.AnimalType.Cat; animal.SetDelegateSound(static (pAnimal) => { ((Test2Cat*)pAnimal)->Sound(); }); fv = 0.0f; } public void Sound() { Debug.Log($"ナーゴ:{fv}"); } } var test2Animals = new NativeArray<IntPtr>(2, Allocator.Persistent); test2Animals[0] = Marshal.AllocCoTaskMem(sizeof(Test2Dog)); ((Test2Dog*)test2Animals[0])->Setup(); test2Animals[1] = Marshal.AllocCoTaskMem(sizeof(Test2Cat)); ((Test2Cat*)test2Animals[1])->Setup(); ((Test2Dog*)test2Animals[0])->iv = 123; ((Test2Cat*)test2Animals[1])->fv = 4.56f; for(int i = 0; i < test2Animals.Length; i++) ((Test2Animal*)test2Animals[i])->Sound(); for(int i = 0; i < test2Animals.Length; i++) Marshal.FreeCoTaskMem(test2Animals[i]); test2Animals.Dispose();
関連
【C#】メモリレイアウトを制御できるStructLayout &LayoutKind.Explicitを用いて別の型として解釈する(あとUnsafe.Asを利用した例も) - はなちるのマイノート