如何使用avr-gcc在C / C ++中执行预主要初始化?
为了确保一些初始化代码在main
之前运行(使用Arduino / avr-gcc),我有如下代码:
class Init { public: Init() { initialize(); } }; Init init;
理想情况下,我希望能够简单地写:
initialize();
但这不编译……
是否有一种不那么冗长的方式来达到同样的效果?
注意:代码是Arduino草图的一部分,因此main
function是自动生成的,不能修改(例如在任何其他代码之前调用initialize
)。
更新:理想情况下,初始化将在setup
函数中执行,但在这种情况下,还有其他代码,具体取决于它在main
之前发生。
您可以使用GCC的constructor
属性来确保在main()
之前调用它:
void Init(void) __attribute__((constructor)); void Init(void) { /* code */ } // This will always run before main()
通过给出“初始化”返回类型并使用它初始化全局变量,可以使上面的内容略微缩短:
int initialize(); int dummy = initialize();
但是,您需要注意这一点,标准并不保证上面的初始化(或者您的init对象的初始化)在main运行之前发生(3.6.2 / 3):
无论命名空间作用域对象的动态初始化(8.5,9.4,12.1,12.6.1)是否在main的第一个语句之前完成,它都是实现定义的。
唯一能保证的是初始化将在使用’dummy’之前进行。
更具侵入性的选项(如果可能的话)可能是在makefile中使用“-D main = avr_main”。 然后,您可以添加自己的main,如下所示:
// Add a declaration for the main declared by the avr compiler. int avr_main (int argc, const char * argv[]); // Needs to match exactly #undef main int main (int argc, const char * argv[]) { initialize (); return avr_main (argc, argv); }
至少在这里你保证初始化将在你期望的时候进行。
这是实现这个目标的一种有点邪恶的方法:
#include static int bar = 0; int __real_main(int argc, char **argv); int __wrap_main(int argc, char **argv) { bar = 1; return __real_main(argc, argv); } int main(int argc, char **argv) { printf("bar %d\n",bar); return 0; }
将以下内容添加到链接器标志:– --wrap main
例如。
gcc -Xlinker --wrap -Xlinker main ac
链接器将使用__wrap_main
调用替换对main
所有调用,请参阅__wrap_main
的ld手册页
您的解决方案简单而干净。 您还可以将代码放在匿名命名空间中。 我认为没有必要让它比那更好:)
如果您使用的是Arduino环境,是否有任何理由无法将其置于安装方法中 ?
当然,这是在Arduino特定的硬件设置之后,所以如果你有这么低级的东西,它真的必须在main
之前,那么你需要一些构造魔术。
更新:
好的,如果必须在main之前完成,我认为唯一的方法是使用像你已经做过的构造函数。
您始终可以为其创建预处理器宏:
#define RUN_EARLY(code) \ namespace { \ class Init { \ Init() { code; } \ }; \ Init init; \ }
现在这应该工作:
RUN_EARLY(initialize())
但它并没有真正缩短,只需移动冗长的代码。
您可以使用“.init *”部分添加要在main()(甚至C运行时)之前运行的C代码。 这些部分最后链接到可执行文件,并在程序初始化期间的特定时间调用。 您可以在此处获取列表:
http://www.nongnu.org/avr-libc/user-manual/mem_sections.html
例如.init1与__init()弱绑定,所以如果定义__init(),它将被链接并调用第一个东西。 但是,堆栈尚未设置,因此您必须小心操作(仅使用register8_t变量,而不是调用任何函数)。
使用类的静态成员。 它们在进入main之前被初始化。 缺点是您无法控制静态类成员的初始化顺序。
这是你改变的例子:
class Init { private: // Made the constructor private, so to avoid calling it in other situation // than for the initialization of the static member. Init() { initialize(); } private: static Init INIT; }; Init Init::INIT;
当然,你把它放在你的一个头文件中,比如preinit.h:
class Init { public: Init() { initialize(); } }; Init init;
然后,在你的一个编译单元中,放入:
void initialize(void) { // weave your magic here. } #include "preinit.h"
我知道这是一个kludge,但我不知道任何可移植的方式进行预主要初始化而不使用在文件范围执行的类构造函数。
您还应该注意包含多个这些初始化函数,因为我不相信C ++规定了顺序 – 它可能是随机的。
我不确定你说的这个“草图”,但是可以在将主编译单元传递给编译器之前用脚本转换主编译单元,例如:
awk '{print;if (substr($0,0,11) == "int main (") {print "initialize();"};}'
你可以看到这会对你的程序产生什么影响,因为:
echo '#include int main (void) { int x = 1; return 0; }' | awk '{ print; if (substr($0,0,11) == "int main (") { print " initialize();" } }'
添加了initialize()
调用生成以下内容:
#include int main (void) { initialize(); int x = 1; return 0; }
可能是您无法对生成的文件进行后期处理,在这种情况下您应该忽略该最终选项,但这就是我首先要查看的内容。
我是如何进行预编码的。 在main之前执行了服务器初始化部分, 请参阅http://www.nongnu.org/avr-libc/user-manual/mem_sections.html initN部分。
无论如何,这只适用于某些原因的-O0优化。 我仍然试图找出哪个选项“优化”了我的预assembly代码。
static void __attribute__ ((naked)) __attribute__ ((section (".init8"))) /* run this right before main */ __attribute__ ((unused)) /* Kill the unused function warning */ stack_init(void) {assembly stuff}
更新,事实certificate我声称此function未使用,导致优化例程。 我打算杀死函数未使用的警告。 它固定为使用过的属性。