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?

7
  • 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. Commented Sep 23, 2014 at 19:01
  • Closely related classes can be kept together in the same files. Commented Sep 23, 2014 at 19:02
  • possible duplicate of Undefined Reference to Commented Sep 23, 2014 at 19:03
  • 2
    You just need to #include "Token.h" within Token_Stream.h Commented Sep 23, 2014 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. Commented Sep 23, 2014 at 19:06

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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: 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.
of course. yes the -o means for the linker, correct?
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.