5.2 在Tornado下的C++开发
基本的C++支持被捆绑在Tornado开发环境里。VxWorks提供了包含对所有程序的C++安全声明的头文件和必须的run-time support.标准的Tornado交互式开发工具如调试器(debugger),shell,和新增的加载器(loader)都包含了对C++的支持。
5.2.1 工具支持
WindSh
Tornado支持C和C++开发语言.WindSh能够解释简单的C++表达式。为了练习在C表达式解释程序(C-expression interpreter)中缺少的C++功能,你可以编译并下载按专门的C++语法封装的程序。参见《Tornado User's Guide:Tornado Tools Reference》或者WindSh C++的在线参考。
Demangling
当C++函数被编译时,类的成员数(如果有)和函数参数的类型、个数被封装到函数的连接程序名(linkage name)中。这成为name mangling或者mangling。在WindSh中的调试和系统信息能够按demangled或者mangled显示的方式打印出C++函数名。
缺省的显示是gnu.另外arm和none(no demangling)是可选项。为了选择另外的模式,改变Tcl变量shDemangleStyle.
举例:
-> ?set shDemangleStyle none
重载的函数名
当你调用一个重载函数时,Windsh打印出匹配的函数的符号并提示你输入想要得函数。更多的关于WindSh如何处理重载函数名,包括例子的信息参见《Tornado User's Guide: Shell》
调试器
Tornado调试器能够调试C++成员函数,包括在构造函数和模板中的单步调试。关于细节,参见《Tornado User'sGuide: Tornado Tools Reference and Debugging with GDB》.
5.2.2 编程问题(Programming Issues)
使C++入口点在C代码中可用
如果想从C代码中引用一个(非重载的,全局的)C++对象(symbols),则必须通过使用extern "C"进行原型说明使得C连接程序知道它:
#ifdef __cplusplus
extern "C" void myEntryPoint();
#else
void myEntryPoint();
#endif
也可使用这种语法来实现在C++代码中调用C对象(symbols)。VxWorks中的C对象在C++中是可被调用的,因为VxWorks头文件对其声明使用了这种机制。
5.2.3 编译C++应用程序
Tornado项目工具(project tool)完全支持C++.配置和编译C++程序推荐使用项目工具。下面的信息对于理解C++环境是有用的。除非有特殊原因而使用人工方法,否则应该使用在《Tornado User's Guide: Projects》中说明的方法。
关于GNU编译器和相关工具的细节,参见《GNU ToolKit User's Guide》。
当使用GNU编译器编译C++模块时,在每个带有C++后缀(如.cpp)的源文件上调用ccarch(正像对C源码).在VxWorks环境中编译C++程序包括如下步骤:
1.每个C++源码文件被编译成目标代码,就像C程序一样。举例编译一个68K目标:
cc68k -fno-builtin -I$WIND_BASE/target/h -nostdinc -O2 \
-DCPU=MC68040 -c foo.cpp
cc68k -fno-builtin -I$WIND_BASE/target/h -nostdinc -O2 \
-DCPU=MC68040 -c bar.cpp
2.目标代码被munch(参见Munching C++ Application Modules).在我们的例子中:
nm68k foo.o bar.o | wtxtcl $WIND_BASE/host/src/hutils/munch.tcl \
-asm 68k > ctdt.c
cc68k -c ctdt.c
3.目标代码和已编译的munch输出连接在一起。(对于可下载的应用程序,它们可使用参数-r局部的(partially)连接在一起;对于可启动的应用程序,它们可静态的与VxWorks BSP连接在一起。)如果使用GNU工具,如下可从编译器驱动中调用连接器:
cc68k -r ctdt.o foo.o bar.o -o linkedObjs.o
这里连接两个目标模块,foo.o和bar.o,生成可下载的目标代码,linkedObjs.o。如果使用选项-frepo,则使用ccarch而不是ldarch来实例化模板。(参见5.2.7 Template Instantiation)
注意:如果使用Wind River System makefile来构造应用程序,munching是通过make来执行。
小心:在连接步骤中,-r用来制定局部连接。一个局部的连接文件仍然是可重定位的,也适合使用VxWorks模块加载器来下载和连接。《GNU ToolKit User's Guide: Using ld》中描述的选项-Ur是为了解析涉及到的C++构造函数。这个选项用于本地开发(native development),而不是交叉开发(cross-development).VxWorks的C++模块不使用选项-Ur。
5.2.4 配置常量
通常VxWorks内核包括C++ run-time, 基本Iostream和对STL的支持.通过包含如下的宏来增/删C++组成:
INCLUDE_CPLUS
包括所有的基本C++ run-time support.这就可以下载和运行已编译的,munched C++模块.这不会配置任何Wind基类库进入VxWorks.
INCLUDE_CPLUS_STL
包括对STL的支持.
INCLUDE_CPLUS_STRING
包括字符串类型库的基本组件.
INCLUDE_CPLUS_IOSTREAMS
包括Iostream库的基本组件.
INCLUDE_CPLUS_COMPLEX
包括复数(complex)类型库的基本组件.
INCLUDE_CPLUS_IOSTREAMS_FULL
包括完全的Iostream库;这意味着也定义了INCLUDE_CPLUS_IOSTREAMS.
INCLUDE_CPLUS_STRING_IO
包括字符串的输入/输出函数;这意味着也定义了INCLUDE_CPLUS_STRING和INCLUDE_CPLUS_IOSTREAMS.
INCLUDE_CPLUS_COMPLEX_IO
包括对复数对象的输入/输出;这意味着也定义了INCLUDE_CPLUS_IOSTREAMS和INCLUDE_CPLUS_COMPLEX.
为了包括一个或多个Wind基类,包括一个或多个如下的常量:
INCLUDE_CPLUS_VXW
包括VxWorks Wrapper类库.
INCLUDE_CPLUS_TOOLS
包括Rogue Wave的Tools.h++类库.
关于配置VxWorks的更多信息,参见《Tornado User's Guide: Projects》.
5.2.5 Munching C++应用程序模块
用C++写的模块在下载到VxWorks目标机之前必须经历额外的主机进程步骤。这个额外的步骤(称作munching)初始化静态对象并保证当该模块被下载到VxWorks时,C++的run-time support对所有的静态对象能按照恰当的顺序调用正确构造函数和析构函数。
如下的命令编译hello.cpp,然后munch hello.o,结果在已munch的文件hello.out,它可通过Tornado模块加载器加载:
cc68k -IinstallDir/target/h -DCPU=MC68020 -nostdinc -fno-builtin \
-c hello.cpp
nm68k hello.o | wtxtcl installDir/host/src/hutils/munch.tcl \
-asm 68k > ctdt.c
cc68k -c ctdt.c
ld68k -r -o hello.out hello.o ctdt.o
注意:可用实际的路径名或者$WIND_BASE(UNIX)或%WIND_BASE%(Windows)来替换installDir。
小心:《GNU ToolKit User's Guide: Using ld》描述选项-Ur来解析涉及到的C++构造函数. 这个选项用于本地开发 (native development),而不是交叉开发(cross-development).VxWorks的C++模块不使用选项-Ur。
5.2.6 静态构造函数和析构函数
在munching,下载和连接后,必须调用静态构造函数和析构函数。
调用静态构造函数和析构函数的方法
VxWorks提供了两种方法来调用静态构造函数和析构函数:[这两种方法是互补的(interactively)]
自动:调用静态构造函数认为是下载的side effect;调用静态的析构函数认为是卸载的side effect。
人工:通过调用cplusCtors()和cplusDtors()来调用静态构造函数和析构函数。
调用cplusXtorSet()来改变方法;在windsh参考的条目中查看它的细节。调用cplusStratShow()来显示当前方法.
在自动方法下,它是缺省的,静态构造函数在成功下载后被立即调用。如果在模块下载前设置为自动方法,模块的静态构造函数在模块装载器返回到它的调用者之前就被调用。在自动方法下,模块卸载器在实际卸载模块前调用模块的静态析构函数。
人工方法引起调用静态构造函数是作为调用cplusCtors()的结果.在windsh参考中可找到关于函数cplusCtors()和cplusDtors()的更多细节。为了调用当前加载的静态构造函数和析构函数,使用人工模式时不带参数。人工模式也能在模块上逐一调用静态构造函数和析构函数。
在系统开始和结束时构造函数和析构函数
当你创建可启动的VxWorks应用程序时,在系统初始化期间调用静态构造函数。更改在usrConfig.c中的函数usrRoot()使它包括对cplusCtorsLink()的调用.这就调用了所有的已连接到系统的静态构造函数。
为了修改usrConfig.c来调用cplusCtorsLink(),查找C++初始化部分:
#ifdef INCLUDE_CPLUS /* C++ product */
cplusLibInit ();
#endif
#ifdef INCLUDE_CPLUS_MIN /* C++ product */
cplusLibMinInit ();
#endif
接下来,增加cplusCtorsLink()到一个或两个部分,这取决于系统需求.在如下的例子中,仅当配置了最小C++时才调用cplusCtorsLink():
#ifdef INCLUDE_CPLUS_MIN /* C++ product */
cplusLibMinInit ();
cplusCtorsLink ();
#endif
注意:静态对象不会被初始化除非调用了cplusCtorsLink().因而,如果应用程序在usrRoot()中使用了静态对象,那么应该在使用它们之前调用cplusCtorsLink()。
为了使得cplusCtorsLink()正确运行,必须在完全连接的VxWorks映像上而不是单个的模块上执行munch操作。
相应地函数cplusDtorsLink()用来调用所有的静态析构函数。这个函数在需要依序卸载进程的系统中是有用的。
在代码中,在适合调用所有静态析构函数的地方包含对cplusDtorsLink()的调用,这些静态析构函数是最初连接到系统中的。
函数cplusCtorsLink()和cplusDtorsLink()不会调用在系统初始化完后下载的模块的静态构造函数和析构函数。如果系统使用了模块下载器,以下的过程在<调用静态构造函数和析构函数的方法>中描述。
5.2.7 模板实例化
C++工具箱(toolchain)支持三种不同的模板实例化方法。最简单的(这也是在VxWorks的makefiles里缺省使用的方法)是隐式实例化(imlicit instantiation).在这种方法下,每个模板的代码在每个需要它的模块中展开。为了使之工作,模板的实现部分必须在每个使用它的模块中可见。这通常通过在头文件中包含模板功能实现部分以及它们的
声明来实现。隐式实例化的缺点是它会导致代码复制和应用程序过大。
第二种方法是使用在例5-1中的语法来显示实例化所需要的任何模板。在这种方法下,编译时应使用选项-fno-implicit-templates。这种设计允许你对在何处实例化模板有最大的控制,并避免代码膨胀。
-frepo
手动实例化模板的方法把隐式实例化的简单和获得的更小的覆盖面(footprint)结合在一起。通过操作每个模块的模板实例数据库来工作。
编译器将产生扩展名为.rpo的文件;这些文件列举了所有的模板实例,这些模板实例在那儿能够被实例化的相应的目标文件中使用。编译器连接wrapper collect2,然后更新.rpo文件告诉编译器何处安放这些实例并重新构造任何受到影响的目标文件。在同一个文件中当编译器继续安置实例时,连接时的开销(link-time overhead)在第一次通过时是可忽略的。
过程(procedure)
模板头文件必须包含模板实现部分。通常如果模板实现部分保存在.cpp文件中,#include theTemplate.cpp必须添加到模板头文件中。
带有选项=frepo的完全构造必须创建.rpo文件,该文件告诉编译器哪个模板实例化。应该由ccarch而不是ldarch来启动连接步骤。
随后单独模块如平常一样被编译(但是带有选项-frepo没有其他模板参数)。
当有新的模板实例时,项目的相关部分必须重新构建来更新.rpo文件。
加载顺序
Tornado工具的动态连接能力要求模块在引用被下载的对象(symbol)前包含对该对象的定义。举个例子,在下面的例子中,应该在下载PairB.o前下载PairA.o。(也应该预先连接它们和下载已连接的目标)
例子
这个例子使用标准的VxWorks BSP makefile(为了具体化,假定一个68K目标).
Example 5-1: Sample Makefile
make PairA.o PairB.o ADDED_C++FLAGS=-frepo
/* dummy link step to instantiate templates */
cc68k -r -o Pair PairA.o PairB.o
/* In this case the template Pair<int>::Sum(void)
* will be instantiated in PairA.o.
*/
//Pair.h
template <class T> class Pair
{
public:
Pair (T _x, T _y);
T Sum ();
protected:
T x, y;
};
template <class T>
Pair<T>:
air (T _x, T _y) : x (_x), y(_y)
{
}
template <class T>
T Pair<T>::Sum ()
{
return x + y;
}
// PairA.cpp
#include "Pair.h"
int Add (int x, int y)
{
Pair <int> Two (x, y);
return Two.Sum ();
}
// PairB.cpp
#include "Pair.h"
int Double (int x)
{
Pair <int> Two (x, x);
return Two.Sum ();
}