gcc的模版匹配及其它

一、gcc的模版匹配实现

1、主体函数
gcc的代码实现现在看起来依然晦涩,所以下面的分析只是大致的一个意会过程,没有精确详细的描述。以gcc4.1.0版本为例,模版特殊化的具体判断主要在gcc-4.1.0\gcc\cp\pt.c:most_specialized_class函数完成,从代码中可以看到,一个模版的所有特殊化列表保存在模版的DECL_TEMPLATE_SPECIALIZATIONS (tmpl)链表中,对于每一个模版特殊化声明,它的TREE_TYPE (t)表示该模版特殊化声明指明的参数,TREE_VALUE (t)表名该声明所有引用的模版参数。以
template <class T> class foo<T t, T* pst> {};
为例,它的TREE_TYPE为<T t, T* pst>列表,而TREE_VALUE 则为class T。
/* Return the most specialized of the class template partial
   specializations of TMPL which can produce TYPE, a specialization of
   TMPL.  The value returned is actually a TREE_LIST; the TREE_TYPE is
   a _TYPE node corresponding to the partial specialization, while the
   TREE_PURPOSE is the set of template arguments that must be
   substituted into the TREE_TYPE in order to generate TYPE.
 
   If the choice of partial specialization is ambiguous, a diagnostic
   is issued, and the error_mark_node is returned.  If there are no
   partial specializations of TMPL matching TYPE, then NULL_TREE is
   returned.  */
 
static tree
most_specialized_class (tree type, tree tmpl)
{
  tree list = NULL_TREE;
  tree t;
  tree champ;
  int fate;
  bool ambiguous_p;
  tree args;
 
  tmpl = most_general_template (tmpl);
  args = CLASSTYPE_TI_ARGS (type);
  for (t = DECL_TEMPLATE_SPECIALIZATIONS (tmpl); t; t = TREE_CHAIN (t))
    {
      tree partial_spec_args;
      tree spec_args;
 
      partial_spec_args = CLASSTYPE_TI_ARGS (TREE_TYPE (t));
      spec_args = get_class_bindings (TREE_VALUE (t), 
      partial_spec_args, 
      args);
      if (spec_args)
{
  list = tree_cons (spec_args, TREE_VALUE (t), list);
  TREE_TYPE (list) = TREE_TYPE (t);
}
    }
 
  if (! list)
    return NULL_TREE;
 
  ambiguous_p = false;
  t = list;
  champ = t;
  t = TREE_CHAIN (t);
  for (; t; t = TREE_CHAIN (t))
    {
      fate = more_specialized_class (champ, t);
      if (fate == 1)
;
      else
{
  if (fate == 0)
    {
      t = TREE_CHAIN (t);
      if (! t)
{
  ambiguous_p = true;
  break;
}
    }
  champ = t;
}
    }
 
  if (!ambiguous_p)
    for (t = list; t && t != champ; t = TREE_CHAIN (t))
      {
fate = more_specialized_class (champ, t);
if (fate != 1)
  {
    ambiguous_p = true;
    break;
  }
      }
 
  if (ambiguous_p)
    {
      const char *str = "candidates are:";
      error ("ambiguous class template instantiation for %q#T", type);
      for (t = list; t; t = TREE_CHAIN (t))
{
  error ("%s %+#T", str, TREE_TYPE (t));
  str = "               ";
}
      return error_mark_node;
    }
 
  return champ;
}
2、如何推倒参数
具体实例化通过get_class_bindings函数实现,它首先通过unify判断经过参数推倒获得的parameter是否一致,如果有冲突,则认为特殊化声明匹配失败;如果没有冲突,使用tsubst函数进行具体参数替换和检验。
这里关键的步骤是unify函数的实现,它是如何推倒的?这个匹配其实和正则表达式的匹配类似,只是正则表达式中是字符匹配,这里是类型或者说结构匹配。以
template <class T> class foo<T(*pfun)(T)> {};
为例,当一个函数定义为
int bar(int)
将这个函数传递给foo,声明结构
class foo<bar>;
此时模板匹配就开始从结构到类型、从外到内进行匹配,对于这个例子,首先判断声明和调用都使用了函数,然后返回值确定T的类型为int,再判断参数推倒出的类型和返回值推倒的类型一致,从而匹配成功,当然中间可以尝试一些默认转换来完成匹配,这个转换是语言规定的例如从派生类到基类的转换、低精度整数向高精度整数的转换等。
下面是unify (tree tparms, tree targs, tree parm, tree arg, int strict)函数中对于函数的判断
  switch (TREE_CODE (parm))
    {
……
   case METHOD_TYPE:
    case FUNCTION_TYPE:
      if (TREE_CODE (arg) != TREE_CODE (parm))
return 1;
 
      /* CV qualifications for methods can never be deduced, they must
    match exactly.  We need to check them explicitly here,
    because type_unification_real treats them as any other
    cvqualified parameter.  */
      if (TREE_CODE (parm) == METHOD_TYPE
  && (!check_cv_quals_for_unify
      (UNIFY_ALLOW_NONE,
       TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (arg))),
       TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (parm))))))
return 1;
 
      if (unify (tparms, targs, TREE_TYPE (parm),
 TREE_TYPE (arg), UNIFY_ALLOW_NONE))函数类型及返回值判断
return 1;
      return type_unification_real (tparms, targs, TYPE_ARG_TYPES (parm),
    TYPE_ARG_TYPES (arg), 1, DEDUCE_EXACT, 函数参数列表一致性判断
    LOOKUP_NORMAL);
}
3、如何判断哪个更加特殊
这个地址是IBM的一个说明,摘录过来,其内容为
A template X is more specialized than a template Y if every argument list that matches the one specified by X also matches the one specified by Y, but not the other way around. If the compiler cannot find the most specialized specialization, then the use of the class template is ambiguous; the compiler will not allow the program.
对于gcc的实现来说,它比较两个特殊化哪个更特殊,是通过将A特殊化声明推倒出来的参数作为B的特殊化参数,看是否可以通过一致性检测并替换成功,然后反过来再执行一次,如果A推倒出的参数可以通过B的检测而反过来不行,则A胜出;如果A推倒的参数B可以通过,并且B推倒的参数A也可以,则平局,否则B胜出。
int
more_specialized_class (tree pat1, tree pat2)
{
  tree targs;
  tree tmpl1, tmpl2;
  int winner = 0;
  
  tmpl1 = TREE_TYPE (pat1);
  tmpl2 = TREE_TYPE (pat2);
 
  /* Just like what happens for functions, if we are ordering between
     different class template specializations, we may encounter dependent
     types in the arguments, and we need our dependency check functions
     to behave correctly.  */
  ++processing_template_decl;
  targs = get_class_bindings (TREE_VALUE (pat1), 
      CLASSTYPE_TI_ARGS (tmpl1),
      CLASSTYPE_TI_ARGS (tmpl2));
  if (targs)
    --winner;
 
  targs = get_class_bindings (TREE_VALUE (pat2), 
      CLASSTYPE_TI_ARGS (tmpl2),
      CLASSTYPE_TI_ARGS (tmpl1));
  if (targs)
    ++winner;
  --processing_template_decl;
 
  return winner;
}
4、以一个例子说明下
下面是从stackoverflow上搜到的一个例子,放在这个环境里说明下:
When looking at X<int, T*, 10> and X<T, T*, I>:
对于实例化X<int, float*, 10>来说,根据这个输入推倒出第一个模板特殊化参数T的值为float,整个列表就是<int, float*, 10>,用这个列表实例化X<T, T*, I>,也可以成功;反过来亦然,所以此处有二义性。从这里看,大部分推倒都应该是唯一的,两个都可以通常发生在一个发生了隐式转换导致的。
二、bash的case语句判断
bash对每个pattern中的内容都不作解释,它不像C语言一样判断是否有重复内容,只是把它们按照脚本中出现的顺序组成列表,然后逐个判断:
execute_case_command (case_command)
 for (clauses = case_command->clauses; clauses; clauses = clauses->next)
    {
      QUIT;
      for (list = clauses->patterns; list; list = list->next)
{
  es = expand_word_leave_quoted (list->word, 0);
 
  if (es && es->word && es->word->word && *(es->word->word))
    pattern = quote_string_for_globbing (es->word->word, QGLOB_CVTNULL);
  else
    {
      pattern = (char *)xmalloc (1);
      pattern[0] = '\0';
    }
 
  /* Since the pattern does not undergo quote removal (as per
     Posix.2, section 3.9.4.3), the strmatch () call must be able
     to recognize backslashes as escape characters. */
  match = strmatch (pattern, word, FNMATCH_EXTFLAG|FNMATCH_IGNCASE) != FNM_NOMATCH;
  free (pattern);
 
  dispose_words (es);
 
  if (match)
    {
      do
{
  if (clauses->action && ignore_return)
    clauses->action->flags |= CMD_IGNORE_RETURN;
  retval = execute_command (clauses->action);
}
      while ((clauses->flags & CASEPAT_FALLTHROUGH) && (clauses = clauses->next));
      if ((clauses->flags & CASEPAT_TESTNEXT) == 0)
EXIT_CASE ();
      else
break;
    }
 
  QUIT;
}
    }
三、make规则匹配
在3.82之前,如果多个pattern匹配,会选择第一个,之后会选择通配符匹配到最短的那条。
make-3.82\implicit.c:
int
stemlen_compare (const void *v1, const void *v2)
{
  const struct tryrule *r1 = v1;
  const struct tryrule *r2 = v2;
  int r = r1->stemlen - r2->stemlen;
  return r != 0 ? r : (int)(r1->order - r2->order);
}
 
static int
pattern_search (struct file *file, int archive,
                unsigned int depth, unsigned int recursions)
  /* Sort the rules to place matches with the shortest stem first. This
     way the most specific rules will be tried first. */
  if (nrules > 1)
    qsort (tryrules, nrules, sizeof (struct tryrule), stemlen_compare);
四、正则表达式的多匹配
默认是greedy匹配,而且有些不支持non-greedy匹配。

posted on 2019-03-07 09:43  tsecer  阅读(325)  评论(0编辑  收藏  举报

导航