0

I am working on everyone's favorite intro to C++ project. The Dice game. I am having a really hard time understanding how C++ sends info back and forth and how using functions and classes in separate files works. My biggest question is why do I get Undeclared Identifier on every single variable?...

histogram.h

#pragma once
#include "stdafx.h"
#include "histogram.cpp"
#include <cstdlib>
#include <stdio.h>
#include <iostream>

typedef unsigned int uint;

uint rolls = 0, i = 0, die1 = 0, die2 = 0, output = 0, show = 0;
uint frequency[12] = { 0 };

class Dice
{
public:

    void inline rollDie(uint rolls)
    {
        for (i = 1; i <= rolls; i++) {
            die1 = 1 + rand() % 6;
            die2 = 1 + rand() % 6;
            ++frequency[die1 + die2];
        }
    }
}; 

histogram.cpp

#include "stdafx.h"
#include "histogram.h"

using namespace std;

class theHisto 
{
public:

    void printHisto()
    {
        for (int i = 1; i < 12; i++)
        {
            //Output some asterisks for the histogram
            for (int freq = (frequency[i] * 100 / rolls); freq > 0; freq--)
                cout << "*";
            cout << endl;
        }
    }
};

main.cpp

#include "stdafx.h"
#include "histogram.h"
#include "histogram.cpp"

using namespace std;

uint main()
{
    cout << "How many times would you like to roll the dice?";
    cin >> rolls;
    Dice myDice;
    myDice.rollDie(rolls);
    theHisto mytheHisto;
    mytheHisto.printHisto();
}

Sorry if this is messy but when I copy code into the "Enter code here" box it just shows up as regular text so I had to indent each line by 4 spaces so formatting may be a little off....

Khalil Khalaf
  • 9,259
  • 11
  • 62
  • 104
Bren Bailey
  • 11
  • 1
  • 1
  • 8
  • What is the actual error you get? – NathanOliver Sep 28 '16 at 18:04
  • 4
    "Pro" code formatting tip: 1) paste the code 2) select the code 3) ctrl+k – Borgleader Sep 28 '16 at 18:05
  • This code does not compile. Are you sure you are not missing essential libraries? But including other un needed instead? – Khalil Khalaf Sep 28 '16 at 18:09
  • You are #including a .cpp file from another .cpp file -- that in general isn't right, although you can get it to work. The intent is for each .cpp file to be compiled to an object file (.obj or .o usually depending on your compiler), and then linked together. – Nathan Monteleone Sep 28 '16 at 18:10
  • You're also including a .cpp from a header, which is even worse :( – Nathan Monteleone Sep 28 '16 at 18:11
  • You should not include `.cpp` files. They are intended to contain actual implementation of classes defined in `.h` files. So, for each of your classes you should have a two files: one `.cpp` and one `.h`. – Gasper Sep 28 '16 at 18:12
  • You might try reading http://stackoverflow.com/a/6264256/27130 - it's an explanation of how compilation and linking works overall. – Nathan Monteleone Sep 28 '16 at 18:12
  • @NathanMonteleone A requirement of the project is this "Arrange your C++ code in three separate files: "histogram.h", "histogram.cpp", and "main.cpp". " – Bren Bailey Sep 28 '16 at 18:30
  • Off topic: you don't want `#include "stdafx.h"` in your header. But you might want `#include "yourheader.h"` in stdafx.h. stdafx.h is for pre-compiling headers to save you time when building. Headers tend to not change much, but the CPP files including them do. Precompiling and caching the header with stdafx.h may save you time by not forcing them to be built along with the cpp files that include them. – user4581301 Sep 28 '16 at 18:38
  • @BrenBailey Understood -- but this isn't the way to go about it. You need to compile the .cpp files separately, then use a linker to combine them into a single executable. – Nathan Monteleone Sep 28 '16 at 18:44
  • @NathanOliver Undeclared Identifier is the error I get for every variable declared in my header file. Could someone point me towards some actual code examples that show headers and mains communicating so that I can see practically how it works instead of guessing. I just don't understand how why I'm getting undeclared identifier on every variable when they are all declared in a header that is included on all of my files... – Bren Bailey Sep 28 '16 at 18:51
  • @FirstStep He's not _missing_ anything, per se. It's just that due to `#include`ing `histogram.cpp` in `histogram.h`, he ends up trying to use `std::cout` and `std::endl` before the `#include ` is encountered, and ends up trying to use his global variables before they're formally declared. – Justin Time - Reinstate Monica Sep 28 '16 at 19:03
  • @JustinTime Yes he is missing `#include ` for the cout / cin in `main()` and all their `std::`s. – Khalil Khalaf Sep 28 '16 at 19:10
  • @FirstStep It's in `histogram.h`, so `main.cpp` indirectly `#include`s it. Good point, though, I'll make a note about that under my answer. – Justin Time - Reinstate Monica Sep 28 '16 at 19:15
  • @JustinTime I think you are right :) – Khalil Khalaf Sep 28 '16 at 19:23
  • @FirstStep It's problematic, since anyone that `#include`s `histogram.h` will get three library headers whether they want to or not, but it does work. – Justin Time - Reinstate Monica Sep 28 '16 at 19:32
  • @JustinTime I just got informed that It will work, and if some libraries are included in a file, and we include that file in main, all those libraries will be copied. Even the `using namespace std;` as well. It is not a good practice though. – Khalil Khalaf Sep 28 '16 at 19:37

2 Answers2

3

First, don't #include "histogram.cpp", that's the root cause of all the problems. Specifically:

  1. The compiler will start with either main.cpp or histogram.cpp, most likely main.cpp. It will then proceed to create a single translation unit from each .cpp file, by preprocessing it and including every file specified by an #include directive.
  2. While preprocessing main.cpp, it will encounter #include "histogram.h". This tells the preprocessor to paste the file histogram.h at the top of main.cpp, where it encounters that line. It will then continue preprocessing with the contents of histogram.h, until it reaches the end of the code pasted from the file and moves on to the next line in main.cpp.
  3. While preprocessing the code pasted in from histogram.h, it will encounter #include "histogram.cpp". This tells the preprocessor to paste the file histogram.cpp there, just as it did for histogram.h. It will then continue preprocessing with the contents of histogram.cpp, until it reaches the end of the code pasted from the file and moves on to the next line pasted from histogram.h.
  4. #pragma once will prevent stdafx.h, histogram.h and histogram.cpp from being copy-pasted multiple times, and preprocessing will finish normally. [Note that #pragma once may or may not guard against the #include "histogram.cpp" in main.cpp, since it's in histogram.h and not main.cpp. Similarly, if stdafx.h contains anything, it may run into this same issue; I will assume that it's empty, and was automatically inserted by your IDE, and thus will only mention it once, at the top of the translation unit.] Compilation will now begin, with the following file:

    // Contents of "stdafx.h" here.
    
    using namespace std;
    
    
    
    class theHisto {
    public:
    
        void printHisto()
        {
            for (int i = 1; i < 12; i++)
            {
                //Output some asterisks for the histogram
                for (int freq = (frequency[i] * 100 / rolls); freq > 0; freq--)
                    cout << "*";
                cout << endl;
            }
        }
    };
    // Contents of <cstdlib> here.
    // Contents of <stdio.h> here.
    // Contents of <iostream> here.
    
    
    
    typedef unsigned int uint;
    
    uint rolls = 0, i = 0, die1 = 0, die2 = 0, output = 0, show = 0;
    uint frequency[12] = { 0 };
    
    class Dice
    {
    
    
    public:
    
    
        void inline rollDie(uint rolls)
        {
            for (i = 1; i <= rolls; i++) {
                die1 = 1 + rand() % 6;
                die2 = 1 + rand() % 6;
                ++frequency[die1 + die2];
            }
        }
    };
    // Contents of "histogram.cpp" may also be here.
    using namespace std;
    
    
    
    uint main()
    {
        cout << "How many times would you like to roll the dice?";
        cin >> rolls;
        Dice myDice;
        myDice.rollDie(rolls);
        theHisto mytheHisto;
        mytheHisto.printHisto();
    }
    

Note, if you will, that the body of histogram.cpp is now placed before the body of histogram.h, meaning:

  • frequency is used before being declared.
  • rolls is used before being declared.
  • std::cout is used before #include <iostream> is encountered.
  • std::endl is used before #include <iostream> is encountered.
  • If the compiler doesn't respect #pragma once the way you expect it to, class theHisto will be redefined.

Thus, you get "undefined variable used" errors.

Instead, you want your compiler to compile both histogram.cpp and main.cpp, and combine them during the linking phase. While we're at it, since you already #include "stdafx.h" at the start of each .cpp file, there's no need to do the same in histogram.h.


Next, theHisto should be defined in histogram.h, not histogram.cpp.

// histogram.h

#pragma once
// #include "stdafx.h"
// #include "histogram.cpp"
#include <cstdlib>
#include <stdio.h>
#include <iostream>

typedef unsigned int uint;

uint rolls = 0, i = 0, die1 = 0, die2 = 0, output = 0, show = 0;
uint frequency[12] = { 0 };

class Dice
{
public:
    void inline rollDie(uint rolls)
    {
        for (i = 1; i <= rolls; i++) {
            die1 = 1 + rand() % 6;
            die2 = 1 + rand() % 6;
            ++frequency[die1 + die2];
        }
    }
};

class theHisto
{
public:
    void printHisto();
};

And...

// histogram.cpp

#include "stdafx.h"
#include "histogram.h"

using namespace std;

void theHisto::printHisto()
{
    for (int i = 1; i < 12; i++)
    {
        //Output some asterisks for the histogram
        for (int freq = (frequency[i] * 100 / rolls); freq > 0; freq--)
            cout << "*";
        cout << endl;
    }
}

This will resolve the errors, but will cause linkage errors, due to your global variables being defined in multiple compilation units. To solve this, you can declare them as extern in histogram.h, and define them in one of the .cpp files.

// histogram.h

#pragma once
// #include "stdafx.h"
// #include "histogram.cpp"
#include <cstdlib>
#include <stdio.h>
#include <iostream>



typedef unsigned int uint;

extern uint rolls, i, die1, die2, output, show;
extern uint frequency[12];

class Dice
{
public:
    void inline rollDie(uint rolls)
    {
        for (i = 1; i <= rolls; i++) {
            die1 = 1 + rand() % 6;
            die2 = 1 + rand() % 6;
            ++frequency[die1 + die2];
        }
    }
};

class theHisto
{
public:
    void printHisto();
};

And...

// histogram.cpp

#include "stdafx.h"
#include "histogram.h"

using namespace std;


uint rolls = 0, i = 0, die1 = 0, die2 = 0, output = 0, show = 0;
uint frequency[12] = { 0 };


void theHisto::printHisto()
{
    for (int i = 1; i < 12; i++)
    {
        //Output some asterisks for the histogram
        for (int freq = (frequency[i] * 100 / rolls); freq > 0; freq--)
            cout << "*";
        cout << endl;
    }
}

And...

// main.cpp

#include "stdafx.h"
#include "histogram.h"
// #include "histogram.cpp"
using namespace std;

// You could define your variables here, instead of in "histogram.cpp", if you wanted.

uint main()
{
    cout << "How many times would you like to roll the dice?";
    cin >> rolls;
    Dice myDice;
    myDice.rollDie(rolls);
    theHisto mytheHisto;
    mytheHisto.printHisto();
}

This will solve the linkage errors, and allow your code to compile. Note that the compiler's command line should specify both main.cpp and histogram.cpp, and will likely look something like this (assuming Visual Studio, because of stdafx.h).

cl /EHsc main.cpp histogram.cpp

Alternatively, you can eliminate histogram.cpp entirely, by defining theHisto::printHisto() as inline.

// histogram.h

#pragma once
// #include "stdafx.h"
// #include "histogram.cpp"
#include <cstdlib>
#include <stdio.h>
#include <iostream>



typedef unsigned int uint;

extern uint rolls, i, die1, die2, output, show;
extern uint frequency[12];

class Dice
{
public:
    void inline rollDie(uint rolls)
    {
        for (i = 1; i <= rolls; i++) {
            die1 = 1 + rand() % 6;
            die2 = 1 + rand() % 6;
            ++frequency[die1 + die2];
        }
    }
};

using namespace std;

class theHisto
{
public:
    void printHisto()
    {
        for (int i = 1; i < 12; i++)
        {
            //Output some asterisks for the histogram
            for (int freq = (frequency[i] * 100 / rolls); freq > 0; freq--)
                cout << "*";
            cout << endl;
        }
    }
};

And...

// main.cpp

#include "stdafx.h"
#include "histogram.h"
// #include "histogram.cpp"
using namespace std;



uint main()
{
    cout << "How many times would you like to roll the dice?";
    cin >> rolls;
    Dice myDice;
    myDice.rollDie(rolls);
    theHisto mytheHisto;
    mytheHisto.printHisto();
}

This one will be compiled with the following, again assuming Visual Studio:

cl /EHsc main.cpp

Either of the previous two should fix your problem. I would recommend the former, so you can also move Dice::rollDie() to histogram.cpp, as I did with theHisto::printHisto(). Just remember not to #include any .cpp files, unless 1) they're included after any code they depend on, and 2) you only #include them in a single file, which should have a header guard. [Note that if you know what you're doing, and how to break the second rule safely, you can pull off a lot of crazy tricks.] It's generally better to tell the compiler to process each .cpp file as a separate translation unit, instead of just combining them into a single, massive blob.


[See also the first comment on this answer, which contains further recommendations; they shouldn't be necessary to get your code to work, but will be useful. If anything, the one most likely to break is main()'s return type; while unsigned int can be implicitly converted to int, not every compiler will agree with main() returning an unsigned int instead of an int.]

  • Also, `main()` should return `int` instead of `unsigned int`, and I would recommend placing your `using namespace std` directives inside `theHisto::printHisto()` and `main()`, instead of in the global namespace. You can also remove the global variables `i`, `die1`, and `die2`, and replace them with local variables in `Dice::rollDie()`. You didn't use `output`, so you can likely remove it. – Justin Time - Reinstate Monica Sep 28 '16 at 19:09
  • You should also move your standard library `#include`s (``, ``, and ``) to `main.cpp` and `histogram.cpp`, so that any other file that `#include`s `histogram.h` won't get them as well. I'm not sure if you actually _need_ ``, for that matter; I didn't see anything that used it. I would also recommend seeding `rand()` with `srand()`; you could either do this at the top of `main()`, or in `Dice`'s default constructor. `#include ` with your other library includes, and use `srand(time(nullptr));` – Justin Time - Reinstate Monica Sep 28 '16 at 19:24
  • (If your compiler isn't C++11 compliant, or complains about `nullptr`, then either use the compiler's version of the switch `-std=c++11` (if available; you can also use `-std=c++14`), or replace `nullptr` with `NULL`.) – Justin Time - Reinstate Monica Sep 28 '16 at 19:26
  • I will update this with newer code soon. You all's help has been very awesome. I should say that some things such as having histogram.cpp instead of simplifying are part of the assignment in order to teach us how c++ works even though we know it isn't exactly an ideal set up. – Bren Bailey Sep 28 '16 at 23:49
  • @BrenBailey Ah, okay. That makes sense, knowing how to compile multiple source files into a single project is extremely useful, especially once you move onto more complex projects. Glad I could help. – Justin Time - Reinstate Monica Oct 04 '16 at 19:02
0

Here are some problems I see with your code. I don't know if that solves your problem as you didn't post the error message.

  1. The method main() should return int.
  2. The frequency array is too small. Or you values for die1 and die2 are too big. Depends how you want to look at it.
  3. You are mixing C++ and C headers. Inclue cstdio instead of stdio.h. Best not use cstdio and cstdlib at all. You don't need them.
  4. Just use unsigned as a data type instead of the uint you defined yourself.
  5. You aren't initializing your random number generator.
RotatingPieces
  • 413
  • 2
  • 8
  • 2
    I disagree with 4. Theres nothing wrong with a typedef. – Borgleader Sep 28 '16 at 18:21
  • So this is the error i get for every variable Error C2065 'rolls': undeclared identifier histogram Did you mean use stdio.h instead of cstdio? What do you mean by main should return int? What int should it return? – Bren Bailey Sep 28 '16 at 18:28
  • @BrenBailey The return type of the main function is specifically int, you used uint. – Borgleader Sep 28 '16 at 18:29