C/C++ で avr-gcc を使用してプリメイン初期化を実行するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/949890

質問

いくつかの初期化コードが実行される前に確実に実行されるようにするため、 main (Arduino/avr-gccを使用)次のようなコードがあります。

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

Init init;

理想的には、次のように単純に記述できるようにしたいと考えています。

initialize();

しかし、これはコンパイルされません...

同じ効果を達成するもっと冗長な方法はありますか?

注記: コードは Arduino スケッチの一部であるため、 main 関数は自動的に生成され、変更することはできません (たとえば、 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オブジェクトのための1)は、メインの前に行われることを保証するものではありませんが(3.6.2 / 3)実行されます:

  

これは、名前空間スコープのオブジェクトの動的初期化(8.5、9.4、12.1、12.6.1)は、メインの最初の文の前に行われたか否かを実装定義されます。

が保証されている唯一のものは、初期設定が「ダミー」の前に開催されます、これまで使用されていることである。

(それが可能だ場合)より侵入のオプションは、あなたのメイクファイルに「avr_main = -Dメイン」を使用するかもしれません。次のようにあなたがあなた自身のメインを追加することができます:

// 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 <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ページ

シンプルでクリーンで

あなたのソリューション。あなたはさらに行うことができますことは、匿名の名前空間にコードを置くことです。私はそれよりもそれを改善するための必要性を見ない:)

あなたはArduinoの環境を使用している場合は、セットアップにそれを置くことができない何らかの理由があります方法で?

もちろん、これはArduinoの固有のハードウェアのセットアップの後なので、あなたはそれが本当にmain前に行かなければならないような低レベルのものを持っている場合、あなたは、いくつかのコンストラクタの魔法を必要とする。

UPDATE:

それがメインの前に行う必要がある場合は、

[OK]を、私は唯一の方法は、あなたがすでにやるようにコンストラクタを使用することだと思います。

あなたはいつもそれのプリプロセッサマクロを作ることができます:

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

さて、この作業をする必要があります:

RUN_EARLY(initialize())

しかし、それは本当にただの周りに冗長なコードを移動、物事が短くなっていない。

あなたは、メイン()(さらにCランタイム)の前に実行されるようにCコードを追加するために「* .INIT」セクションを使用することができます。これらのセクションは、最後に実行可能ファイルにリンクし、プログラムの初期化中に特定の時間にアップと呼ばれています。あなたがここにリストを取得することができます:

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

あなたは__init()を定義した場合、それがリンクされ、最初に呼び出されるので、例えば

.init1が弱い、)(__initにバインドされています。あなたは何をすべきかに注意する必要がありますのでただし、スタックは(のみregister8_t変数を使用して、任意の関数を呼び出していない)、設定されていません。

クラスの静的メンバを使用してください。これらは、メインに入る前に初期化されます。欠点は、静的クラスのメンバの初期化の順序を制御することができないということです。

ここにあなたの例では、変換されます:

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;

確かに、これをヘッダー ファイルの 1 つ、たとえば preinit.h に入れます。

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

そして、 1つ コンパイル単位の場合は、次のように入力します。

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

生成されたファイルを後処理できない場合は、最後のオプションを無視する必要があるかもしれませんが、それが私が最初に検討していることです。

私は、プリメイン符号化を行う方法があります。 メインの前に実行サーバの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}

更新、それは私が離れてルーチンを最適化するために、リード、この機能は未使用である主張が判明しました。私は、機能未使用の警告を殺すことを意図していました。代わりに使用する属性を使用して固定されます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top