POV-Ray : Newsgroups : povray.off-topic : My first C++ program Server Time
30 Sep 2024 15:21:59 EDT (-0400)
  My first C++ program (Message 1 to 10 of 149)  
Goto Latest 10 Messages Next 10 Messages >>>
From: Invisible
Subject: My first C++ program
Date: 19 Sep 2008 08:03:59
Message: <48d3952f$1@news.povray.org>
(Yeah, I realise nobody else is going to care...)



#include <iostream>
#include <vector>
#include <string>

using namespace std;

bool StackCheck(vector<int> stack, int min)
{
   if (stack.size() >= min) return true;

   cout << "Stack underflow." << endl;
   return false;
}

void Add(vector<int>& stack)
{
   if (!StackCheck(stack, 2)) return;

   int x = stack.back(); stack.pop_back();
   int y = stack.back(); stack.pop_back();
   stack.push_back(x + y);
}

void Sub(vector<int>& stack)
{
   if (!StackCheck(stack, 2)) return;

   int x = stack.back(); stack.pop_back();
   int y = stack.back(); stack.pop_back();
   stack.push_back(x - y);
}

int main()
{
   cout << "Calc #01" << endl;

   vector<int> stack;
   string cmd;

   while (true)
   {
     for (int i=0; i<stack.size(); i++)
     cout << "[" << i << "]: " << stack[i] << endl;

     cout << "Calc> ";
     cin  >> cmd;

     if (cmd == "^") break;
     if (cmd == "+") {Add(stack); continue;}
     if (cmd == "-") {Sub(stack); continue;}
     stack.push_back(1);
   }

   cout << "Exit." << endl;
}



Almost unbelievably, even though this program is written in C++ and 
involves I/O, it seems to work correctly. I'm sure anybody who actually 
knows how C++ is supposed to work will have a good laugh at this, but I 
learned a few things by doing it.

The first mistake was passing the stack by value - which, obviously, 
fails miserably. The next thing I tried was returning the new stack to 
the caller, but that didn't work either. (Much like C, C++ compiler 
errors seem rather cryptic. Plus I'm running GCC under QEMU, so it takes 
about 60 seconds for each compiler run!)

At present, I haven't figured out how to convert a string to an integer 
yet, so this "calculator" is pretty much useless. I'm sure it'll be 
explained later in the tutorial. I also find myself constantly cursing 
that I have to write an entire 5 lines of (nearly identical) code for 
every arithmetic operation I want to implement and there's no way to 
abstract this. (I don't have copy & paste available!)

Also, there's an interesting glitch where the program won't continue if 
you enter a blank line - you must enter *something* or the program won't 
continue. Presumably this is just the defined behaviour of the >> 
operator...

Still, I have a program that reads user input without segfaulting. 
That's gotta be pretty impressive!

PS. Nano is a horrid, horrid text editor!


Post a reply to this message

From: Invisible
Subject: (And in Haskell. Obviously.)
Date: 19 Sep 2008 08:20:08
Message: <48d398f8$1@news.povray.org>
Invisible wrote:

> #include <iostream>
> #include <vector>
> #include <string>
> 
> using namespace std;
> 
> bool StackCheck(vector<int> stack, int min)
> {
>   if (stack.size() >= min) return true;
> 
>   cout << "Stack underflow." << endl;
>   return false;
> }
> 
> void Add(vector<int>& stack)
> {
>   if (!StackCheck(stack, 2)) return;
> 
>   int x = stack.back(); stack.pop_back();
>   int y = stack.back(); stack.pop_back();
>   stack.push_back(x + y);
> }
> 
> void Sub(vector<int>& stack)
> {
>   if (!StackCheck(stack, 2)) return;
> 
>   int x = stack.back(); stack.pop_back();
>   int y = stack.back(); stack.pop_back();
>   stack.push_back(x - y);
> }
> 
> int main()
> {
>   cout << "Calc #01" << endl;
> 
>   vector<int> stack;
>   string cmd;
> 
>   while (true)
>   {
>     for (int i=0; i<stack.size(); i++)
>     cout << "[" << i << "]: " << stack[i] << endl;
> 
>     cout << "Calc> ";
>     cin  >> cmd;
> 
>     if (cmd == "^") break;
>     if (cmd == "+") {Add(stack); continue;}
>     if (cmd == "-") {Sub(stack); continue;}
>     stack.push_back(1);
>   }
> 
>   cout << "Exit." << endl;
> }

module Main (main) where

main = do
   putStrLn "Calc #01"
   main_loop []

main_loop stack = do
   mapM_ (\(n,v) -> putStrLn $ "[" ++ show n ++ "]: " ++ show v) (zip 
[0..] stack)

   putStr "Calc> "
   cmd <- getLine

   case cmd of
     "^" -> return ()
     "+" -> binary (+) stack
     "-" -> binary (-) stack
     _   -> main_loop (1:stack)

binary fn stack = case stack of
   x:y:zs -> do main_loop ((fn x y) : zs)
   _      -> do putStrLn "Stack underflow."; main_loop stack


Somehow... it seems so much easier in Haskell. ;-)


Post a reply to this message

From: scott
Subject: Re: My first C++ program
Date: 19 Sep 2008 08:23:31
Message: <48d399c3@news.povray.org>
> I also find myself constantly cursing that I have to write an entire 5 
> lines of (nearly identical) code for every arithmetic operation I want to 
> implement and there's no way to abstract this. (I don't have copy & paste 
> available!)

What you could do, is have a function like DoOperation(stack,op), and use 
"op" as a token for the operation.

So you would call it like:

     if (cmd == "+") {DoOP(stack,0); continue;}
     if (cmd == "-") {DoOP(stack,1); continue;}
     if (cmd == "/") {DoOP(stack,2); continue;}
     if (cmd == "*") {DoOP(stack,3); continue;}

Or you could even use some fancy look-up table to convert from the character 
code to op number, or just use the ASCII code of the operator as the op 
number... you get the idea.

Then, DoOperation() would look like:

   if (!StackCheck(stack, 2)) return;

   int x = stack.back(); stack.pop_back();
   int y = stack.back(); stack.pop_back();
   int answer;
   if(op==0) answer=x+y;
   if(op==1) answer=x-y;
   if(op==2) answer=x/y;
   if(op==3) answer=x*y;
   stack.push_back(answer);

I'm sure there's a better way of doing it, but I think that's less work than 
your first version.

PS good work on the first C++ program, it's way cooler than any of my first 
programs.


Post a reply to this message

From: Invisible
Subject: Re: My first C++ program
Date: 19 Sep 2008 08:27:49
Message: <48d39ac5$1@news.povray.org>
scott wrote:

> What you could do, is have a function like DoOperation(stack,op), and 
> use "op" as a token for the operation.

In other words, move the operation check to the place where all the 
values are available. I guess that could work.

> PS good work on the first C++ program, it's way cooler than any of my 
> first programs.

I figured copying out HelloWorld.cpp didn't count is "MY first program". ;-)

Warp gave me the link of Tuesday, and I have now written working code of 
my own design, not based on any example code. This would have been quite 
impossible in C. (Unless you avoid I/O, anyway.) So it seems they did 
make C++ at least a little easier. ;-)


Post a reply to this message

From: Warp
Subject: Re: My first C++ program
Date: 19 Sep 2008 08:59:40
Message: <48d3a23c@news.povray.org>
Invisible <voi### [at] devnull> wrote:
> using namespace std;

  Don't do that. It might feel tempting, but don't. It's better in the long
run to avoid it.

> bool StackCheck(vector<int> stack, int min)

  You are passing the vector by value here, which may not be a good idea,
especially if the vector is big. That's because the entire vector will be
copied. (Some STL implementations *might* use copy-on-write, but most of
them don't, and instead just deep-copy.)

  Teach yourself to intuitively write:

bool StackCheck(const std::vector<int>& stack, int min)

  Usually with big objects (such as vectors) you basically always want
to pass by const reference rather than by value. (You should pass by
value only if that's *really* what you want to do.)

  Sure, this might sound like a nuisance, but C and C++ are value based
languages, and by default everything is passed by value. To pass by
reference (or by pointer) you need additional syntax.

>    int x = stack.back(); stack.pop_back();
>    int y = stack.back(); stack.pop_back();
>    stack.push_back(x + y);

  You could also save a bit of writing by doing it like this:

    int x = stack.back(); stack.pop_back();
    stack.back() += x;

  Or if you want to really minimize things:

    stack.at(stack.size()-2) += stack.back();
    stack.pop_back();

  Of course it starts being a bit obfuscated.

> int main()
> {
>    cout << "Calc #01" << endl;
...
>    cout << "Exit." << endl;
> }

  While it is perfectly valid to omit the return value of main()
(IIRC it defaults to EXIT_SUCCESS), it's generally recommended to
return EXIT_SUCCESS or EXIT_FAILURE (which are defined in some header
file).

  Having said that, when I write very small test programs like this one,
I don't bother writing any return command in main() either. So it's not
that bad. :P

> At present, I haven't figured out how to convert a string to an integer 
> yet, so this "calculator" is pretty much useless.

  It depends on what your input syntax is. Making a parser in C++ is not
one of the easiest things. Especially a stack calculator is difficult
because you can't know what the type of the next input element will be
in advance, but you have to simply read it as a string and then examine
its contents. The next element might be a number or an operator, and you
can't know which until you read it as a string and examine it.

  If the input had a very specific format, then it would be easier.
For example if your input consisted of lines like:

command 1 2

that is, if the keyword "command" appears in the input, it will always
be followed by two integers, then you could write code like this:

std::string keyword;
std::cin >> keyword;
if(keyword == "command")
{
    int x, y;
    std::cin >> x >> y;
    executeCommand(x, y);
}

  But as I said, if the next parameter can be a number or an operator or
a keyword or something else, it becomes more complicated.

> Also, there's an interesting glitch where the program won't continue if 
> you enter a blank line - you must enter *something* or the program won't 
> continue. Presumably this is just the defined behaviour of the >> 
> operator...

  If you call >> with a string, then it will read the input until it finds
a whitespace-delimited string. >> is not line-based in any way, it's
whitespace-based.

> PS. Nano is a horrid, horrid text editor!

  Why don't you use something better?

-- 
                                                          - Warp


Post a reply to this message

From: Invisible
Subject: Re: My first C++ program
Date: 19 Sep 2008 09:11:17
Message: <48d3a4f5$1@news.povray.org>
>> using namespace std;
> 
>   Don't do that.

So noted.

>> bool StackCheck(vector<int> stack, int min)
> 
>   You are passing the vector by value here, which may not be a good idea,
> especially if the vector is big. That's because the entire vector will be
> copied.

Right, OK.

> bool StackCheck(const std::vector<int>& stack, int min)
> 
>   Usually with big objects (such as vectors) you basically always want
> to pass by const reference rather than by value.

Ooo... I didn't realise you could do that. (Pass a const reference, that 
is.) So that means, what? If you accidentally try to change it, you'll 
get a compiler warning? Can you do that with non-reference types? (Not 
that it would matter if you were to change those - except that your 
program might not do what you intended.)

>>    int x = stack.back(); stack.pop_back();
>>    int y = stack.back(); stack.pop_back();
>>    stack.push_back(x + y);
> 
>   You could also save a bit of writing by doing it like this:
> 
>     int x = stack.back(); stack.pop_back();
>     stack.back() += x;
> 
>   Or if you want to really minimize things:
> 
>     stack.at(stack.size()-2) += stack.back();
>     stack.pop_back();
> 
>   Of course it starts being a bit obfuscated.

...ouch.

>   While it is perfectly valid to omit the return value of main()
> (IIRC it defaults to EXIT_SUCCESS), it's generally recommended to
> return EXIT_SUCCESS or EXIT_FAILURE (which are defined in some header
> file).

Noted.

Since you're here... do you happen to know the C++ name for stderr?

>> At present, I haven't figured out how to convert a string to an integer 
>> yet, so this "calculator" is pretty much useless.
> 
>   It depends on what your input syntax is. Making a parser in C++ is not
> one of the easiest things. Especially a stack calculator is difficult
> because you can't know what the type of the next input element will be
> in advance, but you have to simply read it as a string and then examine
> its contents. The next element might be a number or an operator, and you
> can't know which until you read it as a string and examine it.

I was thinking along the lines of "each line of input is either +, -, or 
a number". But I don't know how to convert a string into a number. 
Apparently the ">>" operator will do this if you hand it a number-type 
variable, so there must be some library function somewhere that does the 
conversion. I just don't know it's name yet.

>> Also, there's an interesting glitch where the program won't continue if 
>> you enter a blank line - you must enter *something* or the program won't 
>> continue. Presumably this is just the defined behaviour of the >> 
>> operator...
> 
>   If you call >> with a string, then it will read the input until it finds
> a whitespace-delimited string. >> is not line-based in any way, it's
> whitespace-based.

Well, that's consistent at least.

>> PS. Nano is a horrid, horrid text editor!
> 
>   Why don't you use something better?

Because my virtual machine is slow enough already without starting X11, 
and Nano is the only text-mode editor I have available (other than Vim 
and Emacs anyway).


Post a reply to this message

From: Invisible
Subject: Re: My first C++ program
Date: 19 Sep 2008 09:35:35
Message: <48d3aaa7$1@news.povray.org>
>> I also find myself constantly cursing that I have to write an entire 5 
>> lines of (nearly identical) code for every arithmetic operation I want 
>> to implement and there's no way to abstract this. (I don't have copy & 
>> paste available!)
> 
> What you could do, is have a function like DoOperation(stack,op), and 
> use "op" as a token for the operation.

I wrote a function that returns the top element from the stack while 
simultaneously removing it. That cuts down the typing quite a bit.

Note to self: Do *not* attempt to remove an element from an empty vector...


Post a reply to this message

From: Invisible
Subject: Re: My first C++ program
Date: 19 Sep 2008 10:05:39
Message: <48d3b1b3$1@news.povray.org>
Invisible wrote:

> At present, I haven't figured out how to convert a string to an integer 
> yet, so this "calculator" is pretty much useless. I'm sure it'll be 
> explained later in the tutorial.

The tutorial recommends the C function atoi() - which, being a C 
function, is about as user-friendly as Windows 2. Much hair-pulling 
involving pointers ensues.

(It turns out that you can assign a string to a char*, but not the 
reverse. Go figure. Anyway, the solution is simple enough...)


Post a reply to this message

From: Invisible
Subject: Re: My first C++ program
Date: 19 Sep 2008 10:10:25
Message: <48d3b2d1$1@news.povray.org>
void *(*(*fp1)(int))[10];

...OK, I'm scared! o_O

At least when somebody writes (Either (Maybe (Set Int) (Map Int String)) 
[(Word32, Int -> IO Int)]), it's relatively easy to work out what that is.

(Presumably in either language, if you ever write something like this 
for real, you're doing something worribly wrong somewhere...)


Post a reply to this message

From: Warp
Subject: Re: My first C++ program
Date: 19 Sep 2008 10:56:08
Message: <48d3bd88@news.povray.org>
Invisible <voi### [at] devnull> wrote:
> > bool StackCheck(const std::vector<int>& stack, int min)
> > 
> >   Usually with big objects (such as vectors) you basically always want
> > to pass by const reference rather than by value.

> Ooo... I didn't realise you could do that. (Pass a const reference, that 
> is.) So that means, what? If you accidentally try to change it, you'll 
> get a compiler warning?

  No, you get a compiler error. Since you declared the parameter as const,
you *can't* modify it.

  Declaring everything you don't intend to modify as 'const' is a good
programming practice in C++ for many reasons:

1) If you don't intend to modify the parameter, very especially if the
   parameter is a reference (in which case you would be modifying the
   original rather than a copy), then you will get a compiler error if
   you try to modify it anyways. This will catch programming mistakes
   at compile time, which is a good thing.

2) Declaring a reference parameter as 'const' is self-documenting: You are
   not only telling the compiler that that parameter is not modified in the
   function, more importantly you are telling that to a human reader. In
   other words, you are effectively documenting that the parameter is a
   pure input parameter, not something the function uses to pass values
   to the calling code.

3) As a bonus, it is possible that in certain situations the compiler might
   be able to optimize const variables better than non-const ones.

  Personally I have got the habit of declaring *everything* that I don't
intend to modify as 'const'. Even simple local variables. For example
I will typically write code like this:

    const int retVal = someFunction();
    somethingElse();
    return ratVal;

  In principle there's no need for the 'const' there, but I have got the
habit of doing it. After a while it becomes self-documenting even for
myself.

> Can you do that with non-reference types?

  Yes. Anything can be declared 'const', even variables and parameters
which are passed by value. In other words, it's perfectly possible to
do this:

void foo(const int value)
{
    const int anotherValue = value+1;
    ...
}

  In practice you seldom see 'const' used in function parameters like
that, though.

  Note that (not surprisingly) pointers are slightly more complicated
with regard to 'const':

    const Type* ptr = something;

  This defines a pointer named 'ptr' which points to a const value.
In other words, you can't modify the value pointed by 'ptr'. However,
you can modify 'ptr' itself, to make it point somewhere else (or make
it null, of whatever).

  If what you want is for 'ptr' itself to be const, not the value it's
pointing to, you have to write it like this:

    Type* const ptr = something;

  Now you can modify the value pointed by 'ptr' but you can't modify 'ptr'
itself. Naturally if you want *both* things, you write:

    const Type* const ptr = something;

  Now you can't modify either 'ptr' nor the value it's pointing to.

  References do not have this kind of complication because the reference
variable itself is always const. What the 'const' keyword determines is
whether the object it's pointing to is const or not.

> Since you're here... do you happen to know the C++ name for stderr?

  std::cerr << "An error message.\n";

  (Anything taking an std::ostream& can be given std::cout or std::cerr
as parameter. Or an output file stream. Or an output string stream.)

> I was thinking along the lines of "each line of input is either +, -, or 
> a number". But I don't know how to convert a string into a number. 
> Apparently the ">>" operator will do this if you hand it a number-type 
> variable, so there must be some library function somewhere that does the 
> conversion. I just don't know it's name yet.

  The problem with the >> operator is that you have to know in advance
what the type of the input will be. In this case you don't know it because,
as you say, the input can be an operator (+ or -) or a number.

  What you can do is this:

    std::string s;
    while(true)
    {
        std::cin >> s;
        if(!std::cin.good()) break;

        int value;
        std::istringstream iss(s);
        iss >> value;
        if(!iss.fail())
            std::cout << "The input was an integer: " << value << "\n";
        else
            std::cout << "The input was something else: " << s << "\n";
    }

> >> PS. Nano is a horrid, horrid text editor!
> > 
> >   Why don't you use something better?

> Because my virtual machine is slow enough already without starting X11, 
> and Nano is the only text-mode editor I have available (other than Vim 
> and Emacs anyway).

  Why don't you use an editor in your host OS?

-- 
                                                          - Warp


Post a reply to this message

Goto Latest 10 Messages Next 10 Messages >>>

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.