2016-05-03 3 views
17

Я ищу альтернативные способы получения параметров командной строки argc и argv, предоставляемых процессу без прямого доступа к переменным, переданным в main().Альтернативный способ получения argc и argv процесса

Я хочу создать класс, который не зависит от 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

Что вы имеете в виду «получить»? Где еще? – user3078414

+10

Нет никакого переносного или стандартного способа получения аргументов для программы, кроме аргументов 'main'. –

+0

@PaulR да, да, API-интерфейсы находятся в C, как вы знаете, но класс находится на C++. Я не хотел отвечать на python, хотя это было бы круто. – user1095108

ответ

12

Чтобы ответить на вопрос, в части, касающейся Windows, командная строка может быть получена как возвращение функции GetCommandLine, которая документально here, без явного доступа к аргументам функции main.

+6

Это популярный ответ. Вы можете рассмотреть возможность добавления примера в строку, как использовать 'GetCommandLine'. –

+0

Или просто с [__argc, __argv и __wargv] (https://msdn.microsoft.com/en-us/library/dn727674.aspx). – isanae

25

Аргументы main определены в среде выполнения C и единственным стандартным/переносным способом получения аргументов командной строки. Не сражайтесь с системой. :)

Если все, что вы хотите сделать, это предоставить доступ к параметрам командной строки в других частях программы с помощью вашего собственного API, есть много способов сделать это. Просто инициализируйте свой собственный класс, используя argv/argc в main, и с этого момента вы можете игнорировать их и использовать свой собственный API. Синтаксический шаблон отлично подходит для такого рода вещей.

Для иллюстрации, один из самых популярных структур C++, 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 имеет специальную функцию, которая фиксирует аргументы командной строки и делает их доступными для рамки:

#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. Аргументы могут быть переданы через main, но ни один другой код не должен знать и не заботиться о том, откуда это произошло.

И если вы действительно хотите, чтобы скрыть тот факт, что аргументы main «s в настоящее время передается в ваш Application конструктор, вы можете скрыть их с помощью макроса.

+0

Занятия в C? Скажите, что это не так! – sfdcfox

+0

Активировать это. Типичный метод, который я вижу, используется для API, которые хотят, чтобы доступ к CL для анализа своих собственных аргументов (например, QT) состоял в том, чтобы просто спросить argv и argc. Самое приятное в использовании стандартной идиомы - то, что опытные программисты будут знать, что вы делаете с первого взгляда. –

+0

Я понимаю идею использования глобальной (она уродливая, но она выполняет свою работу), однако ПОЧЕМУ соблюдение уникальности? Что не так, позволяя кому-либо создавать поддельный набор аргументов и передавать их вместо этого? –

28

На Linux, вы можете получить эту информацию из прока файловой системы процесса, а именно: /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 
+6

1. вы можете читать/proc/self/cmdline 2. Арги могли быть уничтожены 3. Сверху нечетно иметь дело с файлами/proc с абстракцией FILE 4. ARG_MAX - это только предел для одного аргумента, а не всех аргументов 5. Отсутствие ошибок проверки, возможно, может быть опущено в примере. Однако самая большая проблема заключается в том, что ОП, вероятно, пытается сделать что-то неправильно, и такой ответ вроде этого не должен быть опубликован в первую очередь без дальнейших разъяснений. –

+1

Я считаю, что/proc/*/cmdline усекается до некоторой максимальной константы длины. Это не является ограничением размера командной строки процесса, который может запускаться ОС. Вместо этого это ограничение записи списка процессов в Linux. Таким образом, вы можете запустить процесс с более длинным списком аргументов, но ядро ​​не будет запоминать их все, чтобы попытаться прочитать аргументы из списка процессов. –

+0

Ядро не запоминает аргументы. Вместо этого он хранит адреса для начала и конца указанных аргументов, а затем считывает их из адресного пространства целевого процесса. Я не вижу никакого кода, урезающего результат либо (если запрошенная сумма слишком мала, конечно). Http: //lxr.free-electrons.com/source/fs/proc/base.C# L199 –

2

Есть несколько общих сценариев с функциями, требующими аргументами типа int argc, char *argv[] известного мне. Одним из таких очевидных примеров является GLUT, где его инициализирующая функция принимает эти аргументы от main(), что является своего рода «вложенным основным» сценарием. Это может быть или не быть вашим желаемым поведением. Если нет, так как нет конвенции для именования этих аргументов, если ваша функция имеет свой аргумент парсер, и вы знаете, что вы делаете, вы можете делать все, что вам нужно, жёстко:

int foo = 1; 
char * bar[1] = {" "}; 

или читать от пользовательского ввода или сгенерированного в противном случае, AFAIK.

int myFunc(int foo, char *bar[]){ 
//argument parser 
{… …} 
return 0; 
} 

См. Это SO post.

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 прототипа.

5

Передача значений не означает создание зависимости. Ваш класс не заботится о том, где выходят значения argc или argv - он просто хочет, чтобы они прошли. Возможно, вы захотите скопировать значения где-то, но нет гарантии, что они не будут изменены (то же самое относится к альтернативным методам, например GetCommandLine).

На самом деле, вы создаете скрытую зависимость, когда используете что-то вроде GetCommandLine. Внезапно вместо простой семантики «передать значение» вы «волшебным образом берете свои входы из других источников» - в сочетании с вышеупомянутыми «значения могут меняться в любое время», это делает ваш код намного более хрупким, не говоря уже о невозможно проверить. И анализ аргументов командной строки определенно является одним из случаев, когда автоматическое тестирование является весьма полезным. Это глобальная переменная и метод аргумента метода, если хотите.

+0

Но OP утверждает, что это в C, поэтому нет классов. Вы можете передать их функции init и иметь значения, хранящиеся в статических переменных в коде модуля. – jamesqf

+0

@jamesqf Ну, я просто использую то же имя, которое использовал OP :) Передача значений - важная часть, а не то, как именно она реализована. – Luaan

1

Похоже, что вы хотите глобальную переменную; вам следует просто передать argc и argv в качестве параметров.

+1

Это именно то, чего я не хочу делать. – user1095108

+1

Итак, вы хотите получить getter для глобальной переменной, но вы не хотите, чтобы глобальная переменная eh? – GameDeveloper

12

Я полностью согласен с @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 и иметь аналогичную среду. Поэтому вы можете получить аргументы.

Но позвольте мне повторить: это только для образовательных целей и не должно использоваться ни в одном производственном коде. Он не будет переносимым и может сломаться в любой момент времени без предупреждения.

+3

Это ужасно. – cat

+3

Это довольно изящно. Являются ли функции '__attribute __ ((constructor))' гарантированы гарантией доступа к argv/argc, или это просто злоупотребляет некоторой случайностью представления? –

+0

Это не гарантировано. –

2

В C/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].

0

Самый переносимый способ - использовать глобальную переменную для хранения параметров. Вы можете сделать это менее уродливым, используя Singleton (например, ваш класс в вопросе, но синглтон инициализирован по основному) или аналогичный Service Locator, который в основном такой же: создать объект в главном, передать и сохранить параметры статически и другой или тот же класс доступа к ним.

непортабельные способы используют GetCommandLine в Windows, доступ к /proc/<pid>/cmdline или (/proc/self/cmdline), или с помощью расширений компилятора конкретного как __attribute__((constructor))

Обратите внимание, что получение командной строки в функции через эквивалент GetCommandLineis not possible (TLDR: Командная строка не передается ядру, но уже разобрана и разделена на вызывающий процесс (например, оболочка))

Смежные вопросы