constructors / Lab 9 Problems [duplicate] - c++

This question already has answers here:
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 5 years ago.
My professor wants us to create a gladiator simulation where we name 5 gladiators, then create their stats, create the boss stats, then allow for the gladiators to fight the boss. During the fight, the health of everyone and randomly generated number damage dealt will be displayed until a winner is decided and from there, we will prompt the user if they would want a rematch.
Currently, I am stuck on figuring out what is and how do I use a constructor. Overall, I am lost with the project in total, but for now I want to understand this step by step. Inside the BossFight.h, consists of the prototype functions.
class BossFight {
private:
//Holds the party of gladiators that is banded together to fight the boss
Gladiator party[PSIZE];
//Holds the powerful boss that the party is fighting
Gladiator boss;
//Variables used for record keeping
int turnNum, fightsStarted, fightsWon;
//Will fill the party with gladiators, this function can call/reuse the createGladiator function.
void getParty();
//Will generate a boss for the party to fight. Has no crit or evasion, but 3* damage min and range, and 6* health
void getBoss();
//Tells the user who won, after how many turns, and the current record for the session
void displayFightResults(bool partyWon);
//One turn occurs, where each party member attacks the boss, then the boss attacks the party.
//Returned value indicates status of fight (continue, party win, party loss)
//Boss will randomly choose between attacking a single (randomly chosen) party member for full damage, or
//attacking the full party for half damage.
int takeTurn();
//Handles dealing damage to the entire party
void bossAttacksArea();
public:
//Responsible for generating the party and the boss, should initialize the other
//private variables as well
BossFight();
//User calls this when they want to run a fight. It will ask them if they want to use
//the same party, or get a new one.
void runFight();
};
What I have done so far is
#include <iostream>
#include <string>
#include "BossFight.h"
#include <stdlib.h>
#include <time.h> // Allows seed to generate new random numbers every time.
using namespace std;
const int SIZE = 5; //Party Size
Gladiator createGladiator(string name) // Data type Gladiator with its data named createGladiator
{
Gladiator stats; // Structure tag
for (int i = 0; i < SIZE; i++)
{
int maxHealth, evasion, critical;
stats.name = name;
// set max health
switch (rand() % 3) // % 3 means the range. So the starting number is 0 and final number is 2. Used to find a random number between the range 0-2.
// Uses that random number to open up one of the cases.
{
case 0: stats.maxHealth = 150;
break;
case 1: stats.maxHealth = 200;
break;
case 2: stats.maxHealth = 250;
break;
}
// set evasion
int numE = (rand() % 5); // Used to find a random number between the range 0-4.
switch (numE) // Uses that random number to open up one of the cases.
{
case 0: stats.evasion = 50;
break;
case 1: stats.evasion = 75;
break;
case 2: stats.evasion = 100;
break;
case 3: stats.evasion = 125;
break;
case 4: stats.evasion = 150;
break;
}
// Set Critical
int numC = (rand() % 5); // Used to find a random number between the range 0-4.
switch (numC) // // Uses that random number to open up one of the cases.
{
case 0: stats.critical = 50;
break;
case 1: stats.critical = 75;
break;
case 2: stats.critical = 100;
break;
case 3: stats.critical = 125;
break;
case 4: stats.critical = 150;
break;
}
// Set minDamage
int minimum, maximum;
minimum = 8;
maximum = 5;
int numMin = (minimum + rand() % (maximum + minimum)); // Used to find a random number between the minimum and maximum values.
stats.dmgMin = numMin;
// set DamageRange
int maxMin, maxMax;
maxMin = 16;
maxMax = 5;
int numMax = (maxMin + rand() % (maxMax - maxMin)); // Used to find a random number between the minimum and maximum values.
stats.dmgRange = numMax;
return stats; //Return all of the stats into the structure tag.
}
}
BossFight::BossFight() ***< -- stuck right here ***
{
getParty();
}
void BossFight::getBoss()
{
getBoss();
}
void getParty(string name[])
{
{
cout << "To begin with, enter 5 gladiator's name" << endl; // First for loop asking user for array input.
for (int i = 0; i < SIZE; i++)
{
cin >> name[i];
}
for (int i = 0; i < SIZE; i++)
{
cout << "Gladiator " << i + 1 << " name is " << endl;
cout << name[i] << endl;
}
}
}
int main()
{
srand(time(NULL)); //initiate random number generator seed.
string name[SIZE];
cout << "Hello user" << endl;
BossFight();
system("PAUSE");
}
I would appreciate any type of help. Do remember that I am taking an intro to computer science class, so I may not understand complex coding yet. I have been doing fine so far interpreting how the code should work in english, but find it hard to interpret how it should be via code.
Also, I am getting an error
Severity Code Description Project File Line Suppression State
Error LNK2019 unresolved external symbol "private: void __thiscall
BossFight::getParty(void)" (?getParty#BossFight##AAEXXZ) referenced in
function "public: __thiscall BossFight::BossFight(void)"
(??0BossFight##QAE#XZ) ConsoleApplication6 Severity Code Description Project File Line Suppression
State Error LNK2019 unresolved external symbol "private: void
__thiscall BossFight::getParty(void)" (?getParty#BossFight##AAEXXZ) referenced in function "public: __thiscall BossFight::BossFight(void)"
(??0BossFight##QAE#XZ) ConsoleApplication6 C:\Users\1\documents\visual
studio 2017\Projects\ConsoleApplication6\ConsoleApplication6\1.obj 1
And was wondering what even caused this? All i have in my visual studio is my header and cpp file.

This is called a linker error. What the error is saying is that BossFight::getParty() is being used by the BossFight::BossFight() constructor implementation, but you haven't provided an implementation of the getParty() method.
It looks like you were trying to add an implementation by declaring and implementing a getParty(std::string*) function, but this is not an implementation of the BossFight::getParty() method.
To implement BossFight::getParty(), you will need something like:
void BossFight::getParty() {
// implementation here
}
You will probably also want to hang on to the BossFight object that you construct by giving it a name:
BossFight boss_fight; // This declares *and* constructs a BossFight object on the stack.

Related

Turning on random LEDs from a class

For a school project I need to randomly turn on LEDs in a color that is randomly chosen. So for example you can choose between red, blue, yellow and green. Then you need to pick one color randomly and randomly situated LEDs of that specific color on. The amount of LEDs that need to be turned on is input from the main document, I am trying to write these functions in a different class.
I need different arrays that contain the different LEDs of that color like:
int GrLeds[] = {LED_1, LED_5}; //the amount of LEDs can be changed
int ReLeds[] = {LED_2, LED_6};
int BlLeds[] = {LED_3, LED_7};
int GrLeds[] = {LED_4, LED_8);
Then one of these colors needs to be chosen randomly. I thought about putting the different color option in an array like the following:
int randomClr[] = {ledG, ledR, ledB, ledY};
But doing it like this would require me to link the ledG to GrLeds[] etc.
Is there a possibility to choose one of the arrays randomly, or something what would result in the same? I know Java has the option to use a list but that does not seem to be possible within c++.
What you are basically looking for is the random() function, which gives you a random number between an initial and final input numbers.
To integrate it within your code, as you are gonna manage more than one set of LEDs which is integrated by multiple LEDs, I would just create a matrix for that, and then choose a random row from that matrix (each row will represent a color), and turn on all the LEDs from that row.
Some pseudo-code that you can work with:
int randomClr[4][2] = {
{LED_1, LED_5},
{LED_2, LED_6},
{LED_3, LED_7},
{LED_4, LED_8}
};
// some code...
// Get a random number from 0 to 3
int randNumber = random(4);
for (int i = 0; i < 2; i++) {
// Your code to turn on the LEDs, for example:
digitalWrite(randomClr[randNumber][i], HIGH);
delay(100);
digitalWrite(randomClr[randNumber][i], LOW);
}
Your problem is kinda similar to an application that I developed some time ago which also involved some LEDs and randomness.
I wrote the following code for running some tests before migrating the functionalities to the Arduino ecosystem.
Feel free to reuse and adapt my code to your needs. Keep in mind that I wrote it to be tested on C++17 using Codelite and not for the Arduino platform, therefore you can replace the random function with the one from Arduino.
Hope it helps. If so, just show a bit of appreciation including the link to this answer in your code, for posterity ;)
#include <iostream>
#include <random>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <time.h>
using namespace std;
#define MAX_COLORS 4
char textOut[100];
int cycles;
string colorNames[4] = { "RED", "BLUE", "GREEN", "PURPLE" };
typedef enum { RED, BLUE, GREEN, PURPLE } ColorList;
struct ColorsGroup {
ColorList colorCode;
string name;
};
ColorsGroup colorLED[4];
// Methods
int random(int, int);
ColorList retrieveColor(int);
void fillColors(void);
void printColors(int);
void setup()
{
fillColors();
cycles = 0;
}
int main()
{
cout << "********** Color picker *********" << endl;
setup();
while(cycles < 10) {
fillColors();
printColors(cycles);
cycles++;
}
return 0;
}
// From: https://stackoverflow.com/questions/7560114/random-number-c-in-some-range
int random(int min, int max)
{
static bool first = true;
if(first) {
srand(time(NULL));
first = false;
}
return min + rand() % ((max + 1) - min);
}
void fillColors(void)
{
for(int idx = 0; idx < MAX_COLORS; idx++) {
ColorList newColor = retrieveColor(random(0, MAX_COLORS - 1));
colorLED[idx].colorCode = newColor;
colorLED[idx].name = colorNames[newColor];
}
}
void printColors(int i)
{
sprintf(textOut, "%d. colorLED >> ", i);
cout << textOut;
for(int idx = 0; idx < MAX_COLORS; idx++) {
const char* nameStr = colorLED[idx].name.c_str(); // or &colorLED[idx].name[0];
sprintf(textOut, "%s[%d]", nameStr, colorLED[idx].colorCode);
cout << textOut;
if(idx <= MAX_COLORS - 2) {
sprintf(textOut, ", ");
cout << textOut;
}
else {
cout << ";" << endl;
}
}
}
ColorList retrieveColor(int col)
{
switch(col) {
case 0:
return RED;
break;
case 1:
return BLUE;
break;
case 2:
return GREEN;
break;
case 3:
return PURPLE;
break;
default:
return RED; // for the sake of completeness
break;
}
}
And this code spits out the following:

Problems with functions in classes C++ (LNK2019 & LNK1120 errors)

I've been working on a project for my college class that uses classes in c++, unfortunately anytime I try to call on a function that is passed parameters within my class the program fails to compile with the two following errors:
Error LNK2019 unresolved external symbol "int __cdecl binsearch(class Course * * const,int,char * const)" (?binsearch##YAHQAPAVCourse##HQAD#Z) referenced in function _main Project1 C:\Users\cvos\source\repos\Project1\Project1\courses_main.obj 1
and
Error LNK1120 1 unresolved externals Project1 C:\Users\cvos\source\repos\Project1\Debug\Project1.exe 1
I've looked up the LNK problems, and most results suggest it's something related to symbols in c++ vs c (That fix doesn't work) or that there is a problem with linking the files within visual studio (That fix also didn't work), and finally that it was something to do with it needing to be on the console subsystem (Which it already was).
The strange thing is, if I comment out my calls to all of the functions I've made in the "Course" class that are passed parameters, the program runs fine. It's only when I am trying to use the functions created in the "Course" class that the program fails to run, leading me to suspect strongly I'm doing something wrong with how I'm passing variables to my member functions.
I'll post the relevant parts of my code:
Within my header file "courses.h" I declare my function:
int binsearch(Course* co_ptr[], int size, char search[]);
Within my 2nd source file "courses_functions.cpp" I define the function:
int Course::binsearch(Course* co_ptr[], int size, char search[])
{
int low = 0, high = size - 1, first_index = -1, last_index = -1, num_of_entries = 0;
while (low <= high)
{
int mid = (low + high) / 2;
if (co_ptr[mid]->name == search)
{
bool found = false;
int i = mid;
while (!found) //Check values below mid
{
if (co_ptr[i]->name == search)
{
first_index = i; //first_index now holds a potential first occurence of our name
if (i == 0)
{
found = true;
}
}
else
{
found = true;
}
i--; //decrement i and check again.
}
i = mid; //Reset i
found = false; //Reset found
while (!found) //Check values above mid
{
if (co_ptr[i]->name == search)
{
last_index = i; //last_index now holds a potential last occurence of our name
if (i == size - 1)
{
found = true;
}
}
else
{
found = true;
}
i++; //increment i and check again.
}
break; //Breaks us out of the main while loop
}
else if (co_ptr[mid]->name < search)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
if ((first_index != -1) && (last_index != -1))
{
for (int i = first_index; i <= last_index; i++)
{
std::cout << "\nEntry found: "
<< std::endl << co_ptr[i]->name << ' ' << co_ptr[i]->units << " units, grade:" << co_ptr[i]->grade;
num_of_entries++;
}
return num_of_entries;
}
else
{
std::cout << "\nEntry not found.";
return num_of_entries;
}
}
Lastly in my main source file "courses_main.cpp" I call the function:
else if (selection == 3) //Display a course
{
char title[50] = "";
int results = 0;
std::cout << "\nEnter a class to search for: ";
std::cin.getline(title, 50, '\n');
std::cin.ignore();
results = binsearch(courses, size, title);
}
As this is for a college class, I'm not looking to use any alternative methods, I'm mainly trying to figure out why the method I'm using would return the errors I shared above, but I will gladly post more snippets of my code if it is necessary.
Thanks!
The cause is almost certainly one of the following:
You're not compiling the implementation file (just using the header elsewhere).
You're compiling the implementation, but not using the compiled object in the linking of objects into the executable.
You have some minor mismatch in naming, e.g. using binsearch() not in a class context, or using a slightly different signature somehow (not likely giving what you've told us).
Need to see a declaration of your "courses.h" file. You may have declared binsearch outside of your Course class declaration. In which case you will get a linker error as mentioned.
Based on your usage in main.. your implementation of this function need not be in Course class, it can be a standalone function outside of Course class. once you move your function definition outside of Course class, your linker should go away, provided you have courses_functions.cpp and courses_main.cpp files in same project in your MSVC IDE.

Problems creating Stack! (arcade game) in C++

im a beginner amateur in C++ and i recently had the idea to remake the popular arcade game Stack! in the C++ console.
The problem occours when the player has got to place the pad:
the pad places well, but if you make an error it doesn't resize properly (you'll understand better when you'll run the code).
Don't care about the graphic that sometimes bugs, because i can fix that on my own.
Please help me!
Here is the code:
#include <iostream>
#include <stdio.h>
#include <cstdlib>
#include <windows.h>
#include <string>
using namespace std;
bool bDirection = true; /* Bool for the direction:
true = dx, false = sx */
string sPad;
int nPadLenght = 6;
int x = 40, y =21; // Referement tile's position
int nSpeed = 200;
bool loop = true; // main loop
int nScore = 0; // score
int nPlaceX = 40;
int nTileX = 35, nTileY = 20; // Player's actual postition
int nEndTileX, nEndTileY;
void RenderLine(int *x, int *y);
int main();
// Void for the coordinates
void gotoxy (int x, int y){
COORD coord;
coord.X = x ;
coord.Y = y ;
SetConsoleCursorPosition (GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
void Victory(){
x = 10;
y = 4;
Beep(698.5, 300);
Beep(698.5, 100);
Beep(1047, 500);
system("color a");
gotoxy(x,y); cout << "You win!\n\n Score = " << nScore;
system("pause >nul");
}
void PadLenght(int *x){
// Each number is equal to a possible pad lenght
switch (*x){
case 6:
sPad = "[][][]";
nEndTileX = nTileX + 5;
break;
case 5:
sPad = "[][]]";
nEndTileX = nTileX + 4;
break;
case 4:
sPad = "[][]";
nEndTileX = nTileX + 3;
break;
case 3:
sPad = "[][";
nEndTileX = nTileX + 2;
break;
case 2:
sPad = "[]";
nEndTileX = nTileX + 1;
break;
case 1:
sPad = "[";
nEndTileX = nTileX;
break;
}
}
void SwitchDirection(bool *x){
// Switches the bool of the direction
switch (*x){
case true:
*x = false;
break;
case false:
*x = true;
break;
}
}
void Speed(){
// For each line from 500ms to 20ms speed increments of 10ms
if (nSpeed > 20)
nSpeed -= 20;
}
// void for placing the pad
void Place() {
int i = nPlaceX - nTileX;
if (i < 0)
i * -1;
nPadLenght -= i;
}
void collision(){
// Collisions with the border
if (nTileX > 45 || nTileX < 35)
SwitchDirection(&bDirection);
}
void movement(){
int nLastX = nTileX;
// Place the pad if pressing down arrow
if(GetKeyState(VK_DOWN) & 0x8000){
nTileY--;
Place();
Speed();
Beep(698.5, 50);
Beep(880.0, 50);
Beep(1047, 50);
nScore += 10;
Sleep(60);
}
// Movement of the pad
switch (bDirection){
case true:
gotoxy (nLastX, nTileY); cout << " ";
nTileX++;
break;
case false:
gotoxy (nLastX - nPadLenght, nTileY); cout << " ";
nTileX--;
break;
}
}
int main(){
system("color 0");
while (loop = true){
char a = '"';
gotoxy(x,y); cout << a << a << a << a << a << a;
collision();
PadLenght(&nPadLenght);
movement();
gotoxy (nTileX, nTileY); cout << sPad;
Sleep (nSpeed);
if (nScore > 160) {
Victory();
break;
}
}
return 0;
}
Here are some issues I found:
"using namespace std;"
This is bad. This brings in all of the identifier names from the std namespace. The preference is to use the std prefix (e.g. std::cout) or choose from the std namespace, (e.g. using std::cout;).
Global variables
Prefer to not have global variables. Create them in main and pass them to functions.
For example, you have global x and y, and you use x and y as parameters in functions. This may lead to confusion between you, readers and the compilers, as to which variables you are referring to.
Passing by pointer
Prefer not to use pointers.
Pass by value (without pointers) for variables that can fit into a processor's register, like float, double, int, bool and char.
For classes and structures, pass by reference. If you are not modifying the parameter, pass by constant reference.
Don't create functions for simple content
Functions require an overhead to execute, usually at least 3 instructions (save parameters, branch to function, return from function). Some operations can be performed with less statements:
// Switch directions:
direction = ! direction;
If you must use functions, give the compiler the hint that you want them inline. This means that the compiler will paste the content of the function where the function call is made. Some compilers may do this for optimizations, but you have to tell the compiler to optimize.
Boolean switch statements
Switch statements are kind of overkill for Boolean varibles, since there are only 2 outcomes. Common coding guidelines are to use if and else.
'=' in conditional expressions
Remember, one = for assignment, two for comparison.
The language allows for an assignment in a comparison, but most likely, you were not thinking of assignment, but testing for equality.
Declare variables closest to usage
Make life easier on you, the compiler and reader, by declaring variables closest to where they are used. For example, in main, there is a loop variable. As a reader, I have to scroll to the top of your source to find the definition, when a better idea is to declare it in the main function, where it is used.
One variable declaration per line
A common coding guideline is one variable per line. Multiple lines have a negligible effect on the build time. However, this makes modifications easier. And when declaring pointers, reduces injected defects. Let your compiler optimize the code, your task should be write clear (easily readable) and concise code.
Increase the compiler's warning level
Force your compiler to turn on the warning level to its highest level.
Resolve all warnings.
A clean compilation has zero errors and zero warnings.
With compiler warnings at full, you would have noticed some of the issues I have identified.

Pass enum to class and then return

I've got the following enum declared in code.cpp
and im using a switch statement that depends on the what the enum is set as.
enum State { HighMoral, PoorMoral, EndGame };
State CurrentState = HighMoral;
switch (CurrentState)
{
case HighMoral: random = rand()%3+1;
switch (random)
{
case 1: CurrentState = g_Solider.KillCommander(&CurrentState, random = rand()%2+1);
break;
case 2: CurrentState = g_Solider.KillSeniorCommander(&CurrentState,random = rand()%2+1);
break;
case 3: CurrentState = g_Solider.LessHalfStrength(&CurrentState);
break;
};
break;
case PoorMoral: CurrentState = g_Solider.JoinSeniorCommander();
break;
};
I want to pass this enum to a function in a class and then have it return either HighMoral, PoorMoral or EndGame and change the current state for my switch statement.
However I'm rather clueless when it comes to passing this and returning it.
I've looked around and have had no luck in finding how to do this.
I've got 3 files. code.cpp (contains void main() and the enum), solider.h (contains solider class does not know the state enum exists (how to do this?)), solider.cpp (contains all solider code but needs to take the current state and return a new state)
Here is an example of what I'm trying to do.
Solider.h
#include <time.h>
#include <iostream>
using namespace std;
extern enum State;
class Solider
{
private:
public:
void KillSeniorCommander(State& currentState, int random); // Kill the Senior Commander or random event
void JoinSeniorCommander(State& currentState); // Have the Commander join the group
void DefunctGroup(State& currentState); // Disband the group
};
Solider.cpp
void Solider::KillSeniorCommander(State& currentState, int random)
{
if (SeniorCommander==1) // If The SeniorCommander is Active
{
cout << "The Senior Commander has died!\n";
SeniorCommander--; // Kill the SeniorCommander
Groupsize--; // Reduce the Groupsize
Strength = Strength - (5*2.5); // Remove SeniorCommanders impact on Strength
SquadMoral = SquadMoral - (5*2.5);// Remove SeniorCommanders impact on SquadMoral
CurrentState = PoorMoral;
}
else // Otherwise do something random
{
switch (random)
{
case 1: cout << "Your group survives a ambush!\n";
break;
case 2: random = rand()%5+1; // Give random a new value
if (random>1)
{
cout << random << " group members have died!\n"; // Kill x Aamount of members
}
else
{
cout << "A group member has died!\n"; // Kill a member
}
Groupsize = Groupsize - random; // Remove the members from the group
Strength = Strength - (random*2.5); // Remove there effect Strength
SquadMoral = SquadMoral - (random*2.5); // Remove there effect on GroupMoral
break;
}
CurrentState = CurrentState;
}
} // KillSeniorCommander(int random)
void Solider::JoinSeniorCommander(State& currentState)
{
if (SeniorCommander==2 && Commander == 0) // Check to see if the Commander is dead and a
{ // SeniorCommander is not in service
cout << "The Senior Commander has joined!\n";
SeniorCommander--; // Change their status to active
Groupsize++; // Add them to the group
Strength = Strength - (5*2.5); // Add their impact to Strength
SquadMoral = SquadMoral - (5*2.5); // Add their impact to GroupMoral
CurrentState = HighMoral;
}
else // He isn't available to join
{
cout << "You fail to recruit new command!\n";
CurrentState = CurrentState;
}
} // JoinSeniorCommander()
void Solider::DefunctGroup(State& currentState)
{
cout << "Your group has been disbanded as it is not fit for duty.";
CurrentState = EndGame;
} // DefunctGroup()
code.cpp
Define the enum in a header and include that header in both files.
For instance:
#ifndef SOLDIERSTATE_H
#define SOLDIERSTATE_H
enum SoldierState
{
HighMorale,
PoorMorale,
EndGame
};
#endif
Enums can be treated as integers in interfaces. You can use on of two approaches: Pass by reference and have the function change it intenally, or pass by value and return the next State by value:
// one or the other
void nextState( State& currentState );
State nextState( State currentState );
As with everything else in C++, you need to see the declaration of something you want to use. In your case, this means the definition of State must be moved to a header file and this file then be included both in main.cpp and soldier.h. Then, you will be able to use the type State in declarations of Soldier member functions normally.
class Game
{
public:
enum State { HighMoral, PoorMoral, EndGame };
aMethod(State::PoorMoral);
};
Game::aMethod(State aState)
{
return State::HighMoral;
}

How to efficiently structure a terminal application with multiple menus?

I'm writing a console based program for my coursework, and am wondering how best to structure it so that it is both stable and efficient. I currently have
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
int choice;
do
{
cout << "\E[H\E[2J" // Clear the console
<< "Main menu" << endl << endl
<< "Please select one of the following options by entering it's "
<< "number in the prompt below and pressing [ENTER]. "
<< endl << endl
<< "1. Pay my bill as a guest" << endl
<< "3. Log in" << endl
<< "2. Create an account" << endl
<< "4. Quit program" << endl;
cin >> choice;
switch (choice)
{
case 1: // Pay the bill as a guest to the system
case 2: // Log in to the system
case 3: // Create an account with the system
case 4: // Quit the program
default: // Prompt the user to choose again
}
} while !(default);
// Await user input to terminate the program
cout << "Please press [ENTER] to continue...";
cin.get();
return 0;
}
The purpose of the above code is to provide a list of options for the user to choose from, with the do-while loop working alongside the default statement in the switch to catch any unexpected input. Each case would call a function that presented another menu with it's own list of options, which would be structured using the same do-while, switch method. My concern is that as my program grows, the number of function calls being nested within other functions is going to increase, so that I would eventually end up with a function being called from within a function being called from within a function and so on. This would obviously have severe implications for the maintainability of the program, with the function calls moving further and further away from main(), and the output of these functions weaving a tangled path about the program.
Is it possible to structure my program in such a way as to return execution to main() as often as possible, or is the problem described above simply a consequence of this kind of programming?
NB: I ask this question in the understanding that user-defined functions are supposed to be ancillary to main(), and that they should perform a task before returning control to main() as the earliest possible convenience. I've only been at this a couple of months now so please bear with my ignorance/misunderstanding. Also, ignore any potential compiler errors in the code, I've not tested it yet and it's only provided as an aide to my conceptual question.
I would apply some OO-design and create a menu-class which basically stores items/sub-menus in a vector. This would make it easy to extemd to hierarchical menus
There is nothing particularly wrong with what you've done.
I don't see why it harms maintainability to have functions called from functions and so on. If anything it AIDS maintainability as you can move common code operations into seperate functions. This way you make a fix in one place and instantly fix the rest of the places its used as well.
Well, you can implement your menu structure as a state-machine, so you will be almost always in your main loop. But this can bring your code to the lower level, because you will be effectively programming not in C++ but in your state-machine processor-near code. If your state machine will be good enough, this is not a problem.
Or you can a simple menu-runner class, which will output as a result a request for submenu, so you will just exchange (perhaps using a stack) the description of the currently running menu.
By the way, I don't see any problems in deep nesting of the functions.
Another possible approach is to make a class defined as a list of (menu_option, function) pairs and the know-how to turn them into menus. Then the function can be a call to another class instance's menu or it can do some operation on your database. That lets you keep your data organized away from the business "how to display this menu" logic and add menus and menu items easily.
Don't worry about that or your current approach spending too much time away from main though. As you've structured it, your program won't automatically turn itself into a horrible mess just because you're calling functions from functions. More functions will tend to add to maintainability, as long as you keep them focused.
Think of it this way: a function does one thing, but at a higher level of abstraction than its body. So main() runs your program. create_account() will create an account, which is part of running the program. create_account itself calls several other things that do the building blocks necessary for creating an account. Is determining the new account's name one thing? It goes in its own function. Determining the index of the new account in the database? Too low-level. Put it in the "stuff it in the database" function.
The complexity of the code will correlate to the functionality offered by the program. I would not worry about this right now, revisit refactoring once you have two or three hundred lines.
for use this code you must add:
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "kharid");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
this block code for insert menu item .
for change item to subitem you must change:
"100000"
to
"010000"
for do a difference work per every item you must define multiple:
void tmp_func(char* str)
void tmp_func1(char* str)
void tmp_func2(char* str)
...
and insert in
st_menues[cnt].function_pointer = tmp_func1;
...
st_menues[cnt].function_pointer = tmp_func2;
...
and insert your source code in these functions.
this source can compile in vc++.
i didn't test its in linux . but perhups works.
#include <iostream>
#include <stdio.h>
#include <conio.h>
#include<stdlib.h>
char title_str[20] = " main menu \n";
void print_this(bool with_title,const char* str, ...)
{
if(with_title)printf(title_str);
printf(str);
}
void clear()
{
#ifdef _WIN32
system("cls");
#else
std::cout << "\033[2J\033[1;1H";
#endif
print_this(true,"");
}
struct def_struct_menu
{
void (*function_pointer)(char*);
char menu_string[24];
char layer[7];
int pos;
};
void set_title(char* str)
{
sprintf(title_str," %s \n",str);
}
void tmp_func(char* str)
{
clear();
set_title(str);
printf("calc okokok");
_getch();
}
def_struct_menu st_menues[100] = { 0 };
def_struct_menu st_cur_menues[100] = { 0 };
void back_to_main_menu(int& highlight_line, int& cur_layer, int& start)
{
highlight_line = 0;
cur_layer = 0;
start = 0;
set_title((char*)"main menu");
}
int main()
{
int cnt = 0;
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "kharid");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "asan");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "shenase");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
}
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "sharj");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "ramz");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "yekbarmasraf");
memcpy(st_menues[cnt++].layer, "001000", sizeof(st_menues[cnt].layer));
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "qrcode");
memcpy(st_menues[cnt++].layer, "001000", sizeof(st_menues[cnt].layer));
}
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "mostaghim");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
}
const int ST_SIZE = cnt;
int input = 0;
int highlight_line = 0;
int cur_layer = 0;
int start = 0;
while (input != -1)
{
int size = 0;
memset(st_cur_menues, 0, sizeof(def_struct_menu) * ST_SIZE);
for (int i = start; i < ST_SIZE; i++)
{
if (cur_layer > 0)
{
if (st_menues[i].layer[cur_layer - 1] == '1')
{
break;
}
}
if (st_menues[i].layer[cur_layer] == '1')
{
memcpy(&st_cur_menues[size++], &st_menues[i], sizeof(def_struct_menu));
}
}
clear();
if (size == 0)
{
back_to_main_menu(highlight_line, cur_layer, start);
}
for (int i = 0; i < size; i++)
{
if (highlight_line == i)
print_this(false,"*");
else
print_this(false," ");
print_this(false,st_cur_menues[i].menu_string);
print_this(false,"\n");
}
//print_this("enter number\n");
input = _getch();
switch (input)
{
case 'x':
{
exit(0);
}
case 27://escape button
{
back_to_main_menu(highlight_line, cur_layer, start);
break;
}
case 13://enter button
{
if (size == 0)
{
back_to_main_menu(highlight_line, cur_layer, start);
break;
}
st_cur_menues[highlight_line].function_pointer(st_cur_menues[highlight_line].menu_string);
start = st_cur_menues[highlight_line].pos + 1;
cur_layer++;
highlight_line = 0;
}
break;
case 72://up arrow key
{
if (highlight_line == 0)
highlight_line = (size - 1);
else
highlight_line--;
}
break;
case 80://down arrow key
{
if (highlight_line == (size - 1))
highlight_line = 0;
else
highlight_line++;
}
break;
default:
break;
}
}
return 0;
}
tnx