user2386276
user2386276

Reputation: 548

C++ program structure

I'm having some trouble thinking of how to structure a program in C++. I am following a book in learning C++ and at one point we construct two classes in an effort to solve a problem. The book ends up putting both classes, and how they're used all in one file and running it, and this works. But I understand more properly structured code would include header files and each class would get it's own file, and trying to structure the program like this is causing problems when I try to get the code to compile.

I have two classes, Token and Token_Stream, from other languages I know Token and Token_Stream should get their own files and that each should have a declaration file. My main issue is:

Token_Stream needs to know about Token. When a Token_Stream is initialized it initializes a Token. I had thought it would suffice to have just the declaration of Token included in Token_Stream and that would be enough, but that appears not to be the case. I know a bit about programming with OOP languages, but Token_Stream does not inherit anything from Token, nor should it (I believe) it just needs to know enough to initialize a Token and store it. I will include each of the relevant files below:

Token.h

// Token.h, declaration for Token

class Token
{
public:
    char kind;
    double value;

    Token(char ch);

    Token(char ch, double val);
}; //class Token

Token.cpp

// Token.cpp

#include "Token.h"

using namespace std;

Token::Token(char ch) 
    :kind(ch), value(0){}

Token::Token(char ch, double val)
    :kind(ch), value(val) {}

Token_Stream.h

// Token_Stream.h, declarations


class Token_Stream
{
public:
    Token_Stream();
    Token get();
    void putback(Token);

private:
    bool full; // do we already hold a token?
    Token buffer; // what Token do we hold?
};//class Token_Stream

Token_Stream.cpp

// Token_Stream.cpp implementation.

#include <iostream>
#include <stdexcept>
#include "Token.h" // needs to know what a Token is
#include "Token_Stream.h"

using namespace std;

/***********************************************************************
 * Token_Stream::Token_Stream()
 * 
 * Constructor for Token_Stream(), sets full = false and buffer as 0
 * Need to do :buffer(0), so we don't create an extra buffer variable
 **********************************************************************/
Token_Stream::Token_Stream()
:buffer(0)
{
    full = false; // nothing in our stream yet.
}//constructor

/***********************************************************************
 * void Token_Stream::put_back(Token t)
 * 
 * Given a token, we fill buffer and change full to true
 * 
 * Parameter: t - Token to fill buffer
 **********************************************************************/
void Token_Stream::putback(Token t)
{
    if(!full) // if its empty
    {
        buffer = t;
        full = true;
    }//if not full
    else
        throw runtime_error("buffer already full");
}// putback


/***********************************************************************
 * Token Token_Stream::get()
 * 
 * gets another token from input, or if we have one stored, gets that.
 * 
 * Returns: Token - next token in stream, either from buffer or from
 *                  input
 **********************************************************************/
Token Token_Stream::get()
{
    if(full) //if we already have something
    {
        full = false;
        return buffer;
    }

    //if we've reached here we haven't returned:

    char ch;
    cin>>ch; //get next input and switch over cases:
    switch(ch)
    {
        // if they input a valid character:
        case ';': 
        case 'q':
        case '(': case '+': case '*': case '-': case '/': case '%': 
        case ')':
            return Token(ch);
            break;

        //if they input a valid number, or lead with a decimal i.e., .5
        case '.': case '0': case '1': case '2': case '3': case '4': 
        case '5': case '6': case '7': case '8': case '9': 
        {
            cin.putback(ch);
            double val;
            cin>>val; //read it as a number
            return Token('8',val);
            break;
        }//case of valid number
        default:
            throw runtime_error("Bad Token");
    }//switch
}//get

So those are the files, and when I try to compile things, i.e., put a blank int main(){} in Token.cpp, everything works fine, I compile, and if I wanted to I could run things in main()

But when I try put a blank int main(){} in Token_Stream.cpp and try to compile it does not work, I am running:

g++ -Wall -std=c++11 -o "Token_Stream" "Token_Stream.cpp" 

and I am not even getting line number errors, but its claiming an undefined reference to Token::Token(char) etc and the rest of the Token constructors, so I'm guessing that this means that Token_Stream.cpp needs to see more of Token.cpp, how do I do this? Do I just simultaneously compile them?

Upvotes: 0

Views: 332

Answers (2)

qbt937
qbt937

Reputation: 947

You need to link Token.cpp to your executable.

g++ -Wall -std=c++11 -o "Token_Stream" "Token.cpp" "Token_Stream.cpp"

Otherwise gcc won't find the implementation of Token's constructor.

Upvotes: 2

quamrana
quamrana

Reputation: 39354

Besides your linking problems, if you are looking for proper structures within programs you still need to sort out your dependencies.

Token_Stream.h should start like this:

// Token_Stream.h, declarations
#include "Token.h"  // Note that this include is at the top.

class Token_Stream
...

Token_Stream.cpp should start like this:

// Token_Stream.cpp implementation.
#include "Token_Stream.h"  // Note that this include is at the top.

#include <iostream>
#include <stdexcept>
...

The main points above are:

  1. Each header file should be included at the top of its implementation file.
  2. Each header file should include everything it actually needs, and no more.

These prerequisites will allow your clients to include your headers wherever they need them.

Upvotes: 0

Related Questions