Альтернативный способ получения 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: Командная строка не передается ядру, но уже разобрана и разделить на процесс вызова (например, оболочку))