piątek, 27 listopada 2009

Przeszukiwanie listy argumentów linii poleceń w C

Po długich i wyczerpujących walkach z nvcc (kod skompilowany nvcc nie bardzo się komponuje z g++, nie wiem czemu, innym DZIAŁA - SOA#1) postanowiłem, że algorytm optymalizacji rojem będzie miał postać programu w C, który zapisze wynik działania algorytmu w bazie sqlite3, a ja sobie w innej aplikacji skompilowanej już normalnym kompilatorem będę te dane obrabiał. W międzyczasie okazało się, że fajnie byłoby ustalać liczbę cząsteczek roju i postanowiłem do tego wykorzystać argumenty linii poleceń. Wtedy pojawił się problem jak z argc i argv wyciągnąć to o co mi chodzi. Czynność ta jest wykorzystywana na porządku dziennym w programach, więc istnieje w tym celu odpowiednia procedura, a nazywa się ona getopt() i znajduje się w pliku nagłówkowym unistd.h. W obecnej chwili używam dwóch opcji: -n oraz -o. Obie wymagają argumentu. Oto kod:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char** argv) {
//initialize variables
int N = 10; // Ilosc agentow
int c = 0;
char* dbfilename = NULL;

while((c = getopt(argc, argv, "n:o:")) != -1)
{
switch(c)
{
case 'n':
N = atoi(optarg);
printf("Number of particles: %d\n", N);
if (N <= 0)
{
fprintf(stderr, "Invalid number of particles, setting to default = 10\n");
N = 10;
}
break;
case 'o':
dbfilename = optarg;
printf("Output to file: %s\n", dbfilename);
break;
case '?':
switch(optopt)
{
case 'n':
fprintf(stderr, "Option -n sets number of particles and requires integer argument.\n");
break;
case 'o':
fprintf(stderr, "Option -o sets name of output file and requires a string argument.\n");
break;
default:
fprintf(stderr, "Unknown option: %c\n", optopt);
};
break;
default:
printf("Abort: %d = %c\n", c, c);
abort();
}
}
Przede wszystkim po "przegryzieniu się" przez wszystkie argumenty getopt() zwraca -1, dlatego taki warunek jest w while. W przeciwnym przypadku dostaniemy wartość znaku, który reprezentuje opcję. Opcje jakie przewidujemy w programie podajemy jako trzeci argument. Aby zaznaczyć, że opcja ma wymagany argument należy dodać po niej ":". Aby zaznaczyć, że opcja ma opcjonalny argument dodajemy "::". W switch podejmujemy odpowiednią akcję w zależności od opcji.
Dla -n będzie to zamiana alphanumeric to integer, czyli zamiana łańcucha znaków na liczbę całkowitą, a następnie sprawdzenie czy wartość ta jest większa od 0. Argument dla opcji jest przechowywany w optarg jako łańcuch znaków.
Opcja -o odpowiada za nazwę pliku wynikowego. Jak widać można przypisać wskaźnik optarg do wskaźnika typu char*, czyli nie jest on typu const char* (tak jakby to było w przypadku np printf("Tekst typu const char*"). Dokładniej optarg wskazuje na jeden z elementów argv, więc można go modyfikować. W przypadku błędów podawana jest wartość '?' co może oznaczać brak argumentu dla opcji lub nieznaną opcję.
Opcja default z tego co zrozumiałem nie ma prawa się wydarzyć, ale dodałem tam trochę kodu, żeby dowiedzieć się co się dzieje w razie czego. W końcu dobry programista patrzy w obie strony przed przejściem przez jednokierunkową ulicę.