11

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

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

Init init;

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

initialize();

但这不编译...

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

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

更新:理想情况下,初始化将在setup函数中执行,但在这种情况下,还有其他代码取决于它发生在main.

4

9 回答 9

15

您可以使用 GCC 的constructor属性来确保它在之前被调用main()

void Init(void) __attribute__((constructor));
void Init(void) { /* code */ }  // This will always run before main()
于 2009-06-04T15:03:39.870 回答
4

您可以通过给“initialize”一个返回类型,并使用它来初始化一个全局变量来使上面的内容稍微短一些:

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

至少在这里你可以保证初始化会在你期望的时候发生。

于 2009-06-04T12:40:28.567 回答
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

链接器将用调用替换所有调用mainto __wrap_main,请参见ld 手册--wrap

于 2009-06-04T14:46:09.607 回答
2

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

于 2009-06-04T11:17:08.187 回答
2

如果您使用的是 Arduino 环境,是否有任何理由不能将其放在setup 方法中?

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

更新:

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

您始终可以对其进行预处理器宏:

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

现在这应该工作:

RUN_EARLY(initialize())

但这并没有真正让事情变得更短,只是移动了冗长的代码。

于 2009-06-04T11:33:56.110 回答
2

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

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

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

于 2009-09-29T10:36:23.017 回答
0

当然,你把它放在你的一个头文件中,比如 preinit.h:

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

然后,在您的一个编译单元中,输入:

void initialize(void) {
    // weave your magic here.
}
#include "preinit.h"

我知道这是一个杂乱无章的事情,但我不知道有任何可移植的方式来进行预主初始化,而不使用在文件范围内执行的类构造函数。

您还应该小心包含多个这些初始化函数,因为我不相信 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;
}

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

于 2009-06-04T11:17:47.173 回答
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;
于 2009-06-04T11:17:55.910 回答
0

这就是我执行预主编码的方式。在 main 之前执行了一些服务器 init 部分,参考http://www.nongnu.org/avr-libc/user-manual/mem_sections.html initN 部分。

无论如何,由于某种原因,这只适用于 -O0 优化。我仍然试图找出哪个选项“优化”了我的预主汇编代码。

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

更新,原来我声称这个函数没有被使用,导致优化程序走了。我打算杀死未使用的功能警告。它被固定为使用过的属性。

于 2016-05-25T10:56:46.370 回答