Acabo de comenzar este nuevo pequeño proyecto mío en C++, y como la aplicación necesita leer y analizar los argumentos que se le pasan en la línea de comandos, pensé que sería mejor tenerlos a todos almacenados dentro de un arreglo std::vector
.
En general suelo activar el soporte de caracteres anchos en mis aplicaciones, para asegurarme de que se pueden usar caracteres Unicode, por lo que el arreglo almacenará los argumentos como objetos std::wstring
.
Probé la solución en Windows (consola y programas GUI Win32) y Linux (consola). Encontrarás más información en los comentarios. Espero que os sea de utilidad. Aquí está el código. Lo tengo en un fichero de cabecera que incluyo en mis fuentes CPP.
#include <cstring>
#include <string>
#include <vector>
// APLICACIONES DE CONSOLA DE WINDOWS
#if _CONSOLE
/**
* Obtén argumentos de línea de comandos como vector en la aplicación de consola de Windows
*
* Obtén el número de argumentos y el puntero del punto de entrada wmain
*/
std::vector<std::wstring> cmdArguments(int argc, wchar_t* argv[]) {
return std::vector<std::wstring>(argv, argv + argc);
}
// APLICACIONES CON INTERFAZ GRÁFICA DE WINDOWS
#elif _WIN32
#include <shellapi.h>
#include <windows.h>
/**
* Obtén argumentos de línea de comandos como vector en una aplicación de Windows
*
* Opcionalmente, puedes pasar un puntero a la línea de comandos
*/
std::vector<std::wstring> cmdArguments(LPWSTR lpCmdLine = NULL) {
std::vector<std::wstring> arguments;
LPWSTR* szArglist;
int nArgs;
// consigue un puntero a los argumentos de la línea de comandos mediante la API de Shell
szArglist = CommandLineToArgvW(lpCmdLine != NULL ? lpCmdLine : GetCommandLineW(), &nArgs);
if (szArglist != NULL) {
for (auto i = 0; i < nArgs; i++) arguments.push_back(szArglist[i]);
}
LocalFree(szArglist);
return arguments;
}
#endif
/**
* Obtén argumentos de línea de comandos como vector en la aplicación de consola de Windows/Linux
*
* Obtén el número de argumentos y el puntero del punto de entrada principal
*/
std::vector<std::wstring> cmdArguments(int argc, char* argv[]) {
std::vector<std::wstring> arguments;
for (auto i = 0; i < argc; i++) {
// número de caracteres multibyte en el origen y el resultado
size_t mbslen = strlen(argv[i]) + 1;
size_t mbolen;
// puntero al argumento de caracteres anchos convertido
wchar_t* wcs = new wchar_t[mbslen];
#ifdef _WIN32
// en Windows podemos usar mbstowcs_s para convertir char a wchar_t antes de agregar como wstring al arreglo
mbstowcs_s(&mbolen, wcs, mbslen, argv[i], mbslen - 1);
#else
// en Linux, calcula la longitud necesaria para contener argv[i] convertido a una cadena de caracteres anchos
mbslen = mbstowcs(NULL, argv[i], 0);
if (mbslen == (size_t) - 1) {
perror("mbstowcs");
exit(EXIT_FAILURE);
}
// asigna una cadena de caracteres anchos del tamaño deseado + 1 para el carácter ancho nulo de final
if (calloc(mbslen + 1, sizeof(*wcs)) == NULL) {
perror("calloc");
exit(EXIT_FAILURE);
}
// convierte la cadena de caracteres multibyte en argv[i] en una cadena de caracteres anchos
if (mbstowcs(wcs, argv[i], mbslen + 1) == (size_t) - 1) {
perror("mbstowcs");
exit(EXIT_FAILURE);
}
#endif
arguments.push_back(std::wstring(wcs));
}
return arguments;
}
A continuación se muestra un ejemplo de cómo lo uso dentro de un proyecto Win32 (especificar un puntero a la línea de comandos es opcional)
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// std::vector<std::wstring> arguments = cmdArguments();
std::vector<std::wstring> arguments = cmdArguments(lpCmdLine);
for (const std::wstring& i : arguments) {
MessageBox(
NULL,
(LPCWSTR)i.c_str(),
(LPCWSTR)L"Argument",
MB_ICONINFORMATION | MB_OK
);
wprintf(_T("%ls"), i.c_str());
}
return 0;
}
En Linux, también establezco la configuración regional para poder usar caracteres especiales
#include <iostream>
#include <locale.h>
#include "arguments.h"
int main(int argc, char* argv[])
{
// establece configuración regional para permitir el uso de caracteres UNICODE en la línea de comandos
if (setlocale(LC_ALL, "es_ES.UTF-8") == NULL) {
perror("setlocale");
exit(EXIT_FAILURE);
}
std::vector<std::wstring> arguments = cmdArguments(argc, argv);
for (const std::wstring& wArg : arguments) {
std::wcout << wArg.c_str() << "\n";
}
return 0;
}
Dejadme saber si tenéis algún problema o si queréis sugerir algunas mejoras.