0

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?

quamrana
  • 37,849
  • 12
  • 53
  • 71
user2386276
  • 548
  • 5
  • 19
  • 1
    You don't need to restrict yourself to one class per translation unit or header files. It is common to have several classes in one header file. – Basile Starynkevitch Sep 23 '14 at 19:01
  • Closely related classes can be kept together in the same files. – Barmar Sep 23 '14 at 19:02
  • possible duplicate of [Undefined Reference to](http://stackoverflow.com/questions/5293021/undefined-reference-to) – Borgleader Sep 23 '14 at 19:03
  • 2
    You just need to `#include "Token.h"` within `Token_Stream.h` – quamrana Sep 23 '14 at 19:03
  • 2
    Judging by your question, it appears as though the context is that you are _trying to access a class in a different file_, and your error message is "_undefined reference to Token::Token(char)"_. If I'm right, then I think that you can write a more concise version of this question in 1/3rd of the length. – Sam I am says Reinstate Monica Sep 23 '14 at 19:06
  • @quamrana including Token.h in Token_Stream.h doesn't seem to do anything, I still get the same errors. – user2386276 Sep 23 '14 at 19:10
  • @user2386276 OK, I see now - You should have included the text of your errors in your question. – quamrana Sep 23 '14 at 20:14

2 Answers2

2

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.

quamrana
  • 37,849
  • 12
  • 53
  • 71
qbt937
  • 947
  • 7
  • 26
  • Ah! okay this does in fact get me to compile! Thank you. So the idea is if you have the implementation in another file, you also have to give it to your compiler, and the compiler just works out the dependencies? or does order matter here? – user2386276 Sep 23 '14 at 19:16
  • @user2386276: Actually it's the _linker_, not the compiler, doing this stuff. And yes, the linker, resolves these sorts of dependencies for you (for the most post). The order can matter but I wouldn't worry about it for the time being; it's more of an issue with third-party libraries linked in with the `-l` flag. – Lightness Races in Orbit Sep 23 '14 at 19:17
  • of course. yes the -o means for the linker, correct? – user2386276 Sep 23 '14 at 19:19
0

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.

quamrana
  • 37,849
  • 12
  • 53
  • 71