Vinnaren i pepparkakshustävlingen!
2018-09-23, 02:46
  #3061
Medlem
erikp121s avatar
Hur får man ett C++ program att utgå från vart binären är när man ska öppna en fil med std::ifstream?

Jag vill alltså kunna utgå från vart som helst (exempelvis /) och köra ett program, men programmet ska utgå från binärens directory när den söker efter filer.

/path/to/test/main.cpp

Binären test.bin ligger i /path/to/test/bin och test.txt ligger i /path/to/test/data. Om jag kör ./test.bin från /path/to/test/bin så hittar den test.txt, men om jag utgår från /path/to/test och kör ./bin/test.bin så hittar den inte text.txt i data (den söker då efter /path/to/data istället för path/to/test/data...).

Finns det någon simpel lösning på detta?
__________________
Senast redigerad av erikp121 2018-09-23 kl. 02:55.
Citera
2018-09-23, 05:50
  #3062
Avstängd
Citat:
Ursprungligen postat av erikp121
Hur får man ett C++ program att utgå från vart binären är när man ska öppna en fil med std::ifstream?

Jag vill alltså kunna utgå från vart som helst (exempelvis /) och köra ett program, men programmet ska utgå från binärens directory när den söker efter filer.

/path/to/test/main.cpp

Binären test.bin ligger i /path/to/test/bin och test.txt ligger i /path/to/test/data. Om jag kör ./test.bin från /path/to/test/bin så hittar den test.txt, men om jag utgår från /path/to/test och kör ./bin/test.bin så hittar den inte text.txt i data (den söker då efter /path/to/data istället för path/to/test/data...).

Finns det någon simpel lösning på detta?

Binärer bör nog ligga i ~/bin eller andra ställen på PATH. Det är ett litet säkerhetsproblem att springa runt och köra program i diverse mappar, eftersom någon annan kan ändra programmet. Eller radera det och byta ut det om de har skrivåtkomst till mappen.

Använd DATA_DIR i källkoden och kompliera med -DDATA_DIR=/path/to/test/data. För filer där varje användare ska ha egna, som inställningar, kolla hemmappen med getenv("HOME"). Bara Windowstöntar flyttar runt sina EXE-filer, på *nix installerar vi.

Du skulle även kunna låta användare ange data directory vid runtime med en miljövariabel som heter MITTPROGRAM_DATA_DIR, som de kan sätta i sin .profile eller få default från /etc/profile, och/eller helt enkelt läsa argv[].

Och om allt ovanstående failar, defaultar du till /usr/local/share/dittprogram eller liknande. Inte till mappen som innehåller programmet, som skulle kunna vara en länk som någon skapat. Nu var visserligen inget av det här svar på din fråga, men kanske lite tips i alla fall.
__________________
Senast redigerad av lennart.lyktstolpe 2018-09-23 kl. 06:37.
Citera
2018-09-23, 14:26
  #3063
Medlem
erikp121s avatar
Citat:
Ursprungligen postat av lennart.lyktstolpe
Binärer bör nog ligga i ~/bin eller andra ställen på PATH. Det är ett litet säkerhetsproblem att springa runt och köra program i diverse mappar, eftersom någon annan kan ändra programmet. Eller radera det och byta ut det om de har skrivåtkomst till mappen.

Använd DATA_DIR i källkoden och kompliera med -DDATA_DIR=/path/to/test/data. För filer där varje användare ska ha egna, som inställningar, kolla hemmappen med getenv("HOME"). Bara Windowstöntar flyttar runt sina EXE-filer, på *nix installerar vi.

Du skulle även kunna låta användare ange data directory vid runtime med en miljövariabel som heter MITTPROGRAM_DATA_DIR, som de kan sätta i sin .profile eller få default från /etc/profile, och/eller helt enkelt läsa argv[].

Och om allt ovanstående failar, defaultar du till /usr/local/share/dittprogram eller liknande. Inte till mappen som innehåller programmet, som skulle kunna vara en länk som någon skapat. Nu var visserligen inget av det här svar på din fråga, men kanske lite tips i alla fall.
Ja, ska man göra riktiga program är det nog rätt väg att gå. Nu är jag dock nybörjare så det känns lättare att isolera program till samma arbetsmapp.

Lyckades googla fram det jag ville göra (länk: https://stackoverflow.com/questions/...-of-executable ).

Här är hela nya main.cpp för den som är intresserad:


Nu är det bara att läsa på om argument m.m. så att man förstår mer vad som händer.
__________________
Senast redigerad av erikp121 2018-09-23 kl. 14:40.
Citera
2018-09-23, 16:48
  #3064
Avstängd
Citat:
Ursprungligen postat av erikp121
Ja, ska man göra riktiga program är det nog rätt väg att gå. Nu är jag dock nybörjare så det känns lättare att isolera program till samma arbetsmapp.

Låt mig vara lite bråkig och förklara hur jag resonerar, mest för att det är kul att diskutera programmering.

Då du ville utöka programmet till att kunna anropas från andra ställen, istället för att skriva ./namnet jämt, och t.o.m. har en datamapp åt det, uppfattade jag det som att det var på väg från ett testprogram till något du ville utveckla längre, ett "riktigt program". Och nybörjarargumentet faller också eftersom alla mina lösningar var enklare än koden du klistrade in i main().

Jag gillar program som man inte behöver installera. Som man kan testa, lägga undan dess mapp någonstans, och radera när man vill utan att det har kladdat ner datorn. Men på *nix kommer dina användare ändå behöva kompilera om binärfilen, och då tycker jag att du lika gärna kan göra det ordentligt:

Förklara i README-filen att han/hon ska placera data-mappen på valfritt ställe, eller bara låta den ligga kvar om hela programmets mapp redan ligger på ett lämpligt ställe som t.ex. ~ för en enskild användare, och kompilera med -DDATA_DIR=/sökväg/till/den.

Då har du undvikit en installation som stoppar upp saker där användaren inte vill ha dem, och istället gett honom/henne kontrollen. Att människan ett halvår senare när han önskar avinstallera programmet, har glömt var han lade mappen och inte har kvar README-filen, tycker jag är hans problem.

Duger inte detta, utan du vill att bara `g++ programmet.cpp` ska lösa allt åt dig, då är du fel ute för det gör det inte. Kräv i så fall hellre att programmet startas från dess mapp med ./namnet, det är inte så farligt för ett nybörjarprogram. Det är bättre att vara ärlig än att låtsas att man vet vad man håller på med.
__________________
Senast redigerad av lennart.lyktstolpe 2018-09-23 kl. 16:58.
Citera
2018-09-23, 19:34
  #3065
Medlem
erikp121s avatar
Citat:
Ursprungligen postat av lennart.lyktstolpe
Låt mig vara lite bråkig och förklara hur jag resonerar, mest för att det är kul att diskutera programmering.

Då du ville utöka programmet till att kunna anropas från andra ställen, istället för att skriva ./namnet jämt, och t.o.m. har en datamapp åt det, uppfattade jag det som att det var på väg från ett testprogram till något du ville utveckla längre, ett "riktigt program". Och nybörjarargumentet faller också eftersom alla mina lösningar var enklare än koden du klistrade in i main().

Jag gillar program som man inte behöver installera. Som man kan testa, lägga undan dess mapp någonstans, och radera när man vill utan att det har kladdat ner datorn. Men på *nix kommer dina användare ändå behöva kompilera om binärfilen, och då tycker jag att du lika gärna kan göra det ordentligt:

Förklara i README-filen att han/hon ska placera data-mappen på valfritt ställe, eller bara låta den ligga kvar om hela programmets mapp redan ligger på ett lämpligt ställe som t.ex. ~ för en enskild användare, och kompilera med -DDATA_DIR=/sökväg/till/den.

Då har du undvikit en installation som stoppar upp saker där användaren inte vill ha dem, och istället gett honom/henne kontrollen. Att människan ett halvår senare när han önskar avinstallera programmet, har glömt var han lade mappen och inte har kvar README-filen, tycker jag är hans problem.

Duger inte detta, utan du vill att bara `g++ programmet.cpp` ska lösa allt åt dig, då är du fel ute för det gör det inte. Kräv i så fall hellre att programmet startas från dess mapp med ./namnet, det är inte så farligt för ett nybörjarprogram. Det är bättre att vara ärlig än att låtsas att man vet vad man håller på med.
Okej, jag försöker implementera -D.

main.cpp:

makefile:

pwd && ls -al:

Som jag tolkar det är dataPath en tom sträng just nu? Därför får jag "Unable to open file...". Hur gör jag för att sökvägen i -DTEST_DATA_PATH=/path/to/test/data/test.txt sätts in i en sträng-variabel i källkoden?
Citera
2018-09-26, 00:07
  #3066
Avstängd
Citat:
Ursprungligen postat av erikp121
Som jag tolkar det är dataPath en tom sträng just nu? Därför får jag "Unable to open file...". Hur gör jag för att sökvägen i -DTEST_DATA_PATH=/path/to/test/data/test.txt sätts in i en sträng-variabel i källkoden?

https://stackoverflow.com/q/3419332

Kod:
#define QUOTE(str) #str
#define EXPAND_AND_QUOTE(str) QUOTE(str)

dataFile.open(EXPAND_AND_QUOTE(TEST_DATA_PATH));

Eller skippa makrona och kompilera med -DTEST_DATA_PATH=\"/path/to/test/data/test.txt\" eller '-DTEST_DATA_PATH="/path/to/test/data/test.txt"', beroende på hur du föredrar att escape:a " i skalet. Det alternativet är antagligen bättre, men jag vet inte vilket av dem som är common practice. Preprocessormakron brukar kunna bita en i stjärten.

Kod:
dataFile.open(TEST_DATA_PATH);

Och att lagra det i en variabel som andra konstanter är bara bra, så slipper du få strängen lite överallt i källkoden, visserligen den preprocessade men nån stackare kanske ska sitta och läsa den nån gång också. Så...

Kod:
std::string dataPath = TEST_DATA_PATH; /* eller hur man nu initialiserar en C++-string */

Försöker användaren kompilera utan -D... får de ett felmeddelande att TEST_DATA_PATH är odeklarerad, och det kan de gott ha.
Citera
2018-09-26, 00:24
  #3067
Avstängd
Citat:
Ursprungligen postat av erikp121
Kod:
    if (dataFile.is_open()){
        std::string line{""};
        while (std::getline(dataFile,line)){
            std::cout << line << "\n";
        }
        dataFile.close();
    }
    else {
        std::cout << "Unable to open file...\n";
    }

Skriv output till stdout, men meddelanden inklusive fel till stderr (std::cerr). På det viset får användare som pipear eller redirectar programmets output fortarande felmeddelandet på skärmen. Istället för att "Unable to open file..." matas vidare respektive lagras som ett slags resultat utan att han/hon vet om det.
Citera
2018-09-26, 04:46
  #3068
Medlem
erikp121s avatar
Tack jag hittade \"-grejen. Först försökte jag med endast citationstecken, men då gav det fel vid kompilering.

Ja, std::cerr ska man väl använda. Kollade under gårdagen på argument till main och där användes std::cerr när programmet inte fått tillräckligt många argument vid uppstart.

Man får ta ett steg i taget.

Gjorde ju min första makefil i förr-förrgår så jag är ganska ny med allt. Raden i test.txt skriver Hello World. Den nivån.
Citera
2018-09-26, 06:42
  #3069
Avstängd
Citat:
Ursprungligen postat av erikp121
Tack jag hittade \"-grejen. Först försökte jag med endast citationstecken, men då gav det fel vid kompilering.

Ja, std::cerr ska man väl använda. Kollade under gårdagen på argument till main och där användes std::cerr när programmet inte fått tillräckligt många argument vid uppstart.

Man får ta ett steg i taget.

Gjorde ju min första makefil i förr-förrgår så jag är ganska ny med allt. Raden i test.txt skriver Hello World. Den nivån.

Varsågod. Om det inte redan har märkts är jag kåt på kompatibilitet, korrekthet, portabilitet, standardisering, syntax och säkerhet, så det är i princip det jag kan tillföra. Och det finns ett behov av det, kanske inte så stort som jag vill tro för en bugg är inte hela världen, men ändå. Sen uttrycker jag mig lite dåligt ibland, men jag letar inte fel hos andra för att nedvärdera på något sätt.

Det jag egentligen blir förbannad på är alla webbsajter som postar rent trams. Stack Overflow är bra på det sättet, i C/C++ finns det användare som Jonathan Leffler och en svensk som jag inte kommer på namnet på, som uttrycker sig så korrekt att jag tror att jag har kommit till himlen.

Internet är ett träsk, och som intresserad och talangfull programmerare bör du lägga ett par timmar på att hitta de bästa böckerna och sidorna. Istället för en svensk lärare som tycker det är vettigt att avsluta inmatningen av tal med att skriva 0. Nån måste rensa upp det här och det verkar vara jag som måste göra det. -- Fritt från "Den Flyende Mannen" av Stephen King.
__________________
Senast redigerad av lennart.lyktstolpe 2018-09-26 kl. 06:45.
Citera
2018-09-27, 19:42
  #3070
Medlem
erikp121s avatar
Citat:
Ursprungligen postat av lennart.lyktstolpe
Varsågod. Om det inte redan har märkts är jag kåt på kompatibilitet, korrekthet, portabilitet, standardisering, syntax och säkerhet, så det är i princip det jag kan tillföra. Och det finns ett behov av det, kanske inte så stort som jag vill tro för en bugg är inte hela världen, men ändå. Sen uttrycker jag mig lite dåligt ibland, men jag letar inte fel hos andra för att nedvärdera på något sätt.

Det jag egentligen blir förbannad på är alla webbsajter som postar rent trams. Stack Overflow är bra på det sättet, i C/C++ finns det användare som Jonathan Leffler och en svensk som jag inte kommer på namnet på, som uttrycker sig så korrekt att jag tror att jag har kommit till himlen.

Internet är ett träsk, och som intresserad och talangfull programmerare bör du lägga ett par timmar på att hitta de bästa böckerna och sidorna. Istället för en svensk lärare som tycker det är vettigt att avsluta inmatningen av tal med att skriva 0. Nån måste rensa upp det här och det verkar vara jag som måste göra det. -- Fritt från "Den Flyende Mannen" av Stephen King.
Ja, jag får tacka för hjälpen. Macro var något helt nytt och nyttigt att lära sig. Så här ser main.cpp ut nu när jag är klar med det delprojektet (filläsning med std::getline):


Inmatning försöker jag alltid att avsluta med "exit" eller "quit", men har inte jobbat så mycket med std::stoi (string to int). Får fel när en sträng inte går att konvertera till ett heltal. Skriver jag exempelvis något annat än "exit" och som inte är ett heltal (t.ex. "test") i följande kod jag knåpade ihop precis så får jag "terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi
Aborted (core dumped)"


Förmodligen finns det något snitsigt sätt att kolla av om en sträng går att konvertera till en int och lägga det som condition i if-satserna.

Tillägg:
Hittade try och catch (Exception handling) som verkar fungera bra.

https://stackoverflow.com/questions/...-function-in-c
__________________
Senast redigerad av erikp121 2018-09-27 kl. 20:15.
Citera
2018-09-28, 00:11
  #3071
Medlem
Sprutnarkomans avatar
Citat:
Ursprungligen postat av erikp121
Ja, jag får tacka för hjälpen. Macro var något helt nytt och nyttigt att lära sig.

Ja, men det är basically textersättning och ställer på det viset lätt till problem, därför undviker man det så mycket som möjligt i det modernare språket C++ och har istället satsat på riktig const-funktionalitet och inline-funktioner.

Ju mindre man använder preprocessorn desto bättre skulle jag väl säga, men i C har man ofta inget val för att case i switchsatser inte tar variabler även om de är const, det krävs en numerisk konstant, och detsamma gäller storleken när man definierar arrayer.

Det vanligaste misstaget är att man inte parentesar in uttrycken, det näst vanligaste är sidoeffekter.

Kod:
#define TRIPLE(n) n * 3

cout << TRIPLE(myvar + 1);

...blir `cout << myvar + 1 * 3`, p.g.a. operatorprioriteten inte det man förväntade sig. Det korrekta är att parentesa både argumenten och hela uttrycket för att skydda dem:

Kod:
#define TRIPLE(n) ((n) * 3)

Kod:
#define SQUARE(n) ((n) * (n))

cout << SQUARE(n++);

...ökar n två gånger, en liten kinderöverraskning. Beteendet (eller resultatet, kommer inte ihåg) för `n++ * n++`, som går att tolka på fler sätt än ett, är t.o.m. odefinierat.

Citat:
Ursprungligen postat av erikp121
Inmatning försöker jag alltid att avsluta med "exit" eller "quit"

Avsluta när strömmen tar slut, dvs när du får EOF på den. Om strömmen, i det här fallet stdin, är kopplad till en fil sker det när filen är slut. Om den är kopplad till en pipe när datat från det andra programmet är slut. Om det är en nätverksuppkoppling när den andra sidan stänger den. Om den är kopplad till en terminal, som stdin är by default, trycker du Ctrl-D för att markera EOF, Ctrl-Z på Windows.

Det har varit det standardiserade sättet att göra det på sen 70-talet, men Windowsanvändare tycker Ctrl är jobbigt, i vissa fall Alt, och det är ett under att de fixar att använda Shift när man kan trycka Caps-Lock en gång för alla. Skämt åsido, jag kan förstå att folk inte orkar lära sig såna här tangentkombinationer, men då är det i så fall MS uppgift att lägga en liten knapp i terminalfönstret för det. Inte alla världens programs uppgift att skyla över bristerna i deras skit-OS.

De flesta, möjligen alla men orkar inte kolla dokumentation, funktioner för läsning i C returnerar ett felvärde när EOF inträffar. Sen anropar man feof(strömmen) för att skilja EOF från de andra fel som kan inträffa (om man nu räknar EOF som ett fel). I C++ har jag för mig att man först läser med <<, sen kollar med nästa instruktion om EOF är satt på strömobjektet, t.ex. cin.

Kod:
for (;;) {
    /* läs en rad från stdin */
    if (/* det blev EOF */) break;
    /* konvertera till heltal */
    /* gör något med heltalet */
}

Anropa programmet t.ex. så här i *nix:

Kod:
makestats # in från terminalen, ut till terminalen
makestats < baseballscores.txt > baseballstats.txt # in från en fil, ut till en annan
printf '90\n34\n54\n6\n' | makestats
primes 0 1000 | makestats | mail -s 'Primtalsstatistik' mamma@jobbet.se

MS vill fördumma användarna så att de kan fortsätta sälja sina färdiga produkter. Om användarna tog makten över datorn, istället för tvärtom, skulle de vara slut.
__________________
Senast redigerad av Sprutnarkoman 2018-09-28 kl. 00:16.
Citera
2018-09-28, 01:42
  #3072
Medlem
erikp121s avatar
Citat:
Ursprungligen postat av Sprutnarkoman
...
Tack för upplysningen. Gäller att ha koll på allt med preprocessorn och tipset med parenteser och att försöka undvika Macron hjälper.

Har inte så stor koll på pipes, inmatning och utmatning så där finns en stor kunskapslucka att fylla. På sin höjd har jag gjort ./a.out >/dev/null när jag "benchmarkade" att räkna till 100 000 000 i C, C++, Java och Python. Även lspci | grep VGA och liknande så jag förstår konceptet någorlunda.

Just i stoi-fallet så är det bara ett testprogram (som alla andra) med en loop som jag avslutar med exit. Det är alltså inmatning från användaren (interaktivt) som jag brukar avsluta med exit eller quit. Skulle jag i framtiden jobba tillsammans med andra program så gäller det att jag har koll på stdin/out, EOF, pipes m.m.

Jag är rätt nöjd med dagen. Lärde mig try och catch åtminstone!

"Slutprodukt":
main.cpp
get_value.cpp
print_result.cpp
my_division.cpp

Något som är skumt i my_division.cpp är att om jag vänder om i den första if-satsen till "((x % y != 0) && (y != 0))" så får jag floating point exception / illegal instruction när jag dividerar med 0 (y == 0). Borde det inte vara exakt lika i och med &&-operatorn?
Citera

Stöd Flashback

Flashback finansieras genom donationer från våra medlemmar och besökare. Det är med hjälp av dig vi kan fortsätta erbjuda en fri samhällsdebatt. Tack för ditt stöd!

Stöd Flashback