Checking command line inputs for errors in C++ - c++

I had this program working just a bit ago but changed something and now my error handling has gone bonkers. I'm practically hitting my head against the wall here trying to put it back the way it was, but no matter what I do now, it spits out an error.
The program is supposed to take command line arguments to define the rows and columns and create a dynamic 2D array based on that. The format is "-rows (number) -columns (number)". I tried to add a few more cases before I turned in the assignment, but I must have changed some logic elsewhere, because even after I removed the new part it still won't work no matter what I input. At this point I think I just need a pair of fresh eyes to point me in the right direction.
int main(int argc, char* argv[]) {
// Checks if the user input the correct number of arguments.
// If so, checks if they were input corecctly. If so, it assigns
// the user input values to rows/columns, and if not, prints
// an error message.
if(argc == 5) {
for(int i = 1; i < argc; i++) {
rows = getArg(argc, argv, i, compare1);
}
for(int i = 1; i < argc; i++) {
columns = getArg(argc, argv, i, compare2);
}
} else {
printError(argv);
}
That is the relevant part of main.
Below are the functions involved in checking for errors. The one I actually have been working on is getArg, so I'm assuming this is where the logic is failing, but I included the other necessary function for clarity.
// Description: Checks if user input was valid
// Parameters: Command line arguments, int i from the for
// loop used to run this check on all command line arguments
// in main, and an array of chars used to compare the user's
// inputs to "-rows" or "-columns"
// Return value: If user input was valid, returns an int
// If not, exits program.
int getArg(int argc, char* argv[], int i, char compare[]) {
int arg;
if (strcmp(argv[i], compare) == 0) {
if (isInt((i + 1), argv)) {
arg = atoi(argv[i + 1]);
} else {
printError(argv);
}
} else {
printError(argv);
}
return arg;
}
// Description: Checks user input for valid integers
// Parameters: Command line arguments
// Return value: Returns true if input is an int;
// false if not.
bool isInt(int argc, char* argv[]) {
bool isInt;
for (int j = 0; j < strlen(argv[argc]); j++) { //For loop runs through each char in the array at argc
if (isdigit(argv[argc][j])) { // Checks to see if char is an integer
isInt = true;
return isInt;
} else {
isInt = false; // If there is ever a non-integer character, exit loop and return false
return isInt;
}
}
}

The first for loop in main continues to scan the argument list after it has matched "-rows". The loop makes the following calls (I'm assuming compare1="-rows" here since you didn't mention it):
i=1: getArg(argc=5, argv={"a.out", "-rows", "2", "-columns", "3"}, i=1, compare="-rows")
returns 2
i=2: getArg(argc=5, argv={"a.out", "-rows", "2", "-columns", "3"}, i=2, compare="-rows")
calls printError(argv) because strcmp("2", "-rows") is nonzero
Also, as Abhishek mentioned, isInt only ever checks the first character of the string because you return in the isInt = true branch.

One possible error that I can spot is that you are not looping correctly in your isInt() function.
You would invariably return out of the function after the 1st iteration.

Its nice you got an answer, but this is one is pretty easy to use and on entire SO you'll find this suggestion wherever such questions are tagged for C++
Using boost::program_options
#include <iostream>
#include <boost/program_options.hpp>
namespace po = boost::program_options;
int main( int argc, char *argv[ ] )
{
try {
int rows,cols;
po::options_description desc("Allowed options");
desc.add_options()
( "help", "produce this help message" )
( "rows", po::value< int>(&rows)->required(), "No. of Rows" )
( "cols", po::value< int>(&cols)->required(), "No. of Cols" )
;
po::variables_map vm;
po::store( po::parse_command_line( argc, argv , desc ), vm );
po::notify( vm );
if ( vm.count( "help" ) )
{
std::cout << desc;
return 0;
}
std::cout<<"Rows :"<<rows<<" "<<"Cols :"<<cols<<std::endl;
}
catch( std::exception& e )
{
std::cout << e.what() << "\n";
return 1;
}
return 0;
}
Usage :
./test --cols 4 --rows 3
Outputs :
Rows :3 Cols :4
Good Tutorial Here

Related

Batch one line to call an executable using arguments from file

For convenience, I have renamed all the files to simple names for my example.
I'm trying to run an executable (test.exe), with a C++ entrypoint int main(int argc, char* argv[]) from a batch file (test.bat), and pass arguments from a text file (test.txt). The end goal is to run unit tests on an SDK using the testing software (test.exe).
My issue is that I do not want to have to use a variable when I call the executable since it makes the code harder to read :
rem This works
set /p test_input=<test.txt& call test.exe %test_input%
After some research, I figured I should use input redirection like so :
rem This does not work
call test.exe < test.txt
This does not work, and I don't understand why.
This is what I initially tried, and it has been suggested before on SO (here).
I have access to the test.exe code, so I can print argc and argv :
int main(int argc, char* argv[])
{
if(new_argc >= 2)
{
if(strcmp("-help", argv[1]) ==0)
{
show_help();
return 0;
}
for(int i=1; i < argc; i++)
{
if(strcmp("-framerate", argv[i]) ==0)
{
i++;
if(i < argc)
{
FrameRate = (float)atof(argv[i]);
}
else
{
std::cerr << "Parameters error" << std::endl;
return 0;
}
} else if ...
{
...
}
}
}
}
If I enter the arguments and parameters manually, it works as expected.
test.txt
-arg1 param1 -arg2 param2 ...
test.bat
call test.exe < test.txt
Output : test.exe runs as if there are no arguments or parameters.
Edit :
Added a few details about the entrypoint and renamed the batch variable.
Thanks to the comments under my question, I was pushed in the right direction.
The problem was my understanding of <. It literally means "Read file to STDIN" (as mentionned here). Many other documentation sites give vague definitions like (as mentionned here)
command < filename : Type a text file and pass the text to command
I need to parse the input correctly, since stdin isn't available in argc or argv, but through std::cin.
My batch code and text file remain unchanged, and I want to maintain the same form of parsing to avoid rewriting multiple projects, so I split the input string using the Solution 1.3 from here (slightly modified) and created a new_argv.
std::vector<char*> split(const std::string& s, char delimiter)
{
std::vector<char*> tokens;
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, delimiter))
{
tokens.push_back(_strdup(token.c_str()));
}
return tokens;
}
int main(int argc, char* argv[])
{
std::string extra_input; // Variable to store the contents of test.txt
std::getline(std::cin, extra_input); // Recuperate the contents of test.txt
std::vector<char*> new_argv = split(extra_input, ' '); // Split the args
for(int i = argc - 1; i >= 0; i--)
new_argv.insert(new_argv.begin(), argv[i]); // Add the original args to the beginning
const size_t new_argc = new_argv.size(); // Create the new argc based on the final argument list (vector)
if(new_argc >= 2)
{
if(strcmp("-help", new_argv[1]) ==0)
{
show_help();
return 0;
}
for(int i=1; i < new_argc; i++)
{
if(strcmp("-framerate", new_argv[i]) ==0)
{
i++;
if(i < new_argc)
{
FrameRate = (float)atof(new_argv[i]);
}
else
{
std::cerr << "Parameters error" << std::endl;
return 0;
}
} else if ...
{
...
}
}
}
// Important, don't forget to free the memory used by the _strdup
for(int i=1; i < new_argc; i++)
{
if(i >= argc)
free(new_argv[i]);
}
}
test.bat
call test.exe < test.txt
test.txt
-arg1 param1 -arg2 param2 ...
Of course, I need to add some checks to make it properly handle whitespace, but that's the gist of it. Thank you for your help and external point of view.
Edit : Fixed a mistake in the code.

My c++ loop doesn't stop when i tell it to do it through the console

I have made a loop which should encrypt the phrases I tell it to, but didn't finish because of the problem. It should detect when I say "stop" in the console and shut down the loop. It doesn't work.
What i want it to do is to detect if i said stop and break the loop. I shouldn t get any random missfires from getting the letters s t o p from other words. As you can see, every time there is a letter out of order, it resets the vectors which locks all of the ifs until 'c' gets the correct letters in the correct order.
using namespace std;
int main()
{
char c,v[5];
int i=0;
while(i!=1)
{
cin.get(c);
if(c=='s' or v[1]=='s')
{
v[1]='s';
if(c=='t' or v[2]=='t')
{
v[2]='t';
if(c=='o' or v[3]=='o')
{
v[3]='o';
if(c=='p' or v[4]=='p')
{
v[4]='p';
v[1]=v[2]=v[3]=v[4]=0;
i=1;
}
else
v[1]=v[2]=v[3]=0;
}
else
v[1]=v[2]=0;
}
else
v[1]=0;
}
cout<<c;
if (i==1)
break;
}
return 0;
}
That should the work and is not indented hell code. It assumes that you are entering one character at a time.
#include <iostream>
int main(int argc, char const *argv[])
{
char keyword[] = "stop";
char* matching_char = keyword;
char char_from_user;
while(*matching_char != '\0')
{
std::cin.get(char_from_user);
// Reset if different character
if(*matching_char != char_from_user)
matching_char = keyword;
// Increment position of match
if(*matching_char == char_from_user)
++matching_char;
// Ignore rest in buffer
std::cin.ignore();
}
return 0;
}
Following your logic, you just need to assign the v array values after each if/else condition otherwise it will just get immediately reassigned to 0. For example, you first assign v[1] = 's', and then right after you assign it to v[1] = 0, because the if returns false in first iteration. The following code should solve the problem.
#include <iostream>
using namespace std;
int main()
{
char c,v[5];
int i=0;
while(i!=1)
{
cin.get(c);
if(c=='s' || v[1]=='s')
{
if(c=='t' || v[2]=='t')
{
if(c=='o' || v[3]=='o')
{
if(c=='p' || v[4]=='p')
{
v[4]='p';
v[1]=v[2]=v[3]=v[4]=0;
i=1;
}
else
v[1]=v[2]=v[3]=0;
v[3]='o';
}
else
v[1]=v[2]=0;
v[2]='t';
}
else
v[1]=0;
v[1]='s';
}
if (i==1)
break;
}
return 0;
}

C++ invalid conversion from 'char' to 'const char'

I am having trouble with a two-dimensional array comparison. I need to create a pseudo login system that asks the user for a username and password and then compares the input to a predefined list of usernames.
In the function, the predefined usernames are represented by the *s and the user input is *s1. When I try to compile it, this pesky trouble-maker appears:
68 D:\Personal\Dev-Cpp\projects\loginSysTest\main.cpp
invalid conversion from char' toconst
regarding the strncmp function in the if statement.
This is the code:
#define nameLenght 30
#define User 10
char usernames[User][User] = {{"student"}, {"admin"}, {"Deus Ex Machina"}};
//=====================================================================
int main(int argc, char *argv[])
{
char usernameInput[nameLenght + 1] = {0};
gets(usernameInput);
int login = compS(*usernames, usernameInput);
if(login == 0)
printf("Access Granted! \n");
else
printf("Access Denied!");
system("PAUSE");
return 0;
}
//=====================================================================
int compS(char *s, char *s1)
{
for(int k = 0 ;k < nameLenght; k++)
{
if(strncmp(s[k], s1, strlen(s1)) == 0)
return 1;
}
}
Thank you in advance.
Just use std::vector and std::string along the lines of:
std::vector<std::string> usernames({"student", "admin", "Deus Ex Machina"});
std::string input;
std::cin >> input;
if (std::find(begin(usernames), end(usernames), input) != end(usernames))
std::cout << "Access Granted!\n";
else std::cout << "Access Denied!\n";
Live demo
If you want (or need) to keep your C-style not-pretty-nor-very-safe code, you could write
strncmp(s + (k+nameLenght), s1, strlen(s1))
That would compile and perhaps work with some more efforts, but you'll have to correct the usernames sizes and actually call compS.
See that other answer for a safer C++-styled code.
There are a few ways you can accomplish this. This is the first way that comes to mind.
char* usernames[] = { "student" , "admin", "Deus Ex Machina", NULL };
int compS(char **s, char *s1)
{
for (int k = 0; s[k] != NULL; k++)
{
if (strncmp(s[k], s1, strlen(s1)) == 0)
return 0;
}
return 1;
}
You have to pass a pointer to a string to strncmp() so I changed your array to an array of pointers to strings. The array has a null pointer at the end to allow the loop to know when it has reached the end.

Can main function have default argument values?

How can I put the default values for main function arguments like the user defined function?
Well, the standard says nothing which prohibits main from having default arguments and say you've successfully coalesced the compiler to agree with you like this
#include <iostream>
const char *defaults[] = { "abc", "efg" };
int main(int argc = 2, const char **argv = defaults)
{
std::cout << argc << std::endl;
}
Live example. It compiles with no errors or warnings, still it's useless; a futile experiment. It almost always would print 1.
Every time you invoke the program, say, with no arguments (or any number of arguments for that matter), argc gets set to 1 and argv[0] points to the program name, so doing it is pointless i.e. these variables are never left untouched and hence having defaults makes little sense, since the defaults would never get used.
Hence such a thing is usually achieved with local variables. Like this
int main(int argc, char **argv)
{
int const default_argc = 2;
char* const default_args[] = { "abc", "efg" };
if (argc == 1) // no arguments were passed
{
// do things for no arguments
// usually those variables are set here for a generic flow onwards
argc = default_argc;
argv = default_args;
}
}
I think you want to do two different things for the following cases.
When no arguments are passed
When arguments are passed.
Here is how you do it.
int main(int argc, char *argv[])
{
if(argc == 1)
{
// case #1
}
else
{
// case #2
}
}
Using argc and argv? Thoses will pass argument from the command line to your program. You can't really use default arguments. You have to pass them during the call to your program like this :
$> ./my_addition "4" "7"
int main(int argc, char *argv[])
{
// argc <=> 'argument count' (=3)
// argv <=> 'argument vector' (i.e. argv[1] == "4")
// argv[0] is usually the bin name, here "my_addition"
for (int i = 0; i < argc; ++i)
std::cout << argv[i] << std::endl;
return (0);
}
Maybe you could use a script to run your program, this could maybe be the closest solution to default argument for main().
exec_my_prog.sh:
#!/bin/zsh
call_your_program + very_meny_args
And calling ./exec_my_prog.sh would run your program with the "default" arguments.

Checking arguments in terminal

How can I write a program to check the arguments in the terminal are correct?
For example, if I have a program hello.cpp and I want to call it as:
./hello yes 10
I want the program to make sure that the first argument is yes or no and the second argument is a number between 1-10. So how can I read these arguments into my program to do the checking?
Thanks!
Command line arguments are passed as a count and individual strings in the argc and argv arguments to main().
int main(int argc, char *argv[])
{
...
}
Simply check the value in argc and the strings in argv for the appropriate values.
You meant to execute like this, ./hello yes 10
there is an option argc and argv in c
where argc is the number of arguments passed and argv with the index shows the argument passed itself.
Take a look at the below code for iterating through all arguments.
int main(int argc, char *argv[]){
int i = 0;
for (i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
As mentioned by other users, The main function is the entry point of your program, and the way it gets data from the command line is through its parameters.
The first int argument is the count of all the arguments passed, including the program name, the second char ** argument is a pointer to each parameter passed, including the program name:
int main
(
int argc, // <-- how many parameters has been provided?
char **argv, // <-- what values has each parameter?
)
{
...
return 0;
}
So, knowing that, your call ./hello yes 10 must be like that:
argc = 3
argv[0] = "./hello"
argv[1] = "yes"
argv[2] = "10"
The names argc and argv are just a convention, you can name them at your pleasure, but it's a good practice to keep the names that everyone are used for.
And the argument doesn't are forced to be int, char ** they must follow a quite rigid convention, borrowed from this answer:
It shall have a return type of type int, but otherwise its type is implementation-defined. All implementations shall allow both the following definitions of main: int main() and int main(int argc, char* argv[])
Knowing that, let's focus on your question:
First of all, you must ensure that 2 arguments are passed, so you must check the argc value and ensure that equals exactly 3.
the first argument is yes or no
Next, you must store your argv[1] (because 0 contains the program name) into a string and compare it with the values "yes" and "no":
std::string YesOrNo(argv[1]);
if (YesOrNo == "yes" || YesOrNo == "no")
And finally, you must store your argv[2] into an integer and check if it is equal or less to 10:
std::stringstream Stream;
int Value = 0;
Stream << argv[2];
Stream >> Value;
if (Value <= 10)
So, the result is:
int main(int argc, char **argv)
{
if (argc == 3)
{
std::string YesOrNo(argv[1]);
if (YesOrNo == "yes" || YesOrNo == "no")
{
std::stringstream Stream;
int Value = 0;
Stream << argv[2];
Stream >> Value;
if (Value <= 10)
{
// Your stuff..
}
}
}
return 0;
}
I let you deal with all the uppercase and lowercase stuff and the false positives with the numeric argument, at least I'm not going to do all your homework ;)