boost.png (6897 bytes)Boost.MultiIndex 編譯器的特性



Boost.MultiIndex 已經在多個不同的編譯器中試過,有不同程序的成功。下面我們列出遇到的限制,以及可用時的適合環境中。

Contents目錄

Borland C++ Builder 6.4 及後續版本

當前,Boost.MultiIndex 不能用於 BCB 6.4 至 BCB 2006 的任何版本。測試中出現了很多問題,看起來庫的未來版本也很難在這個編譯器下工作。

Comeau C/C++ 4.3.10.1 for Windows (VC++ 9.0 為後端)

這個編譯器沒有發現問題。不過,如果以Microsoft Visual C++ 6.0作為後端,則編譯失敗。最近進行的測試中(Boost.1.34.1), VC++ 7.0/7.1 後端可以正常工作。使用 Comeau compiler 4.3.10.1 的 beta 2 版本。

Compaq C++ 6.5-042 for Tru64 UNIX 及後續版本

沒有發現問題。已測試Versions 6.5-042, 7.1-005 和 7.1-006。

GNU GCC 3.2 and later

版本3.2以後的幾個編譯器都沒有發現問題。已測試的版本有:

Boost.MultiIndex 不能工作於版本3.1及之前的GCC。

GNU GCC for Tru64 UNIX

在這個平台上,GCC不能調試長度超過32,768字節的符號名,導致mips-tfile, ... string too big. 象Boost.MultiIndex這樣的重量級模板代碼就可能遇到這種情形,因為它常常會產生長符號名。只要不使用選項-g (生成調試信息),就可以避免該問題。此外,不同的應用環境請參考減少符號名長度一節。

Darwin GCC 4.0

Darwin 8.2 及先前版本 (Mac OS X 10.4.2 及先前版本)帶的是GCC 4.0的 Build 4061,對應於GNU GCC 4.0.0 的預發佈版本,有一個 regression bug,該缺陷與綁定到臨時對象的引用有關。該缺陷影響Boost.MultiIndex 不變量檢查模式的使用。除此之外,Boost.MultiIndex 工作正常。該缺陷已在GCC 4.0 Apple build 5026中被修正,該版本對應於GNU GCC 4.0.0的官方版本,不變量檢查模式可用於該升級版本。

HP aC++ A.06.12 for HP-UX IA64 及後續版本

沒有發現問題。最近測試的版本是 A.06.12。

HP aC++ A.03.80 for HP-UX PA-RISC

沒有發現問題。

IBM VisualAge C++ V6.0 for AIX

注意: 這個信息最後對 Boost 1.33.1 做過檢查。由於該版本在這個平台上會導致問題,Boost 有可能會修改。


member 不被支持,詳細情況請參看 使用 member_offset 一節。member_offset 導致該編譯器產生告警:對非POD類型使用 offsetof。該告警可以通過設置編譯器選項 -qsuppress=1540-1281來關閉,或者加入以下預處理指令:

#pragma info(nolan)
  

不過該pragma指令也可能關閉掉其它與offsetof的使用無關的告警。


如Boost.Serialization不被該平台支持一樣,則序列化功能也不可用。

IBM VisualAge C++ V9.0 for AIX

不支持 member,替代方法參見 使用 member_offset 一節。member_offset 導致該編譯器產生告警:對非POD類型使用 offsetof。該告警可以通過設置編譯器選項 -qsuppress=1540-1281來關閉,或者加入以下預處理指令:

#pragma info(nolan)

不過該pragma指令也可能關閉掉其它與offsetof的使用無關的告警。除此之外,使用 Boost.MultiIndex 沒有其它問題。

Intel C++ Compiler for Linux 8.1 及後續版本

沒有發現問題。已對 8.1 至 10.0 的編譯器版本進行測試。

Intel C++ Compiler for Mac OS 9.1 及後續版本

沒有發現問題。已對 9.1 至 10.1 的編譯器版本進行測試。

Intel C++ Compiler for Windows 32-bit 8.0 及後續版本

使用MSVC++ 7.0或先前版本時,參數相關查找(ADL)缺省是被禁用的。對於很多Boost庫,這會引發問題,特別是Boost.MultiIndex的序列化部分。ADL可以通過 /Qoption,c,--arg_dep_lookup 選項激活。除此之外,Boost.MultiIndex 沒有其它問題。最後測試的編譯器版本是 10.1。

Intel C++ Compiler for Windows 64-bit 10.0 and later

沒有發現問題。最後測試的編譯器版本是 10.1。

Metrowerks CodeWarrior 8.3

以給定類型進行實例化的預定義鍵提取器不提供派生類型的對象。例如:

struct base{};
struct derived:public base{};
...

identity<base> key_extractor;
derived        x;

// 該編譯器不接受:要求顯式轉型為 base
key_extractor(x);
  

除此以外,Boost.MultiIndex 在兩個操作系統 Mac OS 和 Windows 都測試過,沒有問題。

Metrowerks CodeWarrior 9 及後續版本

Boost.MultiIndex 從 9.0 到 9.5 版本都工作正常,在兩個操作系統 Mac OS 和 Windows 都是如此。

Microsoft Visual C++ 6.0 Service Pack 5

註:最近的測試在 Boost 1.36 中。這個信息可能不再準確。


從 Boost.1.36 開始,Boost.Serialization 不再支持該編譯器,因此序列化能力不能再使用。


member 不被支持,詳見 使用 member_offset 一節。

const_mem_funmem_fun 不被支持,詳見 使用 const_mem_fun_explicitmem_fun_explicit 一節。


不支持 index retrievalprojection 的嵌套類型和成員函數:

你可以使用它們的全局等價物。另外,這個編譯器也沒有實現ADL,因此你需要在這些全局名字前加上::boost::multi_index

boost::multi_index::multi_index_container 已通過using聲明引入到namespace boost。但是,MSVC++ 6.0不能完全正確地處理名字的引入。因此,不能寫:

boost::multi_index_container<...>
  

而要這樣寫:

boost::multi_index::multi_index_container<...>
  

或者採用直接的 using namespace boost::multi_index.


由於MSVC++ 6.0不支持模板偏特化,所以在使用composite_key時會不太方便,解決的辦法請見 "在無模板偏特化的編譯器中使用composite_key"一節。


由於函數模板分類支持的問題,composite_key_compare 及相關類不接受 operator() 的符號變種,其中一個參數為長度為1的 tuple 時。結果用戶在指定查找操作的參數時不能省略 tuple。


以給定類型進行實例化的預定義鍵提取器不提供派生類型的對象。例如:

struct base{};
struct derived:public base{};
...

identity<base> key_extractor;
derived        x;

// 該編譯器不接受:要求顯式轉型為 base
key_extractor(x);
  

MSVC++ 6.0 在最大符號名長度方面也存在重大限制,會導致鏈接錯誤 LNK1179: invalid or corrupt file: duplicate comdat comdat. 解決該問題的辦法請見縮短符號名長度reduction of symbol name lengths 一節。


在某些情形下,編譯器會在MSVC內部頭文件<xlocnum>處產生錯誤 C2587: '_U' : illegal use of local variable as default parameter。這是該編譯器常見的bug,在其它庫中也曾出現過,如 Boost Graph Library, Boost.MultiArray, Boost.Regex, CGALMySQL++。該錯誤會在使用multi_index_container的迭代器構造函數時觸發。存在兩種解決辦法:第一種是避免使用該構造函數,將以下代碼:

multi_index_container<...> s(c.begin(),c.end());
  

替換為如下等價操作:

multi_index_container<...> s;
s.insert(c.begin(),c.end());
  

第二種辦法還沒有得到作者的確認,它是從互聯網上其它庫的同類錯誤所提供的。將<xlocnum>的第84行:

 #define _VIRTUAL	virtual
  

替換如下:

 #define _VIRTUAL
  

警告:現在還不清楚這種替換方法會否對其它使用<xlocnum>的代碼產生副作用。


Boost.MultiIndex 中大量使用的模板技術對於編譯器而言是重大的考驗,很多內部的局限都會被觸及。以下辦法可以減輕這些問題:

Microsoft Visual C++ 6.0 Service Pack 5 + STLport 4.5.3 及後續版本

Boost.MultiIndex 可以在此配置下工作。與 MSVC++ 6.0 + Dinkumware標準庫遇到的限制一樣。STLport 4.6.2 和 5.0.1 也已確認工作正常。


與STLport的動態版本工作時,不能使用 Boost.MultiIndex 的序列化功能,會產生鏈接錯誤。這時要使用STLport的靜態版本。該缺陷已在STLport 5.0中修復。

Microsoft Visual C++ 7.0

註:最近的測試在 Boost 1.35 中。以下信息可能不再準確。


從 Boost.1.36 開始,Boost.Serialization 不再支持該編譯器,因此序列化能力不能再使用。


member 不被支持,詳見 使用 member_offset 一節。

不支持 index retrievalprojection 的嵌套類型和成員函數:

你可以使用它們的全局等價物。另外,這個編譯器也沒有實現ADL,因此你需要在這些全局名字前加上::boost::multi_index

boost::multi_index::multi_index_container 已通過using聲明引入到namespace boost。但是,MSVC++ 7.0不能完全正確地處理名字的引入。因此,不能寫:

boost::multi_index_container<...>
  

而要這樣寫:

boost::multi_index::multi_index_container<...>
  

或者採用直接的 using namespace boost::multi_index.


由於MSVC++ 7.0不支持模板偏特化,所以在使用composite_key時會不太方便,解決的辦法請見 "在無模板偏特化的編譯器中使用composite_key"一節。


由於函數模板分類支持的問題,composite_key_compare 及相關類不接受 operator() 的符號變種,其中一個參數為長度為1的 tuple 時。結果用戶在指定查找操作的參數時不能省略 tuple。


以給定類型進行實例化的預定義鍵提取器不提供派生類型的對象。例如:

struct base{};
struct derived:public base{};
...

identity<base> key_extractor;
derived        x;

// 該編譯器不接受:要求顯式轉型為 base
key_extractor(x);
  

Microsoft Visual C++ 7.0 + STLport 5.0.1

注: 最後測試的是 Boost 1.35. 這一信息可能不再準確。

Boost.MultiIndex 在此配置下可用。MSVC++ 7.0 + 原本的 Dinkumware 標準庫也是如此。

Microsoft Visual C++ 7.1

當使用/Gm選項(Enable Minimal Rebuild)編譯本庫時會有問題。看起來這應該是編譯器的內部問題(請見Boost用戶郵件列表中的 this mention of a similar issue)。如果關閉/Gm,Boost.MultiIndex 的編譯和運行都沒有問題。

Microsoft Visual C++ 8.0 及後續版本

沒有發現問題。最後測試的是編譯器版本8.0和9.0。

Microsoft Visual C++ 8.0 x64 交叉編譯器 及後續版本

沒有發現問題。最後測試的是編譯器版本9.0。

Sun Studio 10 for Solaris 及後續版本

沒有發現問題。以下編譯器版本已被測試:

使用了選項 -library=stlport4 來以 STLport 替換缺省的標準庫。

可移植性技術

使用 member_offset

member 鍵提取器在不能正確支持以成員指針為非類型模板參數的編譯器上會有問題,正如 Boost Configuration Library 的宏 BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS 所指出的一樣。以下編譯器已被證實不能正確處理 member

以下程序可以幫助確認你的編譯器是否正確支持以成員指針為非類型模板參數:
#include <iostream>

struct pair
{
  int x,y;

  pair(int x_,int y_):x(x_),y(y_){}
};

template<int pair::* PtrToPairMember>
struct foo
{
  int bar(pair& p){return p.*PtrToPairMember;}
};

int main()
{
  pair p(0,1);
  foo<&pair::x> fx;
  foo<&pair::y> fy;

  if(fx.bar(p)!=0||fy.bar(p)!=1)std::cout<<"KO"<<std::endl;
  else std::cout<<"OK"<<std::endl;

  return 0;
}

  

如果你發現某個編譯器不能通過測試,而且其沒有定義 BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS 宏,請報告到 Boost 開發者郵件列表。

為了解決該問題,提供了一個代用工具 member_offset,它可以完成 member 的工作,只是用起來不太方便,而且可能不太符合標準。關於member_offset的詳細說明請見「參考」一節。作為示例,給出一個類:

class A
{
  int x;
}
  

實例化 member<A,int,&A::x> 可以替換為 member_offset<A,int,offsetof(A,x)>.

為了代碼的可移植性,Boost.MultiIndex 提供了三元宏 BOOST_MULTI_INDEX_MEMBER. 繼續上面的例子,表達式:

BOOST_MULTI_INDEX_MEMBER(A,int,x)
  

缺省被展開為:

member<A,int,&A::x>
  

或者當BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS被定義時,展開為:

member_offset<A,int,offsetof(A,x)>
  


使用 const_mem_fun_explicitmem_fun_explicit

MSVC++ 6.0 在使用 const 成員函數作為非類型模板參數時有問題,因此不能接受 const_mem_fun 鍵提取器。一個簡單的解決辦法是,增加一個模板參數以指定該指針的類型。提取器 const_mem_fun_explicit 用的就是這個辦法,例如以下類型:

struct A
{
  int f()const;
};
  

提取器 const_mem_fun<A,int,&A::f> 可以替換為 const_mem_fun_explicit<A,int,int (A::*)()const,&A::f>。另一個類似的 mem_fun_explicit 類模板可用於非常量成員函數。

如果你需要跨平台的代碼,可以用宏BOOST_MULTI_INDEX_CONST_MEM_FUN來選擇使用哪個鍵提取器,以下宏:

BOOST_MULTI_INDEX_CONST_MEM_FUN(A,int,f)
  

缺省被展開為:

const_mem_fun<A,int,&A::f>
  

而在MSVC++ 6.0中則被展開為:

const_mem_fun_explicit<A,int,int (A::*)()const,&A::f>
  

const成員函數則使用 mem_fun_explicit 和宏 BOOST_MULTI_INDEX_MEM_FUN.

在無模板偏特化的編譯器中使用 composite_key

使用 composite_key 時,要傳入多個值的組合以執行查找:這需要為 composite_key_result的實例正確地特化類模板 std::equal_to, std::less, std::greaterboost::hash,以提供接受 tuple 的正確重載函數——而且對於 std::lessstd::greater,還要接受僅有第一部分的組合。

在不支持模板偏特化的編譯器中,這些特化不能得到支持,因此基於組合值的查找缺省情況下無法使用。這種情況下,使用組合鍵的 multi_index_container 實例還可以工作,無論是有序索引還是散列索引,除了其查找操作不能以組合值為參數。對於有序索引,最明顯的解決辦法是顯式地用composite_key_compare給出比較謂詞;對於散列索引,我們可以使用 composite_key_equal_tocomposite_key_hash。不過這些解決辦法都很冗長,需要顯式地給出鍵提取器的所有組合元素。因此,Boost.MultiIndex 提供了以下替代類模板:

它們替代了composite_key_resultstd::equal_to, std::less, std::greaterboost::hash 特化。它們可以像以下那樣使用:
typedef composite_key<
  phonebook_entry,
  member<phonebook_entry,std::string,&phonebook_entry::family_name>,
  member<phonebook_entry,std::string,&phonebook_entry::given_name>
> ckey_t;

typedef multi_index_container<
  phonebook_entry,
  indexed_by<
    ordered_non_unique< 
ckey_t, // composite_key_result_less 替代 std::less<ckey_t::result_type> composite_key_result_less<ckey_t::result_type> >, ordered_unique< member<phonebook_entry,std::string,&phonebook_entry::phone_number> > > > phonebook;

縮短符號名長度

multi_index_container實例化所產生的類型通常都有很長的符號名,有時會超出編譯器的限制。有幾個方法可以縮短這些生成的符號名,這些方法還可以使得錯誤信息更容易讀。

參數數量的最大限制

類模板 indexed_by, tagcomposite_key 接受可變數量的參數,其最大值通過內部宏限定。即使沒有使用的參數也會增加最終類型名字的長度,所以手工調整相應的宏可以明顯縮短符號名。

Boost.MultiIndex 的某些類模板的參數數量的最大限制值
類模板 限制宏 缺省值 缺省值
(MSVC++ 6.0)
 indexed_by   BOOST_MULTI_INDEX_LIMIT_INDEXED_BY_SIZE  20 5
 tag   BOOST_MULTI_INDEX_LIMIT_TAG_SIZE  20 3
 composite_key   BOOST_MULTI_INDEX_LIMIT_COMPOSITE_KEY_SIZE  10 5

類型隱藏

考慮以下multi_index_container實例化:

typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<identity<employee> >,
    ordered_non_unique<member<employee,std::string,&employee::name> >,
    ordered_unique<member<employee,int,&employee::ssnumber> >
  >
> employee_set;
  

在GCC中,類型 employee_set::nth_type<0>::type 會被展開如下:

boost::multi_index::detail::ordered_index<
  boost::multi_index::identity<employee>,
  std::less<employee>,
  boost::multi_index::detail::nth_layer<
    1, employee,
    boost::multi_index::indexed_by<
      boost::multi_index::ordered_unique<
        boost::multi_index::identity<employee>, mpl_::na, mpl_::na
      >,
      boost::multi_index::ordered_non_unique<
        boost::multi_index::member<employee, std::string, &employee::name>,
        mpl_::na, mpl_::na
      >,
      boost::multi_index::ordered_unique<
        boost::multi_index::member<employee, int, &employee::ssnumber>,
        mpl_::na, mpl_::na
      >,
      mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na,
      mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na,
      mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na
    >,
    std::allocator<employee>
  >,
  boost::mpl::vector0<mpl_::na>,
  boost::multi_index::detail::ordered_unique_tag
>
  

可以看到類型名的主要部分是 indexed_by<...> 部分,而它只是employee_set定義中的索引說明部分的擴展版本。我們可以防止這種超長名字,方法是將它封裝成一個名字短一些的結構:

// 過類型隱藏縮短符號名
//
employee_set_indices隱藏索引說明列表 struct employee_set_indices: indexed_by< ordered_unique<identity<employee> >, ordered_non_unique<member<employee,std::string,&employee::name> >, ordered_unique<member<employee,int,&employee::ssnumber> > > {}; typedef multi_index_container< employee, employee_set_indices > employee_set;

employee_set_indices 就像一個常見的 typedef 那樣保存了詳細信息,但是它的名字不像indexed_by實例化那樣顯式包含信息。使用這個方法,employee_set::nth_type<0>::type 現在變成了:

boost::multi_index::detail::ordered_index<
  boost::multi_index::identity<employee>,
  std::less<employee>,
  boost::multi_index::detail::nth_layer<
    1, employee,
    employee_set_indices,
    std::allocator<employee>
  >,
  boost::mpl::vector0<mpl_::na>,
  boost::multi_index::detail::ordered_unique_tag
>
  

明顯比原先的短了許多,也更易於閱讀。如果我們不是將employee_set_indicesstruct indexed_by<...>繼承,而是用typedef,那麼類型隱藏將沒有用:typedef 是語法上的別名,編譯器在進一步處理之前都要先展開它。

類型隱藏的方法也可用於同樣會產生長名字的 composite_key 實例化。




Revised October 6th 2008

c Copyright 2003-2008 Joaquin M Lopez Munoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)