main()
に渡された変数に直接アクセスせずに、プロセスに提供されたコマンドラインパラメーターargc
およびargv
を取得する別の方法を探しています。
argc
とargv
を使用するコードに明示的に渡す必要がないように、main()
から独立したクラスを作成したいと思います。
編集:いくつかの明確化は整然としているようです。このクラスがあります。
_class Application
{
int const argc_;
char const** const argv_;
public:
explicit Application(int, char const*[]);
};
Application::Application(int const argc, char const* argv[]) :
argc_(argc),
argv_(argv)
{
}
_
しかし、どこかからargc
とargv
をプルするデフォルトのコンストラクターApplication::Application()
と、いくつかの(おそらく)Cコードが必要です。
Linuxでは、この情報はプロセスのprocファイルシステム、つまり/proc/$$/cmdline
から取得できます。
int pid = getpid();
char fname[PATH_MAX];
char cmdline[ARG_MAX];
snprintf(fname, sizeof fname, "/proc/%d/cmdline", pid);
FILE *fp = fopen(fname);
fgets(cmdline, sizeof cmdline, fp);
// the arguments are in cmdline
main
への引数は、Cランタイムによって定義され、コマンドライン引数を取得するための唯一の標準/ポータブルな方法です。システムと戦わないでください。 :)
自分のAPIを使用して、プログラムの他の部分のコマンドラインパラメータへのアクセスを提供することだけが目的の場合は、多くの方法があります。 main
のargv/argc
を使用してカスタムクラスを初期化するだけで、それ以降はそれらを無視して独自のAPIを使用できます。シングルトンパターンは、この種のものに最適です。
説明するために、最も人気のあるC++フレームワークの1つであるQtは、このメカニズムを使用します。
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
std::cout << app.arguments().at(0) << std::endl;
return app.exec();
}
引数はアプリによってキャプチャされ、QStringList
にコピーされます。詳細は QCoreApplication :: arguments() を参照してください。
同様に、Mac上のCocoaには、コマンドライン引数をキャプチャしてフレームワークで使用できるようにする特別な関数があります。
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **)argv);
}
その後、引数は NSProcessInfo.arguments プロパティを使用してアプリのどこからでも利用できます。
更新された質問で、クラスにargc/argv
のコピーがインスタンスに直接格納されていることに気づきました。
int const argc_;
char const** const argv_;
これは安全であるはずですが(argv
ポインターの存続期間は、プロセスの存続期間全体で有効でなければなりません)、C++によく似ているわけではありません。文字列のベクトル(std::vector<std::string>
)をコンテナとして作成し、文字列をコピーすることを検討してください。そうすれば、(必要に応じて)安全に変更することもできます。
Main()から独立したクラスを作成して、argcとargvを、それらを使用するコードに明示的に渡す必要がないようにします。
この情報をmain
から渡すことが、どういうわけか避けられるべき悪いことなのかは明らかではありません。これは、主要なフレームワークが行う方法です。
シングルトンを使用して、Application
クラスのインスタンスが1つだけであることを確認することをお勧めします。引数はmain
を介して渡すことができますが、他のコードでは、これがどこから来たかを知る必要も、気にする必要もありません。
また、reallyがmain
の引数がApplication
コンストラクタに渡されているという事実を隠したい場合は、マクロで非表示にします。
Windowsに関する部分的な質問に答えるために、コマンドラインは、明示的にアクセスせずに、 ここに記載されているGetCommandLine
関数の戻りとして取得できます 。 main
関数の引数に。
@gavinbと完全に同意します。 main
からの引数を実際に使用して保存するか、必要な場所に渡す必要があります。それが唯一の移植可能な方法です。
ただし、教育目的のみで、以下はOS Xではclang
、Linuxではgcc
で動作します。
_#include <stdio.h>
__attribute__((constructor)) void stuff(int argc, char **argv)
{
for (int i=0; i<argc; i++) {
printf("%s: argv[%d] = '%s'\n", __FUNCTION__, i, argv[i]);
}
}
int main(int argc, char **argv)
{
for (int i=0; i<argc; i++) {
printf("%s: argv[%d] = '%s'\n", __FUNCTION__, i, argv[i]);
}
return 0;
}
_
出力されます:
_$ gcc -std=c99 -o test test.c && ./test this will also get you the arguments
stuff: argv[0] = './test'
stuff: argv[1] = 'this'
stuff: argv[2] = 'will'
stuff: argv[3] = 'also'
stuff: argv[4] = 'get'
stuff: argv[5] = 'you'
stuff: argv[6] = 'the'
stuff: argv[7] = 'arguments'
main: argv[0] = './test'
main: argv[1] = 'this'
main: argv[2] = 'will'
main: argv[3] = 'also'
main: argv[4] = 'get'
main: argv[5] = 'you'
main: argv[6] = 'the'
main: argv[7] = 'arguments'
_
その理由は、stuff
関数が__attribute__((constructor))
としてマークされているためです。これにより、ダイナミックリンカーによって現在のライブラリが読み込まれたときに実行されます。つまり、メインプログラムではmain
の前でも実行され、同様の環境になります。したがって、引数を取得できます。
繰り返しますが、これは教育目的のみであり、本番用コードでは使用しないでください。ポータブルではなく、いつの時点でも警告なしに壊れる可能性があります。
Windowsでは、引数を_wchar_t *
_として取得する必要がある場合は、CommandLineToArgvW()
を使用できます。
_int main()
{
LPWSTR *sz_arglist;
int n_args;
int result;
sz_arglist = CommandLineToArgvW(GetCommandLineW(), &n_args);
if (sz_arglist == NULL)
{
fprintf(stderr, _("CommandLineToArgvW() failed.\n"));
return 1;
}
else
{
result = wmain(n_args, sz_arglist);
}
LocalFree(sz_arglist);
return result;
}
_
gcc
はint _wmain(int, wchar_t *)
を有効なmain
プロトタイプとして認識しないため、MinGWを使用する場合に非常に便利です。
値を渡しても、依存関係が作成されるわけではありません。クラスは、それらのargc
またはargv
の値がどこから出てくるかを気にしません-渡されたいだけです。ただし、値をどこかにコピーすることをお勧めします。値が変更されないという保証はありません(GetCommandLine
などの代替メソッドにも同じことが当てはまります)。
実際、まったく逆です。GetCommandLine
のようなものを使用すると、非表示の依存関係が作成されます。突然、単純な「値を渡す」セマンティクスの代わりに、「魔法のように他の場所から入力を取得する」-前述の「値はいつでも変更できる」と組み合わせると、これにより、コードはさらに脆弱になります。テストすることは不可能です。また、コマンドライン引数の解析は、自動テストが非常に有益な場合の1つです。もしそうなら、それはグローバル変数対メソッド引数アプローチです。
C/C++では、main()
がそれらをエクスポートしない場合、それらに直接アクセスする方法はありません。ただし、これは間接的な方法がないという意味ではありません。多くのPosixライクなシステムは、argc
、argv
、envp
を_start()
で初期化し、 main()
通常の呼び出し規約による。これは通常、アセンブリで行われ(スタックポインタを取得するポータブルな方法がまだないため)、「スタートファイル」に入れられ、通常はcrt.oという名前のバリエーションがいくつかあります。
シンボルをエクスポートできるようにmain()
にアクセスできない場合は、_start()
にアクセスできない可能性があります。では、なぜそれについて言及するのでしょうか。そのため、3番目のパラメーターはenvp
です。 environ
は標準のエクスポートされた変数なので、doesはenv [を使用して_start()
中に設定されます。多くのELFシステムでは、environ
のベースアドレスを取得し、負の配列インデックスを使用して逆方向に歩くと、argcおよびargvパラメーターを推定できます。最初のものに到達するまで、最初のものはNULLで、その後に最後のargvパラメータが続く必要があります。 longにキャストされたポインティングされた値が負のインデックスの負の値に等しい場合、argcがあり、次の(負のインデックスより1つ多い)はargv/argv [0]です。
__int argc, char *argv[]
_型の引数を必要とする関数の一般的なシナリオはいくつか知られています。そのような明白な例の1つはGLUTです。その初期化関数は、これらの引数をmain()
から引き継ぎます。これは、「ネストされたメイン」シナリオの一種です。これは望ましい動作である場合とそうでない場合があります。そうでない場合は、関数に引数パーサーがあり、実行していることがわかっている限り、これらの引数に名前を付けるための規則がないため、必要なことをハードコードして実行できます。
_int foo = 1;
char * bar[1] = {" "};
_
またはユーザー入力から読み取るか、別の方法で生成された、AFAIK。
_int myFunc( int foo, char *bar[]){
//argument parser
{… …}
return 0;
}
_
こちらをご覧ください SO post 。
最もポータブルな方法は、パラメーターを格納するためにグローバル変数を使用することです。 Singleton
(問題のクラスのようですが、メインによって初期化されたシングルトン)または同様の_Service Locator
_を使用して、これをより見苦しくないようにすることができます。 、静的にパラメータを渡して保存し、別のまたは同じクラスがそれらにアクセスするようにします。
移植性のない方法は、Windowsで GetCommandLine を使用するか、_/proc/<pid>/cmdline
_または(_/proc/self/cmdline
_)にアクセスするか、__attribute__((constructor))
などのコンパイラ固有の拡張機能を使用します
GetCommandLine
と同等の機能でコマンドラインを取得することに注意してください 不可能 (TLDR:コマンドラインはカーネルに渡されませんが、呼び出しプロセスによってすでに解析および分割されています(たとえば、シェル))
必要なのはグローバル変数のようです。あなたがしなければならないことは、単に引数としてargcとargvを渡すことです。