5.13.4.7.1.
为函数调用产生代码
5.13.4.7.1.1.
构建获取函数地址的表达式
当我们写出类似:
f (a, b);
的代码来调用
f
,编译器需要努力使得这个调用以期望的方式可行。下面的函数泄露了编译器的所作所为。
2420
tree
2421
build_function_call
(tree function, tree
params)
in typeck.c
2422
{
2423
tree fntype, fndecl;
2424
tree coerced_params;
2425
tree result;
2426
tree name = NULL_TREE, assembler_name =
NULL_TREE;
2427
int is_method;
2428
tree original = function;
2429
2430
/* build_c_cast
puts on a NOP_EXPR to make the result not an lvalue.
2431
Strip such NOP_EXPRs, since
FUNCTION is used in non-lvalue context.
*/
2432
if (TREE_CODE (function) == NOP_EXPR
2433
&& TREE_TYPE (function) ==
TREE_TYPE (TREE_OPERAND (function, 0)))
2434
function = TREE_OPERAND (function, 0);
2435
2436
if (TREE_CODE (function) == FUNCTION_DECL)
2437
{
2438
name = DECL_NAME (function);
2439
assembler_name = DECL_ASSEMBLER_NAME
(function);
2440
2441
mark_used
(function);
2442
fndecl = function;
2443
2444
/* Convert
anything with function type to a pointer-to-function.
*/
2445
if (pedantic
&& DECL_MAIN_P (function))
2446
pedwarn ("ISO C++ forbids calling
`::main' from within program");
2447
2448
/* Differs from
default_conversion by not setting TREE_ADDRESSABLE
2449
(because calling an inline
function does not mean the function
2450
needs to be separately
compiled).
*/
2451
2452
if (DECL_INLINE (function))
2453
function = inline_conversion
(function);
2454
else
2455
function = build_addr_func
(function);
2456
}
2457
else
2458
{
2459
fndecl = NULL_TREE;
2460
2461
function = build_addr_func
(function);
2462
}
在调用点内联函数被展开。因此它不能在该编译单元之外分开编译,也不能把它标记为
TREE_ADDRESSABLE
。在这里
ADDR_EXPR
作为这个调用语句的结果被产生出来。
1464
tree
1465
inline_conversion
(tree exp)
in typeck.c
1466
{
1467
if (TREE_CODE (exp) == FUNCTION_DECL)
1468
exp = build1
(ADDR_EXPR, build_pointer_type
(TREE_TYPE
(exp)), exp);
1469
1470
return
exp;
1471
}
当通过指针来访问内联函数(以形式
x.*p
,其中
x
是可取址的,因而
build_address
(它要求可取址的对象作为参数)可以工作),访问性检查是要求的;而对于调用普通函数,需要
decay_conversion
来进行函数到指针的转换。
178
tree
179
build_addr_func
(tree function)
in
tree.c
180
{
181
tree type = TREE_TYPE (function);
182
183
/* We have to do
these by hand to avoid real pointer to member
184
functions.
*/
185
if (TREE_CODE (type) == METHOD_TYPE)
186
{
187
if (TREE_CODE (function) == OFFSET_REF)
188
{
189
tree object = build_address
(TREE_OPERAND (function, 0));
190
return
get_member_function_from_ptrfunc
(&object,
191
TREE_OPERAND (function, 1));
192
}
193
function = build_address
(function);
194
}
195
else
196
function = decay_conversion (function);
197
198
return
function;
199
}
上面的
OFFSET_REF
被用在两种情形中:
1.
一个具有形式‘
A::m
’的表达式,其中‘
A
’是一个类,而‘
m
’是一个非静态成员。在这个情况下,操作数
0
将是一个
TYPE
(对应‘
A
’),而操作数
1
将是一个
FIELD_DECL
,
BASELINK
,或
TEMPLATE_ID_EXPR
(对应‘
m
’)。
如果其地址被获取了,这个表达式就是一个指向成员的指针,但如果其地址没有被获取,则仅表示该对象的一个成员。
这个形式仅被用在解析阶段;一旦语法分析完成,该形式就要被消除。
2.
一个具有形式‘
x.*p
’的表达式。在这个情况下,操作数
0
将是一个对应‘
x
’的表达式,而操作数
1
将是一个具有指向成员的指针类型的表达式。
显然,类方法的指针遵守第二个规则。对于非静态方法的调用,记得还有一个隐含的“
const this*
”,上面的
189
行,
object
就是这个
this
指针。另外在
C++
中,有一个奇特的类型
–
方法类型【
14
】,例如:“
typedef void (C::*Cf) () =
&C::f;
”
Cf
是
C
(假定
C
是一个类)的一个方法类型,它没有参数,并返回
void
。
2324
行的谓词
TYPE_PTRMEMFUNC_P
对这样的类型返回
true
。在
2327
行的
TYPE_PTRMEMFUNC_FN_TYPE
获取关联的方法类型。
2318
tree
2319
get_member_function_from_ptrfunc
(tree
*instance_ptrptr, tree function)
in typeck.c
2320
{
2321
if (TREE_CODE (function) == OFFSET_REF)
2322
function = TREE_OPERAND (function, 1);
2323
2324
if (TYPE_PTRMEMFUNC_P (TREE_TYPE (function)))
2325
{
2326
tree idx, delta, e1, e2, e3, vtbl, basetype;
2327
tree fntype = TYPE_PTRMEMFUNC_FN_TYPE
(TREE_TYPE (function));
2328
2329
tree instance_ptr = *instance_ptrptr;
2330
tree instance_save_expr = 0;
2331
if (instance_ptr == error_mark_node)
2332
{
2333
if (TREE_CODE (function) == PTRMEM_CST)
2334
{
2335
/* Extracting the function address from a pmf
is only
2336
allowed with -Wno-pmf-conversions. It
only works for
2337
pmf constants.
*/
2338
e1 = build_addr_func
(PTRMEM_CST_MEMBER
(function));
2339
e1 = convert (fntype, e1);
2340
return
e1;
2341
}
2342
else
2343
{
2344
error ("object missing in use of
`%E'", function);
2345
return
error_mark_node;
2346
}
2347
}
2348
2349
if (TREE_SIDE_EFFECTS (instance_ptr))
2350
instance_ptr = instance_save_expr =
save_expr
(instance_ptr);
2351
2352
if (TREE_SIDE_EFFECTS (function))
2353
function = save_expr (function);
2354
2355
/* Start by
extracting all the information from the PMF itself.
*/
2356
e3 = PFN_FROM_PTRMEMFUNC
(function);
如果
instance_ptr
是
error_mark_node
,在这里
function
应该是一个
PTRMEM_CST
(成员指针常量的节点),其中的
PTRMEM_CST_CLASS
是
C
的
RECORD_TYPE
,而
PTRMEM_CST_MEMBER
是对应
f
的
*_DECL
(不过我想不出一个会产生这样节点的正常情形,因为上面的
build_address
(
189
行),对于
instance_ptr
,总是成功的,除非传入的节点是
error_mark_node
,这意味着在这个过程中某些东西出错了。即便对于把方法指针转换到函数指针的情形,似乎也不应该到这里来,其细节可以在【
6
】中看到)。
在 注意到你仍将为通过一个函数指针进行的函数调用付出代价;在绝大多数架构上,这样的一个调用会挫败 这个扩展的语法是 extern extern typedef fptr p = 对于 fptr p1 = 你必须指定‘ |
而如果
instance_ptr
不是
error_mark_node
,在
2356
行的
function
是具有成员指针类型的表达式,而
PFN_FROM_PTRMEMFUNC
具有如下定义。
2563
#define
PFN_FROM_PTRMEMFUNC
(NODE)
pfn_from_ptrmemfunc
((NODE))
5637
tree
5638
pfn_from_ptrmemfunc
(tree t)
in typeck.c
5639
{
5640
if (TREE_CODE (t) == PTRMEM_CST)
5641
{
5642
tree delta;
5643
tree pfn;
5644
5645
expand_ptrmemfunc_cst
(t, &delta, &pfn);
5646
if (pfn)
5647
return
pfn;
5648
}
5649
5650
return
build_ptrmemfunc_access_expr
(t, pfn_identifier
);
5651
}
上面的函数获取了由这个成员指针类型的表达式或
PTRMEM_CST
结构所引用的方法,并且它需要调整
this
指针,或适当地操控
vtable
。
首先,如果上面的实参
function
具有形如:“
&C::f
”的形式,它是应该
PTRMEM_CST
节点。不过在构建函数调用的上下文中,
function
不会是
PTRMEM_CST
;而
function
是
PTRMEM_CST
的一个场景是形如:“
void (B::*pfn)() = &B::f;
”的语句,然后
expand_ptrmemfunc_cst
将被
build_ptrmemfunc
调用来从“
&B::f
”的
PTRMEM_CST
中,得到用于方法指针类型“
void (B::*)()
”的结构(见下面)的
__pfn
及
__delta
。
不管怎么说,这值得我们花一些时间看一下怎样为方法指针确定
__pfn
及
__delta
域。下面在
5587
行的
TYPE_PTRMEMFUNC_OBJECT_TYPE
为形如“
int (A::*)(double)
”的类型返回‘
A
’。
5574
void
5575
expand_ptrmemfunc_cst
(tree cst, tree
*delta, tree *pfn)
in
typeck.c
5576
{
5577
tree type = TREE_TYPE (cst);
5578
tree fn = PTRMEM_CST_MEMBER (cst);
5579
tree ptr_class, fn_class;
5580
5581
my_friendly_assert (TREE_CODE (fn) ==
FUNCTION_DECL, 0);
5582
5583
/* The class that
the function belongs to.
*/
5584
fn_class = DECL_CONTEXT (fn);
5585
5586
/* The class that
we're creating a pointer to member of.
*/
5587
ptr_class = TYPE_PTRMEMFUNC_OBJECT_TYPE
(type);
5588
5589
/* First, calculate
the adjustment to the function's class.
*/
5590
*delta = get_delta_difference
(fn_class, ptr_class, /*force=*/
0);
考虑下面的例子:
class
A { public
:
void f (); };
class
B: public
A {};
void (B::*pfn)
() = &B::f;
对于这个例子,
fn_class
将是
A
(来自“
&B::f
”部分),
ptr_class
将是
B
(来自“
vod (B::*pfn) ()
”部分),而
fn
将是
f
。因此,首先需要知道派生类与定义该函数的基类间的调整量。在下面的函数中,参数
force
如果是
false
,表示不允许反向的转换(从
to
到
from
)。
5392
static
tree
5393
get_delta_difference
(tree from, tree
to, int force)
in
typeck.c
5394
{
5395
tree binfo;
5396
tree virt_binfo;
5397
base_kind kind;
5398
5399
binfo = lookup_base
(to, from, ba_check, &kind);
5400
if (kind == bk_inaccessible || kind ==
bk_ambig)
5401
{
5402
error ("
in pointer to member function
conversion");
5403
goto
error;
5404
}
5405
if (!binfo)
5406
{
5407
if (!force)
5408
{
5409
error_not_base_type (from, to);
5410
error ("
in pointer to member conversion");
5411
goto
error;
5412
}
5413
binfo = lookup_base
(from, to, ba_check, &kind);
5414
if (!binfo)
5415
goto
error;
5416
virt_binfo = binfo_from_vbase
(binfo);
5417
if (virt_binfo)
5418
{
5419
/* This is a
reinterpret cast, we choose to do nothing.
*/
5420
warning ("pointer to member cast via
virtual base `%T'",
5421
BINFO_TYPE (virt_binfo));
5422
goto
error;
5423
}
5424
return
fold
(convert_to_integer (ptrdiff_type_node
,
5425
size_diffop
(size_zero_node,
5426
BINFO_OFFSET (binfo))));
5427
}
5428
5429
virt_binfo =
binfo_from_vbase
(binfo);
5430
if (!virt_binfo)
5431
return
fold
(convert_to_integer (ptrdiff_type_node
, BINFO_OFFSET (binfo)));
5432
5433
/* This is a
reinterpret cast, we choose to do nothing.
*/
5434
if (force)
5435
warning ("pointer to member cast via
virtual base `%T'",
5436
BINFO_TYPE (virt_binfo));
5437
else
5438
error ("pointer to member conversion
via virtual base `%T'",
5439
BINFO_TYPE (virt_binfo));
5440
5441
error:
5442
return
fold
(convert_to_integer(ptrdiff_type_node
, integer_zero_node));
5443
}
注意到参数
from
及
to
必须具有继承关系。另外,如果转换涉及虚拟基类,这是仅可以被
reinterpret_cast
处理的语法,在这里应该被忽略。并且内部对象
ptrdiff_type_node
对应于我们经常在
C/C++
程序中看到的
ptrdiff_t
。考虑这个例子:
class
base { public
: void fb() {} virtual
~base() {} };
class
A : public
base {};
class
B1 : virtual
public
A {};
class
B2 : virtual
public
A {};
class
C: public
B1, public
B2 {};
int main() {
C c;
void (C::*pfn) () =
&base::fb;
(c.*pfn)();
return
0;
}
语句
“
PFN pfn = &base::fb;
”
导致错误
“
error: pointer to member
conversion via virtual base ‘A’
”
。但是如果
PFN