L'Isola di Niente
L'Isola di Niente » .NET Tips » STL/CLR を使ってみる

STL/CLR を使ってみる

C++/CLI で STL が使えるようなので試してみる。

2011-05-09 関数オブジェクト等を追記

準備

とりあえず新規で「 CLR コンソールアプリケーション 」。
stdafx.h には何も定義されていない、無視すればいい。

その前に DLL パスが私の環境 (Visual Studio 2008 professional) で定義されていなかった。
オプションを開いて

img/stlclr.png

$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.5

を「参照ファイル」に追加。
こうしないと Microsoft.VisualC.STLCLR.dll が参照できません。

とりあえず vector

MSDN を探しても何故かコンテナ関係しか見つからない。
ということで vector で実験してみる。
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <cliext/vector>

#using <mscorlib.dll> //コレはネイティブコードを使う時に必要

using namespace System;

#pragma managed //以下はマネージド
void ManagedFunc()
{
	cliext::vector<String ^> vec;

	vec.push_back("あいうえお");
	vec.push_back("かきくけこ");

	//iterator は IRandomAccessIterator になる
	cliext::vector<String ^>::iterator ^ it = vec.begin();
	cliext::vector<String ^>::iterator ^ itend = vec.end();
	int i = 0;

	for (; it != itend; it->next())
	{
		Console::WriteLine(vec[i++]);
		//Console::WriteLine(it->get_cref()); //こっちのほうが簡単
	}
}

#pragma unmanaged //以下ネイティブ
void UnManagedFunc()
{
	std::vector<std::string> vec;

	vec.push_back("さしすせそ");
	vec.push_back("たちつてと");

	std::vector<std::string>::iterator it = vec.begin();
	std::vector<std::string>::iterator itend = vec.end();
	int i = 0;

	for (; it != itend; it++)
	{
		std::cout << vec[i++].c_str() << std::endl;
		//std::cout << it->c_str() < std::endl; //解っているならコレで
	}
}

#pragma managed //以下はマネージド
int main(array<System::String ^> ^args)
{
	ManagedFunc();
	UnManagedFunc();

	Console::WriteLine("終了するには何かキーを押してください");
	Console::ReadKey();
	return 0;
}


ハンドル型( ^ )がちょっとうっとうしいけどほとんど同じに書ける、これはなかなか。
しかしイテレータは ++ 演算子が定義されているはずなんだがエラーになるので next メソッドで。
とにかくマネージド環境でも同じように使えるというのは嬉しいですね。

普通なテンプレート

とりあえず通常のテンプレートとしては使えるのか?
#include "stdafx.h"

using namespace System;

namespace me
{
	// typename が正しいけど class と書いても通る
	template <class T, class T2>
	T2 calc(T a, T2 b)
	{
		return a + b;
	}
}

int wmain(int argc, wchar_t* argv[])
{
	Console::WriteLine("20 + 1.5 = {0}", me::calc(20, 1.5));
	//=> 20 + 1.5 = 21.5
	return 0;
}

なるほど問題無いみたい。
template キーワードを generic キーワードに変更するとエラーになる。
generic とは少し違うようだ。
// error
generic <typename T, typename T2>
T2 calc(T a, T2 b)
{
	return a + b;
}

VC++ で T を typename 使うと TCHAR 関連かと少し戸惑ったり...

関数オブジェクト

関数オブジェクトは使えるのだろうか?
#include "stdafx.h"
#include <cliext/vector>
#include <cliext/algorithm>
#include <cliext/functional>

using namespace System;
using namespace cliext;

// 関数オブジェクトテスト
// #include <functional>
template <class T>
ref struct print : public unary_function<T, void>
{
	void operator()(T n)
	{
		Console::Write("{0} ", n * 10);
	}
};

ref class Test
{
public:
	Test()
	{
		vector<int> vec;
		for (int i=0; i < 10; i++)
		{
			vec.push_back(i);
		}
		// for_each Test
		// #include <algorithm>
		for_each(vec.begin(), vec.end(), gcnew print<int>());
	}
};

int wmain(int argc, wchar_t* argv[])
{
	Test^ t = gcnew Test();
	//=> 0 10 20 30 40 50 60 70 80 90
	return 0;
}
何も問題無い、ref と gcnew キーワードを使う必要があるだけだ。
昔作った STL のコードを流用したい場合にはマジで使えそう。

とも感じるけど Generic の List で同じことをやってみる。
関数オブジェクトを用意する必要が無い。
#include "stdafx.h"

using namespace System;
using namespace System::Collections::Generic;

ref class Test
{
public:
	Test()
	{
		List list ;
		for (int i=0; i < 10; i++)
		{
			list.Add(i);
		}
		for each (int n in list)
		{
			Console::Write("{0} ", n * 10);
		}
	}
};

int wmain(int argc, wchar_t* argv[])
{
	Test^ t = gcnew Test();
	//=> 0 10 20 30 40 50 60 70 80 90
	return 0;
}
新たに作るならどう考えたって Generic のほうが簡単だよなぁ...
ついでに Python でもやってみる。
#!/usr/bin/env python3

import sys

vector = []
for i in range(10):
    vector.append(i)
for n in vector:
    sys.stdout.write("{0} ".format(n * 10))
    #=> 0 10 20 30 40 50 60 70 80 90
桁違いに簡単...
Copyright(C) sasakima-nao All rights reserved 2002 --- 2017.