您的位置:首页 > 编程语言

GCC-3.4.6源代码学习笔记(55)

2010-07-08 14:39 531 查看
4.3.1.7.4. 创建std名字空间
4.3.1.7.4.1. 加入全局名字空间
std名字空间是C++标准的一部分。这一名字空间亦是C++的运行时环境的一部分,例如:异常机制、运行时类型识别、标准库等都出自此处。因此,接下来就要创建std名字空间并将其加入全局名字空间。这里std_identifier是std名字空间的全局唯一标识符,它在initialize_predefined_identifiers中已被创建。

cxx_init_decl_processing (continue)

2995 /* Create the `std' namespace. */
2996 push_namespace (std_identifier);
2997 std_node = current_namespace;
2998 pop_namespace ();

函数push_namespace包办了创建std名字空间对象,将其加入全局名字空间(准确地说应该是当前名字空间),并使其成为当前作用域。注意下面3075行,如果name为空,这个名字空间就是匿名名字空间。匿名名字空间只能通过以下方式访问:
namespace A {
int x1;
namespace { // first annoymous namespace
int x2; // the only way to visit x2
... // definition
namespace { // another annoymous namespace
...
}
}
}
A::x1 = 5; // no applicable to annoymous namespace
注意不同层次的匿名名字空间可以有多个。虽然匿名名字空间对于用户来说是无名的,编译器在内部必须为其命名。变量anonymous_namespace_name就是匿名名字空间的内部名字,而且如果有多个匿名名字空间,它们共享这个名字。又,因为匿名名字空间只能以上面的方式访问,因此匿名名字空间使用同一个名字不存在任何问题。

3059 void
3060 push_namespace (tree name) in name-lookup.c
3061 {
3062 tree d = NULL_TREE;
3063 int need_new = 1;
3064 int implicit_use = 0;
3065 bool anon = !name;
3066
3067 timevar_push (TV_NAME_LOOKUP);
3068
3069 /* We should not get here if the global_namespace is not yet constructed
3070 nor if NAME designates the global namespace: The global scope is
3071 constructed elsewhere. */
3072 my_friendly_assert (global_namespace != NULL && name != global_scope_name,
3073 20030531);
3074
3075 if (anon)
3076 {
3077 /* The name of anonymous namespace is unique for the translation
3078 unit. */
3079 if (!anonymous_namespace_name)
3080 anonymous_namespace_name = get_file_function_name ('N');
3081 name = anonymous_namespace_name;
3082 d = IDENTIFIER_NAMESPACE_VALUE (name);
3083 if (d)
3084 /* Reopening anonymous namespace. */
3085 need_new = 0;
3086 implicit_use = 1;
3087 }
3088 else
3089 {
3090 /* Check whether this is an extended namespace definition. */
3091 d = IDENTIFIER_NAMESPACE_VALUE (name);
3092 if (d != NULL_TREE && TREE_CODE (d) == NAMESPACE_DECL)
3093 {
3094 need_new = 0;
3095 if (DECL_NAMESPACE_ALIAS (d))
3096 {
3097 error ("namespace alias `%D' not allowed here, assuming `%D'",
3098 d, DECL_NAMESPACE_ALIAS (d));
3099 d = DECL_NAMESPACE_ALIAS (d);
3100 }
3101 }
3102 }

确定了名字空间的名字,具体工作交由IDENTIFIER_NAMESPACE_VALUE。

264 #define IDENTIFIER_NAMESPACE_VALUE(NODE) / in cp-tree.h
265 namespace_binding ((NODE), current_namespace)

current_namespace的名称具有欺骗性,实际上这个宏得到的是当前使用的名字空间或者上一次使用的名字空间(当前作用域不是名字空间)。

719 #define current_namespace scope_chain->old_namespace in cp-tree.h

注意对于全局名字空间,scope_chain是一个空节点,因此current_namespace将返回null。这个特殊值在名字空间查找时就意味着全局名字空间。

2943 tree
2944 namespace_binding (tree name, tree scope) in name-lookup.c
2945 {
2946 cxx_binding *binding;
2947
2948 if (scope == NULL)
2949 scope = global_namespace;
2950 scope = ORIGINAL_NAMESPACE (scope);
2951 binding = cxx_scope_find_binding_for_name (NAMESPACE_LEVEL (scope), name);
2952
2953 return binding ? binding->value : NULL_TREE;
2954 }

对于各种*_DECL节点,域abstract_origin指向该声明作为一个实例的初始(抽象)声明节点;或者如果该声明不是其他声明的实例,则为NULL。对于下面的例子,在一个内联函数的一个嵌套声明中,该域指回该函数的定义(这段代码通不过编译,因为嵌套的f未定义)。
inline int f (void) { return 0; }

int main (void)

{

int f();

return f ();

}


2090 #define DECL_NAMESPACE_ALIAS(NODE) / in cp-tree.h
2091 DECL_ABSTRACT_ORIGIN (NAMESPACE_DECL_CHECK (NODE))
2092 #define ORIGINAL_NAMESPACE(NODE) /
2093 (DECL_NAMESPACE_ALIAS (NODE) ? DECL_NAMESPACE_ALIAS (NODE) : (NODE))

1407 #define DECL_ABSTRACT_ORIGIN(NODE) (DECL_CHECK (NODE)->decl.abstract_origin)

宏ORIGINAL_NAMESPACE涉及的是C++中名字空间别名的特性。根据【3】,名字空间别名有以下规则:
1. 一个名字空间别名定义,根据以下规则,为一个名字空间声明了一个替代的名字:
namespace-alias:
identifier
namespace-alias-definition:
namespace identifier = qualified-namespace-specifier ;
qualified-namespace-specifier:
::opt nested-name-specifieropt namespace-name
2. 在一个名字空间别名定义中的标识符是,由名字空间限定符所标记的,名字空间名的代名词,并成为一个名字空间别名。【注意:当在一个名字空间别名定义中查找一个namespace-name(注:包括名字空间名及别名)时,仅考虑名字空间的名字,参见3.4.6节】
3. 在一个声明域中,一个名字空间别名定义可以用于重新定义一个声明于这个声明域的名字空间别名,但仅限于引用该别名已经引用的名字空间。例如:以下声明都是合法的:
namespace Company_with_very_long_name { /* ... */ }
namespace CWVLN = Company_with_very_long_name;
namespace CWVLN = Company_with_very_long_name; // OK: duplicate
namespace CWVLN = CWVLN;
4. 一个名字空间名或别名不能与同一声明域内的其他实体同名。在全局域内定义的名字空间名不能与程序在全局域中的其他实体同名。由于声明在不同的编译单元而导致违反该规则,不要求编译器能对此作出诊断。
因此在上面的2950行,ORIGNINAL_NAMESPACE返回名字空间定义(而不是别名),随后的NAMESPACE_LEVEL返回的是一个cp_binding_level对象,也即是名字空间所对应的作用域对象。

1587 NAMESPACE_LEVEL #define NAMESPACE_LEVEL(NODE) / in cp-tree.h
1588 (DECL_LANG_SPECIFIC (NODE)->decl_flags.u.level)

1444 #define DECL_LANG_SPECIFIC(NODE) (DECL_CHECK (NODE)->decl.lang_specific)

函数cxx_scope_find_binding_for_name 则在scope指定的作用域查找name所指示的声明,并返回对应的cxx_binding实例。在当前编译器的实现中,该函数用于名字空间作用域中的查找。

1877 static inline cxx_binding *
1878 cxx_scope_find_binding_for_name (cxx_scope *scope, tree name) in name-lookup.c
1879 {
1880 cxx_binding *b = IDENTIFIER_NAMESPACE_BINDINGS (name);
1881 if (b)
1882 {
1883 /* Fold-in case where NAME is used only once. */
1884 if (scope == b->scope && b->previous == NULL)
1885 return b;
1886 return find_binding (scope, b);
1887 }
1888 return NULL;
1889 }

IDENTIFIER_NAMESPACE_BINDINGS访问标识符节点的namespace_bindings域,这个域记录的是该标识符在所有名字空间中的声明(注意,在C++中名字空间构成最外层的作用域,名字空间不能出现在类定义中,亦不能出现在函数定义里)。在这个标识符中另有bindings域,这个域从标识符的最内层作用域记起,并最终与namespace_bindings连接。

369 #define IDENTIFIER_NAMESPACE_BINDINGS(NODE) / in cp-tree.h
370 (LANG_IDENTIFIER_CAST (NODE)->namespace_bindings)

238 #define LANG_IDENTIFIER_CAST(NODE) /
339 ((struct lang_identifier*)IDENTIFIER_NODE_CHECK (NODE)) in cp-tree.h

我们已经知道标识符节点中的cxx_binding链表将同名的不同声明链接在一起,函数find_bindings从这个链表中找出声明于scope作用域中的对象的cxx_binding实例。

1863 static inline cxx_binding *
1864 find_binding (cxx_scope *scope, cxx_binding *binding) in name-lookup.c
1865 {
1866 timevar_push (TV_NAME_LOOKUP);
1867
1868 for (; binding != NULL; binding = binding->previous)
1869 if (binding->scope == scope)
1870 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, binding);
1871
1872 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, (cxx_binding *)0);
1873 }

名字空间别名不能用于定义名字空间,例如:
namespace A { .. }
namespace aliasA = A;
namespace aliasA { ... } // error: namespace alias “aliasA” not allowed here, assuming “A”
将触发3097行的错误信息。在3099行,编译器对此进行修正以期能捕捉进一步的错误。
而push_namespace根据查找的结果进行以下的操作。

push_namespace (continue)

3104 if (need_new)
3105 {
3106 /* Make a new namespace, binding the name to it. */
3107 d = build_lang_decl (NAMESPACE_DECL, name, void_type_node);
3108 DECL_CONTEXT (d) = FROB_CONTEXT (current_namespace);
3109 d = pushdecl (d);
3110 if (anon)
3111 {
3112 /* Clear DECL_NAME for the benefit of debugging back ends. */
3113 SET_DECL_ASSEMBLER_NAME (d, name);
3114 DECL_NAME (d) = NULL_TREE;
3115 }
3116 begin_scope (sk_namespace, d);
3117 }
3118 else
3119 resume_scope (NAMESPACE_LEVEL (d));
3120
3121 if (implicit_use)
3122 do_using_directive (d);
3123 /* Enter the name space. */
3124 current_namespace = d;
3125
3126 timevar_pop (TV_NAME_LOOKUP);
3127 }

若该名字空间已经在当前名字空间中存在(即3091行的d不为NULL),只需按如下方式,将其置为当前作用域即可(即1414行)。

1406 static void
1407 resume_scope (struct cp_binding_level* b) in name-lookup.c
1408 {
1409 /* Resuming binding levels is meant only for namespaces,
1410 and those cannot nest into classes. */
1411 my_friendly_assert(!class_binding_level, 386);
1412 /* Also, resuming a non-directly nested namespace is a no-no. */
1413 my_friendly_assert(b->level_chain == current_binding_level, 386);
1414 current_binding_level = b;
1415 if (ENABLE_SCOPE_CHECKING)
1416 {
1417 b->binding_depth = binding_depth;
1418 indent (binding_depth);
1419 cxx_scope_debug (b, input_location.line, "resume");
1420 is_class_level = 0;
1421 binding_depth++;
1422 }
1423 }

若不然,调用pushdecl将该名字空间对象加入当前作用域(注意,也是名字空间)。

566 tree
567 pushdecl (tree x) in name-lookup.c
568 {
569 tree t;
570 tree name;
571 int need_new_binding;
572
573 timevar_push (TV_NAME_LOOKUP);
574
575 need_new_binding = 1;
...
604 name = DECL_NAME (x);
605 if (name)
606 {
607 int different_binding_level = 0;
608
609 if (TREE_CODE (x) == FUNCTION_DECL || DECL_FUNCTION_TEMPLATE_P (x))
610 check_default_args (x);
611
612 if (TREE_CODE (name) == TEMPLATE_ID_EXPR)
613 name = TREE_OPERAND (name, 0);
614
615 /* In case this decl was explicitly namespace-qualified, look it
616 up in its namespace context. */
617 if (DECL_NAMESPACE_SCOPE_P (x) && namespace_bindings_p ())
618 t = namespace_binding (name, DECL_CONTEXT (x));
619 else
620 t = lookup_name_current_level (name);

617行的namespace_bindings_p找出当前作用域是否包含在名字空间中。而DECL_NAMESPACE_SCOPE_P如果非0,表明要加入的对象是名字空间声明。

1482 bool
1483 namespace_bindings_p (void) in name-lookup.c
1484 {
1485 struct cp_binding_level *b = innermost_nonclass_level ();
1486
1487 return b->kind == sk_namespace;
1488 }

1427 static cxx_scope *
1428 innermost_nonclass_level (void) in name-lookup.c
1429 {
1430 cxx_scope *b;
1431
1432 b = current_binding_level;
1433 while (b->kind == sk_class)
1434 b = b->level_chain;
1435
1436 return b;
1437 }

618行的DECL_CONTEXT (x) 指向包含x的上下文。如果DECL_CONTEXT是NULL,则表明其上下文是全局名字空间,这正是我们的场景。而且namespace_binding将返回的t为NULL,那么下面代码片段将被执行。

pushdecl (continue)

828 /* This name is new in its binding level.
829 Install the new declaration and return it. */
830 if (namespace_bindings_p ())
831 {
832 /* Install a global value. */
833
834 /* If the first global decl has external linkage,
835 warn if we later see static one. */
836 if (IDENTIFIER_GLOBAL_VALUE (name) == NULL_TREE && TREE_PUBLIC (x))
837 TREE_PUBLIC (name) = 1;
838
839 /* Bind the name for the entity. */
840 if (!(TREE_CODE (x) == TYPE_DECL && DECL_ARTIFICIAL (x)
841 && t != NULL_TREE)
842 && (TREE_CODE (x) == TYPE_DECL
843 || TREE_CODE (x) == VAR_DECL
844 || TREE_CODE (x) == ALIAS_DECL
845 || TREE_CODE (x) == NAMESPACE_DECL
846 || TREE_CODE (x) == CONST_DECL
847 || TREE_CODE (x) == TEMPLATE_DECL))
848 SET_IDENTIFIER_NAMESPACE_VALUE (name, x);
...
872 }
873 else
874 {
...
1003 }
1004
1005 if (TREE_CODE (x) == VAR_DECL)
1006 maybe_register_incomplete_var (x);
1007 }
1008
1009 if (need_new_binding)
1010 add_decl_to_level (x,
1011 DECL_NAMESPACE_SCOPE_P (x)
1012 ? NAMESPACE_LEVEL (CP_DECL_CONTEXT (x))
1013 : current_binding_level);
1014
1015 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, x);
1016 }

上面的IDENTIFIER_GLOBAL_VALUE及SET_IDENTIFIER_NAMESPACE_VALUE具有以下定义。而在一个IDENTIFIER_NODE中(即对应于name,而x对应于声明),如果TREE_PUBLIC非0,表示在内部作用域,这个名字的外部定义已经被看到,而且它能从该模块外访问。

264 #define IDENTIFIER_GLOBAL_VALUE(NODE) / in cp-tree.h
265 namespace_binding ((NODE), global_namespace)
266 #define SET_IDENTIFIER_NAMESPACE_VALUE(NODE, VAL) /
267 set_namespace_binding ((NODE), current_namespace, (VAL))

函数set_namespace_binding将视情况创建出cxx_binding节点,将声明和作用域绑定一起。其中参数val对应声明,name对应于标识符,scope对应于绑定的作用域。

2958 void
2959 set_namespace_binding (tree name, tree scope, tree val) in name-lookup.c
2960 {
2961 cxx_binding *b;
2962
2963 timevar_push (TV_NAME_LOOKUP);
2964 if (scope == NULL_TREE)
2965 scope = global_namespace;
2966 b = binding_for_name (NAMESPACE_LEVEL (scope), name);
2967 if (!b->value || TREE_CODE (val) == OVERLOAD || val == error_mark_node)
2968 b->value = val;
2969 else
2970 supplement_binding (b, val);
2971 timevar_pop (TV_NAME_LOOKUP);
2972 }

函数binding_for_name也只用于将声明与名字空间绑定。在下面,可以很清楚地看到,如果是新的声明,其cxx_binding实例将加入对应的标识符的namespace_bindings队列头。

1894 static cxx_binding *
1895 binding_for_name (cxx_scope *scope, tree name) in name-lookup.c
1896 {
1897 cxx_binding *result;
1898
1899 result = cxx_scope_find_binding_for_name (scope, name);
1900 if (result)
1901 return result;
1902 /* Not found, make a new one. */
1903 result = cxx_binding_make (NULL, NULL);
1904 result->previous = IDENTIFIER_NAMESPACE_BINDINGS (name);
1905 result->scope = scope;
1906 result->is_local = false;
1907 result->value_is_inherited = false;
1908 IDENTIFIER_NAMESPACE_BINDINGS (name) = result;
1909 return result;
1910 }

上面由binding_for_name返回的cxx_binding的value域是null,因为在下面的cxx_binding_make里,注意参数value及type都是null。在返回set_namespace_binding后,value域被设置为声明节点。

326 static cxx_binding *
327 cxx_binding_make (tree value, tree type) in name-lookup.c
328 {
329 cxx_binding *binding;
330 if (free_bindings)
331 {
332 binding = free_bindings;
333 free_bindings = binding->previous;
334 }
335 else
336 binding = ggc_alloc (sizeof (cxx_binding));
337
338 binding->value = value;
339 binding->type = type;
340 binding->previous = NULL;
341
342 return binding;
343 }

一般而言,在同一个域中出现多个同名的声明即意味着语法错误,不过【2】给出了一个例外:
一个类名(9.1)或枚举名(7.2)可以被同名的,声明在同一个域的,一个对象,函数,或枚举值隐藏。如果一个类或枚举名,及一个对象、函数、或枚举值在同一个域以相同名字声明(以任意次序),该类或枚举名,在对象、函数、或枚举值可见之处,被隐藏。
函数supplement_binding设计用来处理这个例外,后面我们再来看它。
在pushdecl的1009行,need_new_binding被设置为1。而在add_decl_to_level中,名字空间的声明将被链入对应cxx_scope实例的namespaces域,532行则对应vtable的情况,剩下的537行处理其他的非名字空间声明。

523 static void
524 add_decl_to_level (tree decl, cxx_scope *b) in name-lookup.c
525 {
526 if (TREE_CODE (decl) == NAMESPACE_DECL
527 && !DECL_NAMESPACE_ALIAS (decl))
528 {
529 TREE_CHAIN (decl) = b->namespaces;
530 b->namespaces = decl;
531 }
532 else if (TREE_CODE (decl) == VAR_DECL && DECL_VIRTUAL_P (decl))
533 {
534 TREE_CHAIN (decl) = b->vtables;
535 b->vtables = decl;
536 }
537 else
538 {
539 /* We build up the list in reverse order, and reverse it later if
540 necessary. */
541 TREE_CHAIN (decl) = b->names;
542 b->names = decl;
543 b->names_size++;
544
545 /* If appropriate, add decl to separate list of statics. We
546 include extern variables because they might turn out to be
547 static later. It's OK for this list to contain a few false
548 positives. */
549 if (b->kind == sk_namespace)
550 if ((TREE_CODE (decl) == VAR_DECL
551 && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
552 || (TREE_CODE (decl) == FUNCTION_DECL
553 && (!TREE_PUBLIC (decl) || DECL_DECLARED_INLINE_P (decl))))
554 VARRAY_PUSH_TREE (b->static_decls, decl);
555 }
556 }



图34:std及全局名字空间
到这里,上图显示了std与全局名字空间的部分关系。
4.3.1.7.4.1. 退回到全局名字空间
一旦std_identifie的NAMESPACE_DECL及关联的绑定上下文被创建,std_node被设为这个NAMESPACE_DECL,在上面的图中,从这个节点出发,很容易就能到达其包含的,及包含它的作用域。接着,就要通过pop_namespace回到全局名字空间。

3131 void
3132 pop_namespace (void) in name-lookup.c
3133 {
3134 my_friendly_assert (current_namespace != global_namespace, 20010801);
3135 current_namespace = CP_DECL_CONTEXT (current_namespace);
3136 /* The binding level is not popped, as it might be re-opened later. */
3137 leave_scope ();
3138 }

leave_scope将退到当前作用域的上一级作用域,并将其设为当前作用域。在1356行,class_binding_level指向当前有效的最近的类作用域。如果1356行的条件满足,则表明我们正在退出一个名字空间的定义,同时我们也在一个类定义中,这几乎就表示某错误(因为class_binding_level不为null会设置is_class_level,将在1370行给出错误信息)。这种情况下,编译器试图修改current_binding_level,不过在下面的1376行,current_binding_level再次被更改,1357行的修改没有什么意义。

1351 cxx_scope *
1352 leave_scope (void) in name-lookup.c
1353 {
1354 cxx_scope *scope = current_binding_level;
1355
1356 if (scope->kind == sk_namespace && class_binding_level)
1357 current_binding_level = class_binding_level;
1358
1359 /* We cannot leave a scope, if there are none left. */
1360 if (NAMESPACE_LEVEL (global_namespace))
1361 my_friendly_assert (!global_scope_p (scope), 20030527);
1362
1363 if (ENABLE_SCOPE_CHECKING)
1364 {
1365 indent (--binding_depth);
1366 cxx_scope_debug (scope, input_location.line, "leave");
1367 if (is_class_level != (scope == class_binding_level))
1368 {
1369 indent (binding_depth);
1370 verbatim ("XXX is_class_level != (current_scope == class_scope)/n");
1371 }
1372 is_class_level = 0;
1373 }
1374
1375 /* Move one nesting level up. */
1376 current_binding_level = scope->level_chain;
1377
1378 /* Namespace-scopes are left most probably temporarily, not completely;
1379 they can be reopen later, e.g. in namespace-extension or any name
1380 binding activity that requires us to resume a namespace. For other
1381 scopes, we just make the structure available for reuse. */
1382 if (scope->kind != sk_namespace)
1383 {
1384 scope->level_chain = free_binding_level;
1385 if (scope->kind == sk_class)
1386 scope->type_decls = NULL;
1387 else
1388 binding_table_free (scope->type_decls);
1389 my_friendly_assert (!ENABLE_SCOPE_CHECKING
1390 || scope->binding_depth == binding_depth,
1391 20030529);
1392 free_binding_level = scope;
1393 }
1394
1395 /* Find the innermost enclosing class scope, and reset
1396 CLASS_BINDING_LEVEL appropriately. */
1397 for (scope = current_binding_level;
1398 scope && scope->kind != sk_class;
1399 scope = scope->level_chain)
1400 ;
1401 class_binding_level = scope && scope->kind == sk_class ? scope : NULL;
1402
1403 return current_binding_level;
1404 }

注意1382行,如果作用域不是名字空间,这个对象将被释放给free_binding_level。但名字空间不如是。这是因为名字空间是最外层的作用域,退出后几乎可以肯定会还会进入该名字空间,又所有的名字空间被保留下来,其间的关系亦然,这大大加快了随后的对这些名字空间对象的查找。这也是为什么在标识符节点中设计了namespace_bindings这个指针。而不保留非名字空间作用域对象的原因可能有:1)这样的对象很多,尤其当我们设计了很多类、函数、及使用了很深的{}块;2)其中的大部分可能访问一次——对于函数及{}块确实如此。这样保持作用域树尽量小,减少了出错的可能,也减少了内存的使用。同时也使得重新进入这些非名字空间作用域的处理变得简单——重新加入作用域树就是了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: