none
C++CLIのクラス配列ラッピングについて RRS feed

  • 質問

  • https://qiita.com/Convert314/items/0171811eabd69042e540

    マネージ配列をアンマネージ配列へ変換するのは、上記のサイトを参考に理解しているのですが、クラス or 構造体の配列を変換する場合はどのようにするのでしょうか。

    #pragma once
    
    #include <deque>
    
    using namespace System;
    
    // manage
    ref class Foo
    {
    internal:
    
    	bool isResponsible;
    	foo* ptr;
    
    public:
    
    	Foo() :
    		isResponsible{ true },
    		ptr{ new foo() }
    	{}
    
    	Foo(Foo^ obj) :
    		isResponsible{ true },
    		ptr{ new foo(obj->ptr) }
    	{}
    
    	Foo(foo* ptr) :
    		isResponsible{ false },
    		ptr{ ptr }
    	{}
    
    	~Foo()
    	{
    		this->!Foo();
    	}
    
    	!Foo()
    	{
    		if (isResponsible && ptr != nullptr)
    		{
    			delete ptr;
    			ptr = nullptr;
    		}
    	}
    
    	property Byte X
    	{
    		Byte get() { return ptr->x; }
    		void set(Byte value) { ptr->x = value; }
    	}
    
    	property Byte Y
    	{
    		Byte get() { return ptr->y; }
    		void set(Byte value) { ptr->y = value; }
    	}
    };
    
    // unmanage
    class foo
    {
    public:
    
    	foo()
    	{
    
    	}
    
    	foo(const foo* obj)
    	{
    
    	}
    
    	~foo()
    	{
    
    	}
    
    	unsigned char x;
    	unsigned char y;
    };
    
    // manage
    ref class Wrapper
    {
    internal:
    	UnmanageExecutor* executor;
    
    public:
    	Wrapper() :executor{ new UnmanageExecutor() } {}
    
    	~Wrapper() { this->!Wrapper(); }
    
    	!Wrapper()
    	{
    		if (executor != nullptr)
    		{
    			delete executor;
    			executor = nullptr;
    		}
    	}
    
    	void Execute(array<Foo^>^ arr)
    	{
    		std::deque<foo> queue;
    
    		for each (Foo^ a in arr)
    		{
    			queue.push_back(*a->ptr);
    		}
    
    		executor->Execute(queue);
    	}
    };
    
    class UnmanageExecutor
    {
    public:
    
    	void Execute(const std::deque<foo>& arr)
    	{
    		for (auto itr = arr.begin(); itr != arr.end(); itr++)
    		{
    		}
    	}
    };

    サンプルに作成したコードでは、stlのdequeとしておりますが、vectorでもポインタでも問題ありません。

    アンマネージ側へクラス配列のデータをより高速に渡す方法が無いかを探しております。

    2019年8月1日 2:30

回答

  • Blittable 型と非 Blittable 型という概念があります。Blittable型は簡単に言うとマネージとアンマネージで同じメモリレイアウトをする型のことです。レイアウトが同じであれば、std::memcpyでコピーしても問題ありません。またP/Invoke(≠C++ Interop)はBlittable型配列をアンマネージ型C配列に変換可能ですので、ネイティブ関数に直接渡すこともできます。更にはコピーと固定の概念を理解していれば、Blittable型配列を固定し先頭アドレスを取得すれば、コピーせずアンマネージ型C配列としてアクセスできます。

    # 具体的なコードを挙げれずすみません。


    2019年8月1日 3:29
  • 佐祐理さんの話を今回のfoo型に当てはめると、fooはBittableなので、マネージド側のFooをfooとbit互換になるように、
    public value struct Foo { Byte x; Byte y; };などと定義すれば、
    Manage側の引数Array<Foo> ^arrはアンマネージド側の「fooの配列」とも互換になるのでキャストで片が付き、コピーが不要になる、ということです。

    多分コードはこんな感じ。

    Managed::Execute( Array<Foo> ^arr ) {
     pin_ptr<Foo> pin = &arr[0]; Foo *ptr = pin;
     unmanaged->Execute( reinterpret_cast<foo*>(ptr), arr->Length );
    }
    
    UnManaged::Execute( foo *arr, int size ){
     for( int i=0; i<size; i++) do_with_foo( arr[i]);
    }

    なお、もちろん、fooが別クラスのポインタを含むとかで非bittableであれば、話は色々変わってきて、マーシャリングがどうこうという話になります。


    jzkey

    2019年8月1日 12:27
  • 質問文で挙げられたコードはByteを含む構造体でしたのでBlittable型の紹介をしました。System::String^の場合、非Blittable型ですが、P/InvokeやMarshal::StructureToPtr()なんかも使えます。ただし、std::stringへの変換は提供されていないので、この場合は質問文のようなコード変換が必要になります。
    2019年8月2日 6:46

すべての返信

  • Blittable 型と非 Blittable 型という概念があります。Blittable型は簡単に言うとマネージとアンマネージで同じメモリレイアウトをする型のことです。レイアウトが同じであれば、std::memcpyでコピーしても問題ありません。またP/Invoke(≠C++ Interop)はBlittable型配列をアンマネージ型C配列に変換可能ですので、ネイティブ関数に直接渡すこともできます。更にはコピーと固定の概念を理解していれば、Blittable型配列を固定し先頭アドレスを取得すれば、コピーせずアンマネージ型C配列としてアクセスできます。

    # 具体的なコードを挙げれずすみません。


    2019年8月1日 3:29
  • 佐祐理さんの話を今回のfoo型に当てはめると、fooはBittableなので、マネージド側のFooをfooとbit互換になるように、
    public value struct Foo { Byte x; Byte y; };などと定義すれば、
    Manage側の引数Array<Foo> ^arrはアンマネージド側の「fooの配列」とも互換になるのでキャストで片が付き、コピーが不要になる、ということです。

    多分コードはこんな感じ。

    Managed::Execute( Array<Foo> ^arr ) {
     pin_ptr<Foo> pin = &arr[0]; Foo *ptr = pin;
     unmanaged->Execute( reinterpret_cast<foo*>(ptr), arr->Length );
    }
    
    UnManaged::Execute( foo *arr, int size ){
     for( int i=0; i<size; i++) do_with_foo( arr[i]);
    }

    なお、もちろん、fooが別クラスのポインタを含むとかで非bittableであれば、話は色々変わってきて、マーシャリングがどうこうという話になります。


    jzkey

    2019年8月1日 12:27
  • ご回答ありがとうございます。

    逆にBlittable型でないものが含まれる場合(System::String^↔std::stringなど)は、上記のような手法しか無いということでしょうか?

    2019年8月2日 0:20
  • 質問文で挙げられたコードはByteを含む構造体でしたのでBlittable型の紹介をしました。System::String^の場合、非Blittable型ですが、P/InvokeやMarshal::StructureToPtr()なんかも使えます。ただし、std::stringへの変換は提供されていないので、この場合は質問文のようなコード変換が必要になります。
    2019年8月2日 6:46