Альтернативный способ получения 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 откуда-то

18

Решение

В 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
28

Другие решения

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

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

Чтобы проиллюстрировать, одна из самых популярных платформ 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 :: аргументы () Больше подробностей.

Точно так же у Какао на 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аргументы передаются вашему Application конструктор, вы можете скрыть их с помощью макроса.

27

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

13

Я полностью согласен с @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 и иметь похожую среду. Таким образом, вы можете получить аргументы.

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

13

В 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, Внезапно, вместо простой семантики «передать значение», вы «волшебным образом берете их входные данные из других мест» — в сочетании с вышеупомянутым «значения могут измениться в любое время», это делает ваш код намного более хрупким, не говоря уже о невозможно проверить. И разбор аргументов командной строки, безусловно, является одним из случаев, когда автоматическое тестирование весьма полезно. Это глобальная переменная против метода аргументного подхода, если хотите.

5

В C / C ++, если main() не экспортирует их, то нет прямого доступа к ним; однако это не означает, что нет никакого косвенного пути. Многие Posix-подобные системы используют формат elf, который передает argc, argv а также envp в стеке для инициализации _start() и перешел в main() через обычное соглашение о вызовах. Обычно это делается в сборке (поскольку до сих пор нет переносимого способа получить указатель стека) и помещается в «стартовый файл», обычно с некоторым изменением имени crt.o.

Если у вас нет доступа к main() так что вы можете просто экспортировать символы, у вас, вероятно, не будет доступа к _start(), Так почему же я вообще об этом упоминаю? Из-за этого 3-го параметра envp, поскольку environ стандартная экспортируемая переменная, делает быть настроенным во время _start() используя envp. Во многих системах ELF, если вы берете базовый адрес environ и пройти его назад, используя отрицательные индексы массива, вы можете вывести параметры argc и argv. Первый должен быть NULL, за которым следует последний параметр argv, пока вы не доберетесь до первого. Когда указатель на значение, приведенное к long, равен отрицательному значению отрицательного индекса, у вас есть argc, а следующий (на один больше, чем отрицательный индекс) — argv / argv [0].

3

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

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

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

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

Пожалуйста, посмотрите это ТАК сообщение.

2