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

ただし、これには注意する必要があります。標準では、mainが実行される前に上記の初期化(またはinitオブジェクトの初期化)が行われることを保証していません(3.6.2 / 3)。

名前空間スコープのオブジェクトの動的初期化(8.5、9.4、12.1、12.6.1)が、mainの最初のステートメントの前に実行されるかどうかは、実装によって定義されます。

保証される唯一のことは、「ダミー」が使用される前に初期化が行われることです。

より煩わしいオプション(可能であれば)は、makefileで「-Dmain=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);
}

少なくともここでは、期待どおりに初期化が行われることが保証されています。

于 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

リンカは、へのすべての呼び出しをへの呼び出しに置き換えますmain。のldmanページ__wrap_main参照してください。--wrap

于 2009-06-04T14:46:09.607 に答える
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 に答える
2

シンプルでクリーンなソリューション。さらにできることは、コードを匿名の名前空間に置くことです。それ以上に良くする必要はないと思います:)

于 2009-06-04T11:17:08.187 に答える
2

Arduino環境を利用している場合、セットアップ方法に入れられない理由はありますか?

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

アップデート:

メインの前に実行する必要がある場合、唯一の方法は、既に行っているようにコンストラクターを使用することだと思います。

いつでもプリプロセッサ マクロを作成できます。

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

これでうまくいくはずです:

RUN_EARLY(initialize())

しかし、それは実際には物事を短くするのではなく、冗長なコードを移動するだけです。

于 2009-06-04T11:33:56.110 に答える
0

確かに、これをヘッダー ファイルの 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;
}

生成されたファイルを後処理できない可能性があります。その場合、その最後のオプションを無視する必要がありますが、それは私が最初に見ているものです。

于 2009-06-04T11:17:47.173 に答える
0

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

変換された例を次に示します。

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}

更新、この関数は使用されていないと主張したことが判明し、ルーチンを最適化することにつながりました。関数未使用の警告を殺すつもりでした。代わりに used used 属性に固定されます。

于 2016-05-25T10:56:46.370 に答える