(04)C#から、C++の関数の実行(クラス)

 2nd January 2023 at 11:16am

C++のクラスを、C#で利用します

「num個のint配列srcから最大値のインデックスと最大値を探すメソッド」という機能のメソッドを含む、「FuncClass」というC++のクラスを、C#から利用します。

「マネージドコードのクラスが、そのメンバにアンマネージドコードのクラスを持つことは許されていない」というルールがあるので、それをどう解決するかがポイントとなります。

1. 準備

C#、C++/CLR(CLI)、C++の各プロジェクトは、(03)C#から、C++の関数の実行(関数)と同様に作ります。

C#のクラスを「test03」などとした場合は、「test02」の部分を「test03」としてください。

2. C++のコード

  • NativeFunc.hの内容を、以下に置き換えます。
#ifdef NATIVEFUNC_EXPORTS
#define NATIVEFUNC_API __declspec(dllexport)
#else
#define NATIVEFUNC_API __declspec(dllimport)
#endif

namespace Native
{
	class NATIVEFUNC_API FuncClass
	{
	public:
		// num個のint配列srcから、最大値とそのインデックスを探す関数です
		void Max(int* src, int num, int* mx, int* mxIndex);
	};
}
  • NativeFunc.cppの内容を、以下に置き換えます。
#include "stdafx.h"
#include "NativeFunc.h"

void Native::FuncClass::Max(int* src, int num, int* mx, int* mxIndex)
{
	*mx = src[0];
	*mxIndex = 0;

	for (int i = 0; i < num; i++) {
		if (src[i] > *mx) {
			*mxIndex = i;
			*mx = src[i];
		}
	}
}
  • ポイントは特にありません。

3. C++/CLR(CLI)ラッパークラスのコード

  • WrapperClass.hの内容を、以下に置き換えます。
#pragma once
#include "..\NativeFunc\NativeFunc.h"
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Collections::Generic;

namespace Wrapper
{
	public ref class WrapperClass
	{
	private:
		Native::FuncClass* _na0; //★ここがポイント
	public:
		WrapperClass();
		~WrapperClass();
		!WrapperClass();
		void Max(array<int>^ src, int num, int% mx, int% mxIndex);
	}; //end class
} //end namespace
  • WrapperClass.cppの内容を、以下に置き換えます。
#include "stdafx.h"
#include "WrapperClass.h"

//★ここがポイント
Wrapper::WrapperClass::WrapperClass()
{
	_na0 = new Native::FuncClass();
}

//★ここがポイント
Wrapper::WrapperClass::~WrapperClass()
{
	delete _na0; _na0 = nullptr;
}

//★ここがポイント
Wrapper::WrapperClass::!WrapperClass()
{
	this->~WrapperClass();
}

void Wrapper::WrapperClass::Max(array<int>^ src, int num, int% mx, int% mxIndex)
{
	// 実行中、ガベージコレクションされないように、pin_ptrを使って固定する
	pin_ptr<int> p_src = &src[0];
	pin_ptr<int> p_mx = &mx;
	pin_ptr<int> p_mxIndex = &mxIndex;

	// 自作関数実行
	_na0->Max(p_src, num, p_mx, p_mxIndex);

	// 固定解除
	p_src = nullptr;
	p_mx = nullptr;
	p_mxIndex = nullptr;
}
  • ポイント
    • Native::FuncClass* _na0で、FuncClassクラスを呼び出します
      • 「マネージドコードのクラスが、そのメンバにアンマネージドコードのクラスを持つことは許されていない」というルールは、ポインタを利用して回避します
    • コンストラクタ、デストラクタ、ファイナライザを定義します
      • コンストラクタで、FuncClassクラスのインスタンスを作成します
      • デストラクタやファイナライザで、インスタンスを破棄します
      • ちなみに、.NETではデストラクタは必ず呼び出されるとは限りません

4. C#のコード

  • Form1.csの内容を、以下に置き換えます。
using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Wrapper;

namespace test03
{
    public partial class Form1 : Form
    {
        WrapperClass wr = new WrapperClass(); //WrapperClassのインスタンスを作成

        public Form1()
        {
            InitializeComponent();

            int[] src = { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2 };
            int mx = 0;
            int mxIndex = 0;
            int num = 10;

            MessageBox.Show("int[] a = { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2 }");

            wr.Max(src, num, ref mx, ref mxIndex); //処理実行

            MessageBox.Show("Max = " + mx.ToString() + ", MaxIndex=" + mxIndex.ToString());

            Environment.Exit(0);
        }
    }
}

結果

結果は、(03)C#から、C++の関数の実行(関数)と同じですので省略します。

上の手順で作成されたソリューションは、ここににあります(サンプルプログラムにおける警告について)。


Homeへプログラミングの記事Topへ