如何使用avr-gcc在C / C ++中执行预主要初始化?

时间:2009-06-04 11:08:56

标签: c++ c initialization arduino avr-gcc

为了确保某些初始化代码在main之前运行(使用Arduino / avr-gcc),我有以下代码:

class Init {
public:
    Init() { initialize(); }
};

Init init;

理想情况下,我希望能够简单地写一下:

initialize();

但这不会编译......

是否有更简洁的方法来达到同样的效果?

注意:代码是Arduino草图的一部分,因此main函数是自动生成的,无法修改(例如在任何其他代码之前调用initialize

更新:理想情况下,初始化将在setup函数中执行,但在这种情况下,还有其他代码依赖于main之前的代码。

9 个答案:

答案 0 :(得分:13)

您可以使用GCC的constructor attribute来确保在main()之前调用它:

void Init(void) __attribute__((constructor));
void Init(void) { /* code */ }  // This will always run before main()

答案 1 :(得分:4)

通过给出“初始化”返回类型并使用它来初始化全局变量,可以使上面的内容略微缩短:

int initialize();
int dummy = initialize();

但是,您需要注意这一点,标准并不保证上述初始化(或初始对象的初始化)发生在main运行之前(3.6.2 / 3):

  

是否实现定义了命名空间作用域对象的动态初始化(8.5,9.4,12.1,12.6.1)是否在main的第一个语句之前完成。

唯一可以保证的是初始化将在'dummy'使用之前进行。

更具侵入性的选项(如果可能的话)可能是在makefile中使用“-D main = avr_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);
}

至少在这里,你可以保证在你期望的时候进行初始化。

答案 2 :(得分:3)

这是实现这一目标的一种有点邪恶的方法:

#include <stdio.h>

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 a.c

链接器会通过调用main来取代对__wrap_main的所有来电,请参阅--wrap上的ld man page

答案 3 :(得分:2)

您的解决方案简单而干净。您还可以将代码放在匿名命名空间中。我认为没有必要让它变得更好:)

答案 4 :(得分:2)

如果您使用的是Arduino环境,是否有任何理由无法将其放入setup method

当然,这是在特定于Arduino的硬件设置之后,所以如果你在main之前有这么低级的东西,那么你需要一些构造函数魔法。

更新:

好的,如果必须在main之前完成,我认为唯一的方法就是使用像你一样的构造函数。

您始终可以为其创建预处理器宏:

#define RUN_EARLY(code) \
namespace { \
    class Init { \
        Init() { code; } \
    }; \
    Init init; \
}

现在这应该有效:

RUN_EARLY(initialize())

但它并没有真正缩短,只需移动详细的代码。

答案 5 :(得分:2)

您可以使用“.init *”部分添加要在main()(甚至C运行时)之前运行的C代码。这些部分最后链接到可执行文件,并在程序初始化期间的特定时间调用。您可以在此处获取列表:

http://www.nongnu.org/avr-libc/user-manual/mem_sections.html

.init1例如与__init()弱绑定,所以如果定义__init(),它将被链接并调用第一个东西。但是,堆栈尚未设置,因此您必须小心操作(仅使用register8_t变量,而不是调用任何函数)。

答案 6 :(得分:0)

当然,你把它放在你的一个头文件中,比如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 <stdio.h>
int main (void) {
    int x = 1;
    return 0;
}' | awk '{
    print;
    if (substr($0,0,11) == "int main (") {
        print "    initialize();"
    }
}'

在添加了initialize()来电时生成以下内容:

#include <stdio.h>
int main (void) {
    initialize();
    int x = 1;
    return 0;
}

可能是您无法对生成的文件进行后处理,在这种情况下您应该忽略该最终选项,但这就​​是我首先要查看的内容。

答案 7 :(得分:0)

使用类的静态成员。它们在进入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;

答案 8 :(得分:0)

我有如何进行预编码。 在main之前执行了服务器初始化部分,引用了http://www.nongnu.org/avr-libc/user-manual/mem_sections.html initN部分。

无论如何,这仅适用于某些原因的-O0优化。我仍然试图找出哪个选项&#34;优化&#34;我的主要汇编代码。

static void
__attribute__ ((naked))
__attribute__ ((section (".init8")))    /* run this right before main */
__attribute__ ((unused))    /* Kill the unused function warning */
stack_init(void) {assembly stuff}

更新,事实证明我声称此功能未使用,导致优化例程。我打算杀死未使用的功能警告。它被固定为used used属性。