关闭

[转帖] 在Tornado下的C++开发

标签: c++iostreamwrapperinclude编译器instantiation
1864人阅读 评论(0) 收藏 举报


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 (); 
}

TOP

原创]5.3 C++语言和库支持
5.3 C++语言和库支持
在这节中描述了VxWorks C++执行的某些特殊方面。要学习更多的关于C++语言和标准库请参考任何C++标准(推荐《The C++ Programming Language, Third Edition》)。

5.3.1 语言特征
我们支持许多但并不是所有的被ANSI认可的C++标准的新特性。Tornado 2.0支持异常处理和运行时类型信息,也就是增强了的模板支持。我们不支持名字空间特性,尽管编译器将接受(和忽略)对std名字空间的引用。

异常处理
缺省地我们的C++编译器支持多线程安全的异常处理。为了取消异常处理,使用编译器标志-fno-exceptions。

使用异常
你或许会写代码来处理C++模块的异常。举个例子,对new的调用会检查返回的指针是否是失败值0。如果你担心在这个版本中增加的异常处理将不能正确编译你的代码,你应该坚持下面的简单规则:
.使用new(不抛出异常)
.在你的Iostream对象中不要直接地(explicitly)打开异常。
.不要使用字符串对象或者把它们封装到块"try {} catch (...) {}" 中。

这些规则来源于下面的信息:
.GNU Iostream不会抛出异常除非在构建库时定义了IO_THROW并且对使用中的特定的Iostream对象打开了异常。缺省是没有异常。异常必须明确地开启需要抛出的每个输入输出状态标志。
.对类basic_string的某些方法,STL不会抛出异常。

异常处理的开销
为了支持对在堆栈展开过程中产生的自动对象的消除,编译器必须把一段固定的代码连同析构函数插入到创建自动(基于堆栈)对象的函数中。
下面的是在PowerPC 604目标(BSP mv2604)上测量的异常处理开销;是按执行的指令数来计数。1235条指令用来执行"throw 1"和有关联的"catch (...)".这里的14条额外指令同析构函数一起注册/注销自动变量和临时对象;29条额外指令在非内联函数中使用;最后,在遇到第一个异常处理结构(construct)(try, catch, throw或注册的自动变量或临时对象),执行947条额外指令。
first time normal case 
void test() { // 3+29 3+29 
throw 1; // 1235 1235 total time to printf 


void doit() { // 3+29+947 3+29 
try { // 22 22 
test(); // 1 1 
} catch (...) { 
printf("Hi\n"); 



struct A { ~A( ) { } }; 

void local_<I>var</I> ( ) { // 3+29 
A a; // 14 
} // 4
-fno-exceptions能够用来关掉异常处理。这样做将减少返回到传统(classical)C++的开销。

未处理的异常
按标准要求,一个没有捕获的异常最后将导致调用terminate().这个函数缺省的行为是挂起任务和把警告消息发到控制台。你也可以通过调用set_terminate()(定义在头文件exception中)安装自己的终端处理。

运行时类型信息(RTTI)
这个特性缺省是开启的,它只增加很小的开销到任何包含带有虚拟函数的类的C++程序。如果你不需要这个特性,可以使用-fno-rtti关掉它。

5.3.2 标准模板库(STL)
STL由小的运行时组件(通过选择INCLUDE_CPLUS_STL将其配置到你的内核中)和头文件集合组成。
STL行为(port)在类的级别上VxWorks线程安全的。这意味着如果两个任务想要使用同样的容器对象(container object),客户端不得不提供明确的锁定(locking).(例如,使用旗语(semaphores)就需要这样做,细节参见2.4.3 Semaphores)然而两个同为STL容器类的不同对象可同时存取。

Iostream库
通过选择包含在项目工具VxWorks视图里的INCLUDE_CPLUS_IOSTREAMS把这个库配置到VxWorks中;参见5.2.4配置常量。
Iostream库头文件在VxWorks头文件目录中,<安装路径>/target/h。为了使用这个库,在应用程序的相应模块里,在头文件vxWorks.h后包括一个或多个头文件。最频繁使用的头文件是iostream.h,但是其他的头文件也会用到,具体信息参看C++参考例如Stroustrup。
标准的Iostream对象(cin, cout, cerr和clog)是全局的,那就是说,对于任何给定的任务他们不是私有的。它们被正确初始化而不管任务或模块引用它们的数目,它们也可安全的交叉使用在多任务中,尽管这些任务有着stdin,stdout和stderr的相同定义。然而当不同的任务有不同的标准输入输出文件描述符时使用它们是不安全的;在这种情况下,对于互斥的处理在于应用程序。
通过创建新的同一个类的Iostream对象做为标准的Iostream对象来模仿私有标准Iostream对象的作用(例如,cin是一个istream_withassign),并分配给它一个新的filebuf对象,该对象依赖合适的文件描述符。这个新的filebuf和Iostream对象对调用它的任务是私有的,并确保没有其它任务能够偶然地误用它们。
ostream my_out (new filebuf (1)); /* 1 == STDOUT */ 
istream my_in (new filebuf (0), &my_out); /* 0 == STDIN; 
* 依赖 my_out */
关于Iostreams库的完全细节,参见在线手册《The GNU C++ Iostream Library》。

字符串和复数类
这些类是新的标准C++库的一部分。通过选择包含在项目工具VxWorks视图里的INCLUDE_CPLUS_STRING和INCLUDE_CPLUS_COMPLEX可以把它们配置到内核中。你可以通过选择INCLUDE_CPLUS_STRING_IO和INCLUDE_CPLUS_COMPLEX_IO包括这些类的I/O功能。

注意:Tornado 2.0 C++没有包括对多字节字符串的支持.Wind基类中tools.h++的一部分包含这样的类.


 

5.5 Wind基类
Wind基类包括两个库:
.VxWorks Wrapper类库
.来自Rogue Wave Software的Tools.h++库
VxWorks Wrapper类库提供了瘦(thin)C++接口给几个标准VxWorks模块。来自Rogue Wave Software的Tools.h++基类库支持各种C++功能。

注意:为了防止VxWorks库和Rogue Wave库的从属冲突(dependency conflicts),所有的VxWorks库,包括VxWorks Wrapper类库,应该在所有包括Tools.h++库的Rogue Wave库前被包括。

5.5.1 VxWorks Wrapper类库
在这个库中的类被称为wrapper类因为对一部分标准的VxWorks函数,每个类封装或隐藏了(encapsulates, or wraps)接口。选择包含在项目工具VxWorks视图里的INCLUDE_CPLUS_VXW把这个库配置到VxWorks中,参见5.2.4配置常量。
VxWorks Wrapper类库头文件在标准的VxWorks头文件目录中,<安装路径>/target/h。这些类和它们相应的头文件在表5-1中列出。为了使用这些类,在应用程序的适当模块中包含相应头文件。

表5-1
--------------------------------------------------------------------------------------
头文件 描述
--------------------------------------------------------------------------------------
vxwLoadLib.h 对象模块的加载器和卸载器(wraps loadLib, unldLib, moduleLib) 
vxwLstLib.h 连接的链表(wraps lstLib) 
vxwMemPartLib.h 内存分配(wraps memLib) 
vxwMsgQLib.h 消息队列(wraps msgQLib) 
vxwRngLib.h 环形缓冲区(wraps rngLib) 
vxwSemLib.h 旗语(wraps semLib) 
vxwSmLib.h 共享内存对象(增加对共享内存旗语,消息队列,内存分配) 
vxwSymLib.h Symbol表(wraps symLib) 
vxwTaskLib.h 任务(wraps taskLib, envLib, errnoLib, sigLib, and task<I>var</I>Lib) 
vxwWdLib.h Watchdog定时器(wraps wdLib) 
--------------------------------------------------------------------------------------
VxWorks Wrapper类设计为提供对面向对象的VxWorks模块的C++语言绑定,但是在使用前就进行了C绑定.图5-1显示了所有的VxWorks Wrapper类的继承关系。这些类的命名与VxWorks函数一致。例如,VXWMsgQ是消息队列类,提供msgQLib的C++接口。

注意:类VXWError和VXWIdObject是VxWorks Wrapper类内部使用的,把它们列在图5-1仅仅为了完整。这两个类并不会在应用程序中直接使用。

图5-1:Wrapper类的继承

例5-3:Watchdog定时器
为了举例说明wrapper类提供的对VxWorks模块的C++语言绑定,下面的例子用watchdog定时器类VXWWd来说明这个方法.关于Watchdog定时器的一般信息参见2.6 Watchdog定时器。
/* Create a watchdog timer and set it to go off in 3 seconds. */ 

/* includes */ 

#include "vxWorks.h" 
#include "logLib.h" 
#include "vxwWdLib.h" 

/* defines */ 

#define SECONDS (3)

task (void) 

/* Create watchdog */
[1] VXWWd myWatchDog; 

/* Set timer to go off in SECONDS - printing a message to stdout */ 

[2] if (myWatchDog.start (sysClkRateGet( ) * SECONDS, logMsg, 
int ("Watchdog timer just expired\n")) == ERROR) 
return (ERROR); 

while (TIMER_NEEDED) 

/* ... */ 

[3] } 
值得注意的一个不同于C接口是wrapper类允许把watchdog定时器作为对象操作而不是通过对象ID来操作。[1]行创建和命名一个watchdog定时器:C++自动调用VXWWd构造函数,隐式调用C函数wdCreate()来创建watchdog定时器。
[2]行举例说明了如何使用来自wrapper类的方法。例子为了使类VXWWd的myWatchDog实例调用定时器而调用方法start()来实现。因为这个方法在指定的对象上调用,所以方法start()的参数列表不需要指明那个定时器开始。(不像wdStart(),相应的C函数).
最后,因为myWatchDog是本地对象,在从函数task()退出的[3]行上自动调用VXWWd watchdog类中的析构函数。这样隐式调用析构函数释放watchdog对象,即使从系统定时器队列中移除的定时器仍然运行。因而,对于声明在堆栈上的对象,没有必要调用同C函数wdDelete()相当的函数。(然而,如果一个对象是用操作符new动态创建的,那么一旦应用程序不再需要这个对象你必须显示的用操作符delete来删除它。)
关于wrapper类和每个wrapper类的函数的细节,参见《VxWorks Reference Manual》。

5.5.2 Tools.h++库
Tools.h++是来自Rogue Wave Software的工业标准基类库,它支持如下功能:
.完整的collection类集合
.基于类的模板
.存储工具
.文件类和file space manager
.B树硬盘恢复
.多线程安全
.多字节和宽字符字符串
.局部字符串整理
.对时间,日期的解析和格式化
.支持多种时间区域
.支持局部消息
.局部I/O流
选择包含在项目工具VxWorks视图里的INCLUDE_CPLUS_TOOLS把这个库配置到VxWorks中,参见5.2.4配置常量。
Tools.h++类库头文件在标准的VxWorks头文件目录中,<安装路径>/target/h/rw。为了使用这个库,在语句#include "vxWorks.h"后#include一个或多个它们的头文件,然后在这些#include语句后面是所有其它的VxWorks库的头文件。关于这个库的一系列的头文件和细节参见《Rogue Wave's Tools.h++ Introduction and Reference Manual》.

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:419450次
    • 积分:4505
    • 等级:
    • 排名:第6823名
    • 原创:13篇
    • 转载:194篇
    • 译文:0篇
    • 评论:57条
    文章分类
    最新评论