Альтернативный способ получения argc и argv процесса
Я ищу альтернативные способы получения параметров командной строки argc
и argv
, предоставляемых процессу без прямого доступа к переменным, переданным в main()
.
Я хочу создать класс, который не зависит от main()
, так что argc
и argv
не нужно явно передавать код, который их использует.
РЕДАКТИРОВАТЬ: Некоторые разъяснения, похоже, в порядке. У меня этот класс.
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)
{
}
Но мне нужен конструктор по умолчанию Application::Application()
, с некоторым (наиболее вероятно) C-кодом, который вытаскивает argc
и argv
откуда-то.
Ответы
Ответ 1
В 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
Ответ 2
Аргументы main
определяются средой выполнения C и единственным стандартным/переносным способом получения аргументов командной строки. Не сражайтесь с системой.:)
Если все, что вы хотите сделать, это предоставить доступ к параметрам командной строки в других частях программы с помощью вашего собственного API, есть много способов сделать это. Просто инициализируйте свой собственный класс, используя argv/argc
в main
, и с этого момента вы можете игнорировать их и использовать свой собственный API. Синтаксический шаблон отлично подходит для такого рода вещей.
Чтобы проиллюстрировать, одну из самых популярных фреймворков С++, 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().
Аналогично, Cocoa на Mac имеет специальную функцию, которая захватывает аргументы командной строки и делает их доступными для фреймворка:
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **)argv);
}
Аргументы затем доступны в любом месте приложения, используя свойство NSProcessInfo.arguments.
Я заметил в вашем обновленном вопросе, что ваш класс непосредственно хранит копию argc/argv
verbatim в своем экземпляре:
int const argc_;
char const** const argv_;
Хотя это должно быть безопасно (время жизни указателей argv
должно быть действительным для полного времени жизни процесса), это не очень похоже на С++. Подумайте о создании вектора строк (std::vector<std::string>
) в качестве контейнера и скопируйте строки в. Затем они могут быть даже безопасно изменены (если хотите!).
Я хочу создать класс, который не зависит от main(), так что argc и argv не должны явно передаваться в код, который их использует.
Непонятно, почему передача этой информации из main
- это как-то плохое, чего следует избегать. Это как раз то, как это делают основные структуры.
Я предлагаю вам взглянуть на использование синглета, чтобы убедиться, что есть только один экземпляр вашего класса Application
. Аргументы могут быть переданы через main
, но ни один другой код не должен знать или заботиться о том, откуда это произошло.
И если вы действительно хотите скрыть тот факт, что аргументы main
передаются вашему конструктору Application
, вы можете скрыть их с помощью макроса.
Ответ 3
Чтобы частично ответить на вопрос, касающийся Windows, командная строка может быть получена как возврат функции GetCommandLine
, которая документирована здесь, без явных доступ к аргументам функции main
.
Ответ 4
Я полностью согласен с @gavinb и другими. Вы действительно должны использовать аргументы из main
и хранить их или передавать их там, где они вам нужны. Это единственный переносимый способ.
Однако только для образовательных целей для меня с clang
для OS X и gcc
для Linux:
#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
и иметь аналогичную среду. Следовательно, вы можете получить аргументы.
Но позвольте мне повторить: это только для образовательных целей и не должно использоваться ни в одном производственном коде. Он не будет переносимым и может прерываться в любой момент времени без предупреждения.
Ответ 5
В 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;
}
Это очень удобно при использовании MinGW, потому что gcc
не распознает int _wmain(int, wchar_t *)
как допустимый прототип main
.
Ответ 6
Передача значений не означает создание зависимости. Ваш класс не заботится о том, откуда выходят значения argc
или argv
- он просто хочет, чтобы они прошли. Возможно, вы захотите скопировать значения где-нибудь, но нет гарантии, что они не будут изменены (то же самое относится к альтернативным методам, таким как GetCommandLine
).
Напротив, на самом деле - вы создаете скрытую зависимость, когда используете что-то вроде GetCommandLine
. Внезапно вместо простой семантики "передать значение" вы "волшебным образом берете свои входы из других источников" - в сочетании с вышеупомянутыми "значения могут меняться в любое время", это делает ваш код намного более хрупким, не говоря уже о невозможно проверить. И анализ аргументов командной строки определенно является одним из случаев, когда автоматическое тестирование является весьма полезным. Это глобальная переменная и метод аргумента метода, если вы это сделаете.
Ответ 7
Существует несколько распространенных сценариев с функциями, требующими аргументов типа int argc, char *argv[]
, известных мне. Одним из таких очевидных примеров является GLUT, где его инициализирующая функция захватывает эти аргументы из main()
, что является своего рода "вложенным основным" сценарием. Это может быть или не быть вашим желаемым поведением. Если нет, поскольку нет соглашения об именовании этих аргументов, если у вашей функции есть свой парсер аргументов, и вы знаете, что делаете, вы можете делать все, что вам нужно, hardcoded:
int foo = 1;
char * bar[1] = {" "};
или читать с пользовательского ввода или сгенерировать в противном случае AFAIK.
int myFunc( int foo, char *bar[]){
//argument parser
{… …}
return 0;
}
Пожалуйста, просмотрите сообщение SO > .
Ответ 8
В C/С++, если main()
не экспортирует их, то нет прямого доступа к ним; однако это не означает, что нет косвенного пути. Многие Posix-подобные системы используют формат эльфа, который передает argc
, argv
и envp
в стек, чтобы быть инициализированным _start()
и передан в main()
через обычное соглашение о вызове. Обычно это выполняется в сборке (поскольку до сих пор нет переносного способа получения указателя стека) и помещается в "начальный файл", как правило, с некоторым изменением имени crt.o.
Если у вас нет доступа к main()
, чтобы вы могли просто экспортировать символы, у вас, вероятно, не будет доступа к _start()
. Так почему же я даже упоминаю об этом? Из-за этого третьего параметра envp
. Поскольку environ
- стандартная экспортированная переменная, которая устанавливается во время _start()
с помощью envp. Во многих ELF-системах, если вы берете базовый адрес environ
и пройдите назад, используя отрицательные индексы массива, вы можете вывести параметры argc и argv. Первый должен быть NULL, за которым следует последний параметр argv, пока вы не перейдете к первому. Когда указатель на значение, приложенное к длине, равен отрицательному значению вашего отрицательного индекса, у вас есть argc, а следующий (один больше, чем ваш отрицательный индекс) - argv/argv [0].
Ответ 9
Похоже, что вы хотите глобальную переменную; вам следует просто передать argc и argv в качестве параметров.
Ответ 10
Самый переносимый способ - использовать глобальную переменную для хранения параметров. Вы можете сделать это менее уродливым, используя Singleton
(например, ваш класс в вопросе, но синглтон инициализирован по основному) или аналогичный a Service Locator
, который в основном такой же: создать объект в основном, передать и сохранить параметры статически, и иметь доступ к другому или тому же классу.
Непереносимые способы используют GetCommandLine в Windows, обращаясь к /proc/<pid>/cmdline
или (/proc/self/cmdline
), или используя расширения для компилятора например __attribute__((constructor))
Обратите внимание, что получение командной строки в функции с помощью эквивалента GetCommandLine
невозможно) (TL;DR: Командная строка не передается ядру, но уже разобрана и разделить на процесс вызова (например, оболочку))