-->

How can I perform pre-main initialization in C/C++

2020-02-19 18:15发布

问题:

In order to ensure that some initialization code runs before main (using Arduino/avr-gcc) I have code such as the following:

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

Init init;

Ideally I'd like to be able to simply write:

initialize();

but this doesn't compile...

Is there a less verbose way to achieve the same effect?

Note: the code is part of an Arduino sketch so the main function is automatically generated and cannot be modified (for example to call initialize before any other code).

Update: ideally the initialization would be performed in the setup function, but in this case there is other code depending on it which occurs before main.

回答1:

You can use GCC's constructor attribute to ensure that it gets called before main():

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


回答2:

You can make the above very slightly shorter by giving "initialize" a return type, and using that to initialize a global variable:

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

However, you need to be careful with this, the standard does not guarantee that the above initialization (or the one for your init object) takes place before main is run (3.6.2/3):

It is implementation-defined whether or not the dynamic initialization (8.5, 9.4, 12.1, 12.6.1) of an object of namespace scope is done before the first statement of main.

The only thing that is guaranteed is that the initialization will take place before 'dummy' is ever used.

A more intrusive option (if it's possible) might be to use "-D main=avr_main" in your makefile. You could then add your own main as follows:

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

At least here you're guaranteed that the initialization will take place when you expect.



回答3:

Here's a somewhat evil method of achieving this:

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

Add the following to the linker flags: --wrap main

eg.

gcc -Xlinker --wrap -Xlinker main a.c

The linker will replace all calls to main with calls to __wrap_main, see the ld man page on --wrap



回答4:

Your solution in simple and clean. What you can additionally do is to put your code in anonymous namespace. I don't see any need to make it better than that :)



回答5:

If you are using the Arduino environment, is there any reason you can't place it in the setup method?

Of course, this is after the Arduino-specific hardware setup, so if you have such low-level stuff that it really has to go before main, then you need some constructor magic.

UPDATE:

Ok, if it has to be done before the main I think the only way is to use a constructor like you already do.

You can always make a preprocessor macro of it:

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

Now this should work:

RUN_EARLY(initialize())

But it's not really making things shorter, just moving the verbose code around.



回答6:

You can use the ".init*" sections to add C code to be run before main() (and even the C runtime). These sections are linked into the executable at the end and called up at specific time during program initialization. You can get the list here:

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

.init1 for example is weakly bound to __init(), so if you define __init(), it will be linked and called first thing. However, the stack hasn't been setup, so you have to be careful in what you do (only use register8_t variable, not call any functions).



回答7:

Use static members of classes. They are initialized before entering to main. The disadvantage is that you can't control the order of the initialization of the static class members.

Here is your example transformed:

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:

Sure, you put this in one of your your header files, say preinit.h:

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

and then, in one of your compilation units, put:

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

I know that's a kludge but I'm not aware of any portable way to do pre-main initialization without using a class constructor executed at file scope.

You should also be careful of including more than one of these initialization functions since I don't believe C++ dictates the order - it could be random.

I'm not sure of this "sketch" of which you speak but would it be possible to transform the main compilation unit with a script before having it passed to the compiler, something like:

awk '{print;if (substr($0,0,11) == "int main (") {print "initialize();"};}'

You can see how this would affect your program because:

echo '#include <stdio.h>
int main (void) {
    int x = 1;
    return 0;
}' | awk '{
    print;
    if (substr($0,0,11) == "int main (") {
        print "    initialize();"
    }
}'

generates the following with the initialize() call added:

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

It may be that you can't post-process the generated file in which case you should ignore that final option, but that's what I'd be looking at first.



回答9:

There is how I perform pre-main coding. There are sever init sections executed before main, refers to http://www.nongnu.org/avr-libc/user-manual/mem_sections.html initN sections.

Anyhow, this only works on -O0 optimization for some reason. I still try to find out which option "optimized" my pre-main assembly code away.

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

Update, it turns out I claimed this function is unused, leading to optimize the routine away. I was intended to kill function unused warning. It is fixed to used used attribute instead.