![]() |
Home | Libraries | People | FAQ | More |
很多編譯器已經支持了 typeof,最值得關注的是 GCC 和 Metrowerks。
Igor Chesnokov 發現一個允許在 VC 系列編輯器上實現 typeof 的方法。它利用了微軟編譯器的一個 bug,這個 bug 允許一個基類的內嵌類定義在從基類派生出的類中:
template<int ID> struct typeof_access { struct id2type; //not defined }; template<class T, int ID> struct typeof_register : typeof_access { // define base's nested class here struct typeof_access::id2type { typedef T type; }; }; //Type registration function typeof_register<T, compile-time-constant> register_type(const T&); //Actually register type by instantiating typeof_register for the correct type sizeof(register_type(some-type)); //Use the base class to access the type. typedef typeof_access::id2type::type type;
當內嵌類是一個在派生類中特化的模板類時,這個方法也適合於 VC7.0。不過,這個漏洞在 VC8.0 中被修復,因此在這個編譯器上這個方法不能工作。
VC8.0 和許多其它編譯器既沒有原生 typeof 支持,也沒有像上面描述的訣竅作為一種選擇。對於這樣的編譯器仿真方法就是實現 typeof 的唯一出路。
根據一個大體的預測,在寫這篇文章的時候,typeof,auto 等被引入 C++ 標準的事情可能還不會馬上發生。即使在那之後,依然需要一段時間,大多數編譯器才能實現這個特性。但是即使在那之後,總還有一些老舊的編譯器需要支持(例如,現在,2005 年,VC7.x 出現很長時間了,甚至 VC8.0 beta 已經可以使用了,很多人依然使用 VC6)。
要想立刻使用這個非常有用的特性,把它實現在庫的層次顯然是很合理的。
仿真方式顯然很重要,即使在一些特定編譯器上提供了更好的選擇。如果一個庫作者想要使用 typeof 開發可移植代碼,她需要使用仿真方式並註冊她的類型和模板。那些仿真是唯一選擇的用戶可以使用它,而那些有著更好選擇的用戶也依然可以使用它,因為在這樣的編譯器上註冊宏被定義為 no-op(什麼都不做)。
還有一些其它考慮適用於 VC7.1 的用戶。即使有更方便的 typeof 訣竅可用,也應該考慮到升級到 VC8 的可能性,在那個編譯器上仿真是唯一的選擇。
通過定義 BOOST_TYPEOF_COMPLIANT 符號,可以將仿真模式強加於那些缺省時不使用它的編譯器:
g++ -D BOOST_TYPEOF_COMPLIANT -I \boost\boost_1_32_0 main.cpp
動機部分的 Lambda 示例需要如下註冊:
#include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP() BOOST_TYPEOF_REGISTER_TEMPLATE(boost::tuples::tuple, 2); BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::lambda_functor, 1); BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::lambda_functor_base, 2); BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::relational_action, 1); BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::logical_action, 1); BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::other_action, 1); BOOST_TYPEOF_REGISTER_TYPE(boost::lambda::greater_action); BOOST_TYPEOF_REGISTER_TYPE(boost::lambda::less_action); BOOST_TYPEOF_REGISTER_TYPE(boost::lambda::and_action); BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::placeholder, (int));
看上去,為了具有發現表達式的類型的能力所付出的代價太高了:需要相當大量的註冊。然而請注意,上面所有這些註冊只需要做一次,在那之後,已註冊類型和模板的任意組合都可以被處理。此外,這些註冊一般不需要最終用戶的參與,而是由一些庫的建構者來做(在本例中是 Boost.Lambda)。
在考慮這些的時候,考慮一下參與的三方是有幫助的:typeof 設施,庫(很可能基於表達式模板法則),和最終用戶。typeof 設施負責註冊基本類型。庫可以註冊它自己的類型和模板。
在最理想的狀態下,如果表達式總是由基本類型和庫定義的類型與模板組成,一個庫的作者可能會覺得 typeof 是被它的庫原生支持的。另一方面,表達式越經常地包含用戶定義類型,就有越多的責任落在最終用戶身上,並由此這個方法就變得越不吸引人。
這樣一來,當決定是否要應用 typeof 設施時,表達式中用戶定義類型的比例成為要考慮的主要因素。
Typeof 預先註冊了基本類型。對於這些類型,以及由用戶庫和最終用戶註冊的任何類型/模板,與以下的任意組合都已被支持:
例如,下面的類型:
int& (*)(const volatile char*, double[5], void(*)(short))
被毫無疑問地支持,而下面這樣的:
void (MyClass::*)(int MyClass::*, MyClass[10]) const
如果 MyClass 被註冊,即可被支持。
Typeof 庫為大多數 STL 類/模板提供了註冊文件。這些文件位於 std 子目錄內,命名與相應的 STL 頭文件相同。這些文件沒有被 typeof 系統包含,必須由用戶根據需要顯式包含:
#include <boost/typeof/std/functional.hpp> BOOST_AUTO(fun, std::bind2nd(std::less<int>(), 21)); //create named function object for future use.
為 Typeof 庫註冊類型的時候利用編譯器是可能的。即使當前還沒有在語言中直接支持 typeof,如果編譯器遇到無法正確處理的表達式,它可以覺察到表達式的類型是什麼並給出錯誤信息。在 typeof 上下文中,這個錯誤信息包含的線索能給你一些啟發,告訴你為了使 BOOST_TYPEOF 可以工作,需要為 Typeof 庫註冊什麼類型。
struct X {}; template<typename A,bool B> struct Y {}; std::pair<X,Y<int,true> > a; BOOST_AUTO(a,b);
我們從 VC7.1 得到如下錯誤信息
error C2504: 'boost::type_of::'anonymous-namespace'::encode_type_impl<V,Type_Not_Registered_With_Typeof_System>' : base
class undefined
with
[
V=boost::type_of::'anonymous-namespace'::encode_type_impl<boost::mpl::vector0<boost::mpl::na>,std::pair<X,Y<int,true>>>::V0,
Type_Not_Registered_With_Typeof_System=X
]
檢查這個錯誤信息,我們看到編譯器在抱怨 X
BOOST_TYPEOF_REGISTER_TYPE(X); //register X with the typeof system
再編譯,我們從 VC7.1 得到一個新的錯誤信息
error C2504: 'boost::type_of::'anonymous-namespace'::encode_type_impl<V,Type_Not_Registered_With_Typeof_System>' : base
class undefined
with
[
V=boost::type_of::'anonymous-namespace'::encode_type_impl<boost::mpl::vector0<boost::mpl::na>,std::pair<X,Y<int,true>>>::V1,
Type_Not_Registered_With_Typeof_System=Y<int,true>
]
檢查這個錯誤信息,我們看到編譯器在抱怨 Y<int,true>。因為 Y 是一個模板,並且包含整型常量,註冊時我們需要更加小心:
BOOST_TYPEOF_REGISTER_TEMPLATE(Y,(typename)(bool)); //register template class Y
當 Y 包含整型常量時,查尋它的精確定義是個好主意。對於只包含 typenames 的簡單模板類,你可以完全依賴編譯器錯誤。
上面的代碼現在可以編譯了。
這一技術可以用於得到一個「為了讓一個給定的項目支持 typeof,有哪些類型需要被註冊」的概覽。
不支持嵌套的 template template parameters(模板模板參數),比如:
template<template<template<class> class> class Tpl> class A; // can't register!
嵌套在其它模板中的類和模板也由於 nondeduced context(非推演上下文)的問題而不能註冊。這一限制在涉及到 Dinkumware STL 的標準迭代器時最應該引起注意,它們被實現為嵌套類。作為替代,它們的實例是可以被註冊的:
BOOST_TYPEOF_REGISTER_TYPE(std::list<int>::const_iterator)
| Copyright 2004, 2005 Arkadiy Vertleyb, Peder Holt |