c++ - para - Forma alternativa de obtener argc y argv de un proceso.
posix que es (10)
Estoy buscando formas alternativas de obtener los parámetros de línea de comando argc
y argv
proporcionados a un proceso sin tener acceso directo a las variables pasadas a main()
.
Quiero hacer una clase que sea independiente de main()
para que argc
y argv
no tengan que pasar explícitamente al código que los usa.
EDITAR: Algunas aclaraciones parecen estar en orden. Tengo esta clase
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)
{
}
Pero me gustaría un constructor predeterminado Application::Application()
, con algún código C (lo más probable), que extraiga argc
y argv
de algún lugar.
En C / C ++, si main()
no los exporta, entonces no hay una forma directa de acceder a ellos; Sin embargo, eso no significa que no haya una manera indirecta. Muchos sistemas de tipo Posix utilizan el formato elf que pasa argc
, argv
y envp
en la pila para ser inicializado por _start()
y pasar a main()
través de la convención de llamada normal. Esto generalmente se hace en ensamblaje (porque todavía no hay una forma portátil de obtener el puntero de pila) y se coloca en un "archivo de inicio", generalmente con alguna variación del nombre crt.o.
Si no tiene acceso a main()
para poder exportar los símbolos, es probable que no tenga acceso a _start()
. Entonces, ¿por qué entonces lo menciono? Por ese 3er parámetro envp
. Ya que environ
es una variable exportada estándar que se establece durante _start()
usando envp. En muchos sistemas ELF, si toma la dirección base de environ
y la recorre utilizando índices de matriz negativa, puede deducir los parámetros argc y argv. El primero debe ser NULL seguido del último parámetro argv hasta que llegue al primero. Cuando el valor apuntado a largo convertido es igual al negativo de su índice negativo, tiene argc y el siguiente (uno más que su índice negativo) es argv / argv [0].
En Linux, puede obtener esta información del sistema de archivos proc del proceso, a saber /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
En Windows, si necesita obtener los argumentos como wchar_t *
, puede usar 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;
}
Esto es muy conveniente cuando se usa MinGW porque gcc
no reconoce int _wmain(int, wchar_t *)
como un prototipo main
válido.
Estoy totalmente de acuerdo con @gavinb y otros. Realmente deberías usar los argumentos de main
y almacenarlos o pasarlos donde los necesites. Esa es la única forma portátil.
Sin embargo, solo con fines educativos, lo siguiente me funciona con clang
en OS X y gcc
en 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;
}
que dará salida:
$ 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''
La razón es porque la función de stuff
está marcada como __attribute__((constructor))
que la ejecutará cuando el enlazador dinámico cargue la biblioteca actual. Eso significa que en el programa principal se ejecutará incluso antes que main
y tendrá un entorno similar. Por lo tanto, usted es capaz de obtener los argumentos.
Pero permítanme repetir: esto es solo para fines educativos y no debe utilizarse en ningún código de producción. No será portátil y podría romperse en cualquier momento sin previo aviso.
Hay algunos escenarios comunes con funciones que requieren argumentos de tipo int argc, char *argv[]
que conozco. Uno de estos ejemplos obvios es GLUT, donde su función de inicialización está tomando estos argumentos de main()
, que es una especie de escenario "principal anidado". Este puede o no ser su comportamiento deseado. Si no, ya que no existe una convención para nombrar estos argumentos, siempre que su función tenga su analizador de argumentos, y sepa lo que está haciendo, puede hacer lo que necesite, codificado:
int foo = 1;
char * bar[1] = {" "};
o leer de la entrada del usuario o generada de otra manera, AFAIK.
int myFunc( int foo, char *bar[]){
//argument parser
{… …}
return 0;
}
Por favor, vea este mensaje SO .
La forma más portátil sería utilizar una variable global para almacenar los parámetros. Puede hacer esto menos feo usando un Singleton
(como su clase en la pregunta, pero un singleton inicializado por main) o un Service Locator
similar que es básicamente el mismo: cree un objeto en main, pase y almacene parámetros estáticamente, y tener otra o la misma clase acceder a ellos.
Las formas no portátiles son usar GetCommandLine en Windows, acceder a /proc/<pid>/cmdline
o ( /proc/self/cmdline
), o usar extensiones específicas del compilador como __attribute__((constructor))
Tenga en cuenta que no es posible poner en funcionamiento la línea de comandos a través de un equivalente de GetCommandLine
(TLDR: la línea de comandos no se pasa al kernel, sino que ya se analizó y dividió por el proceso de invocación (por ejemplo, shell))
Los argumentos a main
están definidos por el tiempo de ejecución de C, y la única forma estándar / portátil de obtener los argumentos de la línea de comando. No luches contra el sistema. :)
Si todo lo que desea hacer es proporcionar acceso a los parámetros de la línea de comandos en otras partes del programa con su propia API, hay muchas maneras de hacerlo. Simplemente inicie su clase personalizada usando argv/argc
en main
y desde ese punto en adelante puede ignorarlos y usar su propia API. El patrón singleton es ideal para este tipo de cosas.
Para ilustrar, uno de los marcos de C ++ más populares, Qt usa este mecanismo:
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
std::cout << app.arguments().at(0) << std::endl;
return app.exec();
}
Los argumentos son capturados por la aplicación y copiados en una QStringList
. Ver QCoreApplication::arguments() para más detalles.
Del mismo modo, Cocoa en la Mac tiene una función especial que captura los argumentos de la línea de comandos y los pone a disposición del marco:
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **)argv);
}
Los argumentos están disponibles en cualquier lugar de la aplicación utilizando la propiedad NSProcessInfo.arguments .
Noté en su pregunta actualizada que su clase almacena directamente una copia de argc/argv
textualmente en su instancia:
int const argc_;
char const** const argv_;
Si bien esto debería ser seguro (la vida útil de los punteros argv
debería ser válida durante toda la vida útil del proceso), no es muy similar a C ++. Considere la posibilidad de crear un vector de cadenas ( std::vector<std::string>
) como un contenedor y copie las cadenas. Entonces, incluso pueden ser mutables con seguridad (¡si lo desea!).
Quiero hacer una clase que sea independiente de main () para que argc y argv no tengan que pasar explícitamente al código que los usa.
No está claro por qué pasar esta información de main
es algo malo que debe evitarse. Así es como lo hacen los grandes marcos.
Le sugiero que use un singleton para asegurarse de que solo haya una instancia de su clase de Application
. Los argumentos se pueden transmitir a través de main
pero ningún otro código necesita saber o saber que aquí es de donde provienen.
Y si realmente quiere ocultar el hecho de que los argumentos de main
se están pasando a su constructor de Application
, puede ocultarlos con una macro.
Para responder a la pregunta en parte, concerniente a Windows, la línea de comando se puede obtener como el retorno de la función GetCommandLine
, que se documenta here , sin acceso explícito a los argumentos de la función main
.
Pasar valores no constituye crear una dependencia. A tu clase no le importa de dónde salen esos valores argc
o argv
, solo quiere que se pasen. Sin embargo, es posible que desee copiar los valores en algún lugar, no hay garantía de que no se GetCommandLine
(lo mismo se aplica a métodos alternativos como GetCommandLine
).
Todo lo contrario, de hecho, estás creando una dependencia oculta cuando usas algo como GetCommandLine
. De repente, en lugar de una simple semántica de "pasar un valor", tiene "mágicamente tomar sus entradas de otro lado", combinada con el mencionado "los valores pueden cambiar en cualquier momento", esto hace que su código sea mucho más frágil, por no mencionar imposible de probar. Y el análisis de los argumentos de la línea de comando es definitivamente uno de los casos en los que las pruebas automatizadas son bastante beneficiosas. Es una variable global frente a un enfoque de argumento de método, por así decirlo.
Suena como que lo que quieres es una variable global; lo que debes hacer es simplemente pasar argc y argv como parámetros.