昨天別人碰到的問題,晚上回來想了一下給出了一個用模板進行類型推導的解決方案。
問題本身需求很清楚,就是需要寫一個Util函數,將C++裡面的那些模板(template)集合對象自動轉換成.NET裡面的泛型(Generic)集合對象,比如將vector<int>轉化成List<int>^。因為類型(Type)不定,所以用模板來進行轉化是不二的選擇。問題的痛點在於這些集合類還可以是巢狀型別,也就是說集合裡面裝的還是集合(比如將vector<vector<int> >轉化成List<List<int>^>^),甚至還得考慮多層集合嵌套的情況。
- // NativeToManagedTypeApp.cpp : main project file.
- // author: Anders Deng
- // date: 2008/8/29
- #include "stdafx.h"
- #include <vector>
- using namespace System;
- using namespace System::Collections::Generic;
- using namespace std;
- // for method overload
- struct __true__value {};
- struct __false__value{};
- // some classes for type traits
- template<class T>
- struct collection_Type_Traits
- {
- typedef __false__value isCollection;
- typedef T __template__param__type;
- };
- template<typename T>
- struct collection_Type_Traits<std::vector<T> >
- {
- typedef __true__value isCollection;
- typedef T __template__param__type;
- };
- template<class T>
- struct collection_Type_Traits<List<T>^ >
- {
- typedef __true__value isCollection;
- typedef T __template__param__type;
- };
- // add more specialization template definition
- // ...
- template<class T>
- struct managed_Type_Traits
- {
- typedef __false__value isValueType;
- typedef T param_type_name;
- };
- template<class T>
- struct managed_Type_Traits<T^>
- {
- typedef __false__value isValueType;
- typedef T param_type_name; // remove ^ from reference type
- };
- template<>
- struct managed_Type_Traits<int>
- {
- typedef __true__value isValueType;
- typedef int param_type_name;
- };
- // add more specialization template definition for other required value class
- // ...
- public ref class Util
- {
- public:
- template<class NativeType, class ManagedType>
- static ManagedType nativeToManaged(NativeType nativeObj)
- {
- return __nativeToManaged<NativeType, ManagedType>(nativeObj, collection_Type_Traits<NativeType>::isCollection(), managed_Type_Traits<ManagedType>::isValueType());
- }
- private:
- // collection && reference type
- template<class NativeType, class ManagedType>
- static ManagedType __nativeToManaged(NativeType nativeObj, __true__value, __false__value)
- {
- ManagedType list = gcnew managed_Type_Traits<ManagedType>::param_type_name();
- for (NativeType::iterator it = nativeObj.begin(); it != nativeObj.end(); ++it)
- {
- typedef collection_Type_Traits<NativeType>::__template__param__type InternalNativeType;
- typedef collection_Type_Traits<ManagedType>::__template__param__type InternalManagedType;
- list->Add(__nativeToManaged<InternalNativeType, InternalManagedType>(*it, collection_Type_Traits<InternalNativeType>::isCollection(), managed_Type_Traits<InternalManagedType>::isValueType()));
- }
- return list;
- }
- // non-collection && reference type
- template<class NativeType, class ManagedType>
- static ManagedType __nativeToManaged(NativeType nativeObj, __false__value, __false__value)
- {
- return gcnew managed_Type_Traits<ManagedType>::param_type_name(nativeObj.c_str());
- }
- // non-collection && reference type
- template<class NativeType, class ManagedType>
- static ManagedType __nativeToManaged(NativeType nativeObj, __false__value, __true__value)
- {
- return nativeObj;
- }
- };
- int main(array<System::String ^> ^args)
- {
- // ----------------------------------
- // test vector<int> => List<int>^
- // ----------------------------------
- vector<int> intV;
- intV.push_back(4);
- intV.push_back(5);
- List<int>^ l = Util::nativeToManaged<vector<int>, List<int>^>(intV);
- for each(int i in l)
- {
- Console::Write("{0} ", i);
- }
- Console::WriteLine();
- // ----------------------------------
- // test vector<vector<int>> => List<List<int>^>^
- // ----------------------------------
- vector<vector<int>> iVV;
- vector<int> iV;
- iV.push_back(1);
- iV.push_back(2);
- iVV.push_back(iV);
- List<List<int>^ >^ list = Util::nativeToManaged<vector<vector<int> >, List<List<int>^>^ >(iVV);
- for each(List<int>^ l in list)
- {
- for each(int i in l)
- Console::Write("{0} ", i);
- Console::WriteLine();
- }
- // ---------------------------------
- // test vector<vector<string>> => List<List<String^>^>^
- // ---------------------------------
- vector<string> sV;
- vector<vector<string>> sVV;
- sV.push_back("abc");
- sV.push_back("def");
- sV.push_back("ghi");
- sVV.push_back(sV);
- List<List<String^>^>^ sll = Util::nativeToManaged<vector<vector<std::string>>, List<List<String^>^>^>(sVV);
- for each(List<String^>^ sl in sll)
- {
- for each(String^ s in sl)
- Console::Write("{0} ", s);
- Console::WriteLine();
- }
- return 0;
- }
代碼如上,就不多解釋了。關於type traits可以參考這篇文章,關於特化(specialization)、偏特化(partial specialization)的文章可以看侯捷先生的《STL源碼分析》。需要特別指出的是,雖然.NET的Generic原理上不支援特化和偏特化的用法(因為.NET泛型是運行時機制,考慮到.NET極強的meta data,個人覺得確實也不必要),但是C++/CLI使用的時候仍然可以利用模板來進行特化/偏特化處理。比如上面代碼中的managed_Type_Traits<T^>類型就是用來提取不帶^符號的原始類型,這樣就可以讓模板自動產生出gcnew T(...)這樣的代碼了。Anyway,C++的模板就跟宏(Macro)一樣,就看你怎麼運用其產生需要的代碼了。
記得n年前學C++的時候看過不少講模板特化,偏特化的資料,讀STL、boost等Library原始碼的時候也發現裡面將模板元編程(TMP)的思想發揮的淋漓盡致。不過慚愧的是自己真正coding的時候還沒直接用過,這次算是親自試了一把,功能還是很強大的(當然請不要拿來與諸如Ruby這樣的動態語言比,畢竟they are totally different)。
小結:玩TMP實際上就是與編譯器鬥,其樂無窮:)