//=====================================================================
//TITLE:
//    MDK裸奔STL
//AUTHOR:
//    norains
//DATE:
//    Wednesday 16- December-2009
//Environment:
//    MDK 4.0.2
//=====================================================================
     MDK和STL都是赫赫有名的玩意,前者全名为RealView Microcontroller Development Kit,是嵌入式工程师的手中的软件开发利器;而STL为Standard Template Library,译名为C++标准库,是C++大虾的最爱。所谓的"裸奔",仅仅是在不依靠操作系统接口的情况下,让CPU跑STL编写的程序。
     在大家的印象中,像简单的嵌入式CPU,无非用的是汇编;更人性化一点,就是C语言;再狠点,无非就是除去高级特性的C++。STL?在嵌入式的MDK中用STL?这可能么?
    在这里我们不去讨论用STL编写嵌入式CPU程序是否有意义等等否决性的问题,只是简单地讨论一下,让嵌入式CPU正常跑STL编写的程序。
      本文假设朋友都熟识MDK编译环境。其实,不怎么熟也没关系,至少,知道怎么创建工程吧。:)
  我们先创建一个工程吧。因为好像最近ARM CORTEX M3内核很火,我们就选择这个核心的CPU吧。没实际的开发板也没关系,因为其实我也没有,只不过我们可以采用MDK自带的模拟器。有这个模拟器,对于我们的测试代码,足矣。当然咯,这个CPU你也可以选择别的型号,对于下文的讨论影响不大。
  
  我们先来看看一个差不多是世界上最简单的STL例子:

  view plaincopy to clipboardprint?
#include <vector>  
    
  int main()  
  {  
   std::vector<int> vtBuf;  
   vtBuf.push_back(1);   
  } 
#include <vector>
  
  int main()
  {
   std::vector<int> vtBuf;
   vtBuf.push_back(1);
  }
   
  如果不出意外,那么这段代码应该会非常顺利地编译通过。如果万一真的没通过,那么请检查一下文件的后缀是不是为CPP。
  
  好了,我们现在跑跑这个程序看看吧。
  
  只不过,很可惜,哑火了。调试器会停留在这段:
  
  
  这个时候,我们是连main主函数还没调进去的,系统就给我们抛锚了。
  
  要解决这问题其实很简单,因为MDK在编译的时候,为了方便在不同的CPU中进行移植,C库函数会调用到一些特殊的与CPU有关的特定函数。在这里之所以会出现软件断点,就是因为这些函数只有声明,没有函数体实现。所以C库中一调用,立马挂掉。
  
  知道了原因,解决问题就非常简单了。
  
  我们再建立一个文件,以.C结尾的,名字为retarget.c。在文件中,输入如下内容:
  view plaincopy to clipboardprint?
 #include <stdio.h>  
  #include <rt_misc.h>  
    
  #pragma import(__use_no_semihosting_swi)  
    
    
  extern int  sendchar(int ch);  /* in serial.c */ 
    
    
  struct __FILE { int handle; /* Add whatever you need here */ };  
  FILE __stdout;  
  FILE __stdin;  
  FILE __stderr;  
    
  int fputc(int ch, FILE *f)   
  {  
    return (sendchar(ch));  
  }  
    
  int fgetc (FILE *fp)    
  {  
    return (0);  
  }  
    
    
  int fclose(FILE* f)   
  {  
  return 0;  
  }  
    
  int ferror(FILE *f)   
  {  
    /* Your implementation of ferror */ 
    return EOF;  
  }  
  int fseek (FILE *fp, long nPos, int nMode)    
  {  
    return (0);  
  }  
    
    
  int fflush (FILE *pStream)    
  {  
    return (0);  
  }  
    
    
  void _ttywrch(int ch)   
  {  
    sendchar(ch);  
  }  
    
    
  void _sys_exit(int return_code)   
  {  
   label:    
    goto label;  /* endless loop */ 
  }  
   
 #include <stdio.h>
  #include <rt_misc.h>
  
  #pragma import(__use_no_semihosting_swi)
  
  
  extern int  sendchar(int ch);  /* in serial.c */
  
  
  struct __FILE { int handle; /* Add whatever you need here */ };
  FILE __stdout;
  FILE __stdin;
  FILE __stderr;
  
  int fputc(int ch, FILE *f)
  {
    return (sendchar(ch));
  }
  
  int fgetc (FILE *fp) 
  {
    return (0);
  }
  
  
  int fclose(FILE* f)
  {
  return 0;
  }
  
  int ferror(FILE *f)
  {
    /* Your implementation of ferror */
    return EOF;
  }
  int fseek (FILE *fp, long nPos, int nMode) 
  {
    return (0);
  }
  
  
  int fflush (FILE *pStream) 
  {
    return (0);
  }
  
  
  void _ttywrch(int ch)
  {
    sendchar(ch);
  }
  
  
  void _sys_exit(int return_code)
  {
   label: 
    goto label;  /* endless loop */
  }
  
 
  然后再建立一个Serial.c文件,内容如下:
  view plaincopy to clipboardprint?
#define CR     0x0D  
    
    
    
  /* 
   * Superclass to initialize the UART. 
   */ 
  extern void $Super$$__rt_entry(void);  
    
  void $Sub$$__rt_entry(void)    
  {  
    $Super$$__rt_entry();  
  }  
    
  /* 
   * Implementation of putchar (also used by printf function to output data) 
   */ 
  int sendchar (int ch)    
  {                 /* Write character to Serial Port    */ 
   return 1;  
  }  
    
    
  int getkey (void)    
  {                     /* Read character from Serial Port   */ 
   return 1;  
  }  
   
#define CR     0x0D
  
  
  
  /*
   * Superclass to initialize the UART.
   */
  extern void $Super$$__rt_entry(void);
  
  void $Sub$$__rt_entry(void) 
  {
    $Super$$__rt_entry();
  }
  
  /*
   * Implementation of putchar (also used by printf function to output data)
   */
  int sendchar (int ch) 
  {                 /* Write character to Serial Port    */
   return 1;
  }
  
  
  int getkey (void) 
  {                     /* Read character from Serial Port   */
   return 1;
  }
  
  

  这两个文件的功能是否正常,对于本文的讨论不重要。重要的是,我们添加这两个文件,定义了相应的函数实现后,程序跑起来会是什么结果。
  
  顺带说一下,这两个文件主要是实现了C库里面的printf和硬件的桥接,在不同的平台之下是完全不同的。换句说,你在这两个文件中的代码,决定了printf输出的去向,是输出到串口呢,还是文件呢,一切都由你说了算。
  
  好了,说了那么多,我们跑跑看:
  
  
  OK,一切正常,断点已经进入了main函数,意味着,我们的STL已经可以正常跑起来咯!

本文来自优快云博客,转载请标明出处: http://blog.youkuaiyun.com/norains/archive/2009/12/16/5019631.aspx