はじめに
前回C++プラグインの各種プラットフォームのテンプレートを作成した記事を書きました。
配列と構造体の連携方法を備忘録として残します。
ソースは前回作成したテンプレートを元にビルドしていきます。
C++コード
ヘッダー
#ifndef SOURCE_LIBRARY_HPP
#define SOURCE_LIBRARY_HPP
#ifdef _WINDOWS
#define _CRT_SECURE_NO_WARNINGS
#define DLL_EXPORT __declspec(dllexport)
#endif
#ifndef _WINDOWS
#define DLL_EXPORT
#endif
struct SampleStruct {
int a;
float b;
char c;
};
extern "C" {
DLL_EXPORT void intArray(int* ptr, int length);
DLL_EXPORT void sampleStruct(SampleStruct* ret);
};
#endif //SOURCE_LIBRARY_HPP
ソース
#include "library.hpp"
void intArray(int* ptr, int length) {
for (int i = 0; i < length; i++) {
ptr[i] = i + 1;
}
}
void sampleStruct(SampleStruct* ret) {
ret->a = 5;
ret->b = 5.5f;
ret->c = 'c';
}
C#とC++のブリッジ
using System;
using System.Runtime.InteropServices;
public class PluginTest
{
public struct SampleStruct
{
public int a;
public float b;
public char c;
}
#if !UNITY_EDITOR && UNITY_IOS
private const string PLUGIN_NAME = "__Internal";
#else
private const string PLUGIN_NAME = "sampleplugin";
#endif
[DllImport(PLUGIN_NAME)]
public static extern void intArray(IntPtr ptr, int length);
[DllImport(PLUGIN_NAME)]
public static extern void sampleStruct(IntPtr intPtr);
}
int配列の連携
C#側
int arraySize = Marshal.SizeOf(typeof(int)) * 5;
IntPtr intPtr = Marshal.AllocCoTaskMem(arraySize);
PluginTest.intArray(intPtr, 5);
int[] intArray = new int[5];
Marshal.Copy(intPtr, intArray, 0, 5);
for (int i = 0; i < intArray.Length; i++)
{
Debug.Log($"index:{i} value:{intArray[i]}");
}
Marshal.FreeCoTaskMem(intPtr);
結果
構造体の連携
C#側
PluginTest.SampleStruct sampleStruct = new PluginTest.SampleStruct();
IntPtr pStruct = Marshal.AllocCoTaskMem(Marshal.SizeOf(sampleStruct));
Marshal.StructureToPtr(sampleStruct, pStruct, false);
PluginTest.sampleStruct(pStruct);
sampleStruct = (PluginTest.SampleStruct)Marshal.PtrToStructure(pStruct, typeof(PluginTest.SampleStruct));
Debug.Log($"a:{sampleStruct.a}");
Debug.Log($"b:{sampleStruct.b}");
Debug.Log($"c:{sampleStruct.c}");
Marshal.FreeCoTaskMem(pStruct);
結果
補足
Marshal.StructureToPtr(sampleStruct, pStruct, false);
第3引数は第2引数のポインタが示す構造体を破棄する場合はtrue。
今回は破棄する必要が無いのでfalse。
注意点
C++側でnewしたものをreturnすると、メモリリークの原因になるので、C#側でメモリの確保と解放をしたほうが安全。
処理の都合上C++側でnewが必要なものに関しては、処理が終わるころにはすべて解放するようにするか、解放用の関数を別途作成してC#側から呼び出すという方法が必要。