【メモ】Native Interops in TinyCLR その2
これの続きです。
Native stubを生成
前回最後にやった「Native stubを生成」で、
class MyNativeClass { [MethodImpl(MethodImplOptions.InternalCall)] public extern int MyNativeFunc(int param1, int param2); }
から生成されたファイルは3つでした。
- TinyCLRApplication1.h
- TinyCLRApplication1.cpp
- TinyCLRApplication1_TinyCLRApplication1_MyNativeClass.cpp
そのうち、実コードを記入するのはTinyCLRApplication1_TinyCLRApplication1_MyNativeClass.cppにある、
TinyCLR_Result Interop_TinyCLRApplication1_TinyCLRApplication1_MyNativeClass::MyNativeFunc___I4__I4__I4(const TinyCLR_Interop_MethodData md)
です。
TinyCLR_ResultはTinyCLR.hに定義されていて、enum型。まぁ、これは分かりやすい。
TinyCLR_Interop_MethodDataはApiProviderとStackが含まれた構造体で、ApiProviderはAcquire,Release,Add,Removeなどのvtableっぽい。StackはApiProviderにあるAPIに渡す引数で使うのかな。
これらメンバーの使い方はココにありますが、ほとんど書かれておらず参考になりませんねw
ApiProviderのメンバーをじっくり見ると、下記のとおり分類できるようだ。
ApiProvider 引数と戻り値
GetArgument(const TinyCLR_Interop_Provider* self, const TinyCLR_Interop_StackFrame& stack, size_t index, TinyCLR_Interop_ManagedValue& value) GetReturn(const TinyCLR_Interop_Provider* self, TinyCLR_Interop_StackFrame& stack, TinyCLR_Interop_ManagedValue& value)
ApiProvider 参照カウンタ操作?
Acquire(const TinyCLR_Interop_Provider* self) Release(const TinyCLR_Interop_Provider* self)
ApiProvider Interop追加/削除
Add(const TinyCLR_Interop_Provider* self, const TinyCLR_Interop_Assembly* interop) Remove(const TinyCLR_Interop_Provider* self, const TinyCLR_Interop_Assembly* interop)
ApiProvider 型情報の検索
FindType(const TinyCLR_Interop_Provider* self, const char* assemblyName, const char* namespaceName, const char* typeName, TinyCLR_Interop_ManagedObjectType& type)
ApiProvider マネージオブジェクトの生成/入れ替え?/自オブジェクトの取得
CreateObject(const TinyCLR_Interop_Provider* self, TinyCLR_Interop_StackFrame& stack, TinyCLR_Interop_ManagedValue& value) ReplaceObject(const TinyCLR_Interop_Provider* self, TinyCLR_Interop_ManagedValue& source, TinyCLR_Interop_ManagedValue& destination) GetThisObject(const TinyCLR_Interop_Provider* self, TinyCLR_Interop_StackFrame& stack, TinyCLR_Interop_ManagedValue& value)
ApiProvider フィールドとイベント
GetField(const TinyCLR_Interop_Provider* self, TinyCLR_Interop_ManagedValue managedObject, size_t index, TinyCLR_Interop_ManagedValue& value) GetStaticField(const TinyCLR_Interop_Provider* self, const TinyCLR_Interop_Assembly& interop, size_t index, TinyCLR_Interop_ManagedValue& value) RaiseEvent(const TinyCLR_Interop_Provider* self, const char* eventDispatcherName, const char* apiName, size_t implementationIndex, uint64_t data0, uint64_t data1, intptr_t data2)
Interopコードを実装
細かいところは置いておいて、サンプルコードを参考に、呼び出された回数を返すようTinyCLRApplication1_TinyCLRApplication1_MyNativeClass.cppを実装してみました。
#include "TinyCLRApplication1.h" TinyCLR_Result Interop_TinyCLRApplication1_TinyCLRApplication1_MyNativeClass::MyNativeFunc___I4__I4__I4(const TinyCLR_Interop_MethodData md) { static int counter = 0; TinyCLR_Interop_ManagedValue ret; ret.Type = TinyCLR_Interop_ManagedValueType::I4; ret.Data.Numeric->I4 = counter++; return TinyCLR_Result::Success; }
Interopのビルド
makefileの作成
サンプルのmakefileをコピペして、OUTPUT_NAMEをMyInterop、INC_DIRSにTinyCLR.hがあるフォルダを追加しました。
OUTPUT_NAME = MyInterop LINKERSCRIPT_NAME = scatterfile MCU_FLAGS = -mcpu=cortex-m4 -mthumb INC_DIRS = -I. -IC:\TinyCLR\TinyCLR-Ports\Core CC = arm-none-eabi-g++.exe LD = arm-none-eabi-g++.exe OC = arm-none-eabi-objcopy.exe CC_FLAGS = $(INC_DIRS) $(MCU_FLAGS) -Os -std=c++11 -xc++ -Wall -Wabi -w -mabi=aapcs -fPIC -fno-exceptions -fno-rtti -fno-use-cxa-atexit -fno-threadsafe-statics LD_FLAGS = $(MCU_FLAGS) -nostartfiles -lc -lgcc -T $(LINKERSCRIPT_NAME) -Wl,-Map,$(OUTPUT_NAME).map -Wl,--oformat -Wl,elf32-littlearm OC_FLAGS = -S -O binary SRC_FILES = $(wildcard *.cpp) OBJ_FILES = $(patsubst %.cpp, %.obj, $(SRC_FILES)) rebuild: clean build clean: del $(OBJ_FILES) $(OUTPUT_NAME).bin $(OUTPUT_NAME).elf $(OUTPUT_NAME).map build: $(OBJ_FILES) $(LD) $(LD_FLAGS) -o $(OUTPUT_NAME).elf $^ $(OC) $(OC_FLAGS) $(OUTPUT_NAME).elf $(OUTPUT_NAME).bin %.obj: %.cpp $(CC) -c $(CC_FLAGS) -o $@ $^
scatterfileの作成
サンプルのscatterfileをコピペして、RLI_BASEとRLI_LENGTHを、デバイスのScatterfile.gcc.ldfに書かれている(ER_RLP_BEGIN値)と(ER_RLP_END値-ER_RLP_BEGIN値)に書き換えます。
MEMORY { SDRAM (wx) : ORIGIN = 0x2001BC00, LENGTH = (0x2001C000 - 0x08) - 0x2001BC00 } SECTIONS { . = ALIGN(4); .text : { *(.text) } .rodata ALIGN(4): { *(.rodata ) } .data ALIGN(4): { *(.data) } .bss ALIGN(4): { *(.bss) } }
make build
準備できた状態がこちら。
make buildコマンドを実行して、ビルド(コンパイル&リンク)してみます。
おおお!なんかできてるっぽい。
(つづく)