Skip to content

template

NAGAMINE Hideaki edited this page Feb 14, 2017 · 10 revisions

C++ の template の XcodeML 化について

基本方針

  • template の実体化前の姿(つまりソースコードに書かれたままの姿)について:
    • Clang AST の表現方針をなるべくそのまま活用して、 XcodeML の declarations (globalDeclarations) の構造に変換して、ソースコード逆変換に備える。
  • template の実体化後の姿(つまりその翻訳単位で具体的なテンプレートパラメータを与えた後の姿)について:
    • Clang AST にその情報が別途存在するのでそれを活用して、 XcodeML に templateInstances という新セクションを準備して、そこに declarations や symbols を並べる。
    • declarations 中に出現する各種の式の型は type= 属性を付加するが、この type には具象型(テンプレートパラメータを含まない型)がつくはずなので、そこに現れる型名は typeTable (現状のとおり、翻訳単位全体で唯一の typeTable) に載せる。
    • また、 templateInstances セクションの中身はバックエンドエンジンが編集して変更することもありうるので、逆変換時にはこの中身をもソースコードに復元する必要がある。これは、前者(つまりもともとの template そのもの)をソースに復元した上で、後者(templateInstances の中身)を「templateの完全特殊化」の形で復元する。

つまり、「template 宣言そのもの」と「実体化後の姿」の両方を XcodeML として表現し、逆変換時にも両方復元する。

以下に復元後のC++ソースコードの例を示す。

template<typename T>
T accumulate(const std::vector<T> & v) {
  T sum(0);
  for (int i = 0; i < v.size(); ++i) {
    sum += v[i];
  }
  return sum;
}

__attribute__((weak))
template<>
int accumulate<int>(const std::vector<int> & v) {
  int sum(0);
  for (int i = 0; i < v.size(); ++i) {
    sum += v[i];
  }
  return sum;
}

int main() {
  std::vector<int> v = {1, 2, 3};
  int sum = accumulate(v);
  return 0;
}

main内でaccumulateの実体化が行われたため、特殊化のコードが出力されている。

typeTable や nnsTable の入れ子構造

template では、パラメータとして型引数や非型引数をとることができて、これを用いた型や定数を宣言することができる。 このため、 template 内では「パラメータから求まる型(例えばパラメータTに対して T* という型)」や「パラメータから求まる定数」が記述できる。 ここで特に前者について、これまでのXcodeMLにおける「typeTableは翻訳単位全体で一つ」という設計とはどうしても相いれないので、 templateDefinition(仮名)要素内に localTypeTable 要素を持たせ、そこにパラメータ依存の型を記述するものとする。 (要検討:パラメータそのものはどうやって表現するか?)

また、これと同様の問題が nnsTable についても存在するため、 localNnsTable も必要に応じて用いる。

これらの問題は、 template に限らず、入れ子のクラスやローカルクラスについても同様の問題があるため、 それらも統一的に扱うものとして考えればすっきりした設計になると考えられる。

プログラムの例

template<typename T, int n>
class A {
  public:
    A(const T&);
    T::elem_type elem();
  private:
    T t;
    int i;
};
<templateClassDefinition>
  <localNnsTable>
    <nestedNameSpecifier nns="Q0">
      <typeName kind="param">T</typeName>
    </nestedNameSpecifier>
  </localNnsTable>
  <localTypeTable>
    <templateParamType name="T0" />
    <basicType is_const="true" type="T0" name="const_T0" />
    <pointerType is_lvalue_reference="true" ref="const_T0" name="ref_to_const_T0" />
    <childType nns="Q0" name="T0__elem_type">elem_type</childType>
    <functionType name="ctor_of_classA">
      <params>
        <id type="ref_to_const_T0"/>
      </params>
    </functionType>
    <functionType name="fun_to_T0__elem_type" return_type="T0__elem_type">
      <params />
    </functionType>
    <classType name="classA">
      <symbols>
        <id type="ctor_of_classA">
          <name>A</name>
        </id>
        <id type="fun_to_T0__elem_type">
          <name>elem</name>
        </id>
        <id type="T0">
          <name>t</name>
        </id>
        <id type="int">
          <name>i</name>
        </id>
      </symbols>
      <declarations>
        <!-- ... -->
      </declarations>
    </classType>
  </localTypeTable>
  <params>
    <id kind="type_param" name="T0">
      <name>T</name>
    </id>
    <id kind="nontype_param" type="int">
      <name>n</name>
    </id>
  </params>
  <declaration>
    <classDecl type="classA"/>
  </declaration>
</templateClassDefinition>

すでに分かっている注意事項

  • template の完全特殊化をおこなうと、その定義はリンク時に唯一でなければならない(内容が一致しているものが複数あってもよい、という意味での ODR 例外をうけることがなくなるので、本当の意味で唯一でなければならない)という問題が発生する。 →これは、 gcc (clang にもある) の attribute の weak 属性をつけることで解消される。

実体化前の姿の表現について

  • template パラメータは変数宣言の型部分に現れる。現状での declarations での変数宣言は型を type= 属性で表現しているので、 template パラメータを意味する記法が新たに必要になりそう。
  • T *x; のような型など、 template パラメータを使ってさらに別の型を作っている場合はどう表現するか?