How can I perform pre-main initialization in C/C++ with avr-gcc?

后端 未结 9 1963
名媛妹妹
名媛妹妹 2020-12-28 09:21

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         


        
相关标签:
9条回答
  • 2020-12-28 09:57

    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 :)

    0 讨论(0)
  • 2020-12-28 10:00

    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.

    0 讨论(0)
  • 2020-12-28 10:00

    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

    0 讨论(0)
  • 2020-12-28 10:07

    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;
    
    0 讨论(0)
  • 2020-12-28 10:15

    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.

    0 讨论(0)
  • 2020-12-28 10:19

    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.

    0 讨论(0)
提交回复
热议问题