Setting Two Functions Equal - c++

I am trying to make a C++ Button class in which the user inputs a function to execute when the button is pressed. So far, this is my code:
class Button {
public:
char* text;
void* buttonclick();
Button (char* constext, void* consbuttonclick()) {
text = constext;
//What do I do here?
}
};
I am trying to make the constructor pass on the value of consbuttonclick() to buttonclick(). I believe there is a way to do this using function pointers, but I have been having trouble with them, so I would greatly appreciate some help on how to proceed.

What you've declared is a function returning a void *. You want a pointer to function returning void. So to start with you need to change the prototype: In place of void * foo() you want void (*foo)(). (You'll have to sort out exactly what you want, but that's the idea.)
Once you have it, you call it simply by using the function invocation operator (), so you'l get something like
class Button {
public:
char* text;
void* buttonclick();
Button (char* constext, void(* consbuttonclick())) {
text = constext;
consbuttonclick();
}
};
Better yet, though, is to create what's called a functor class, like:
class ClickFunctor {
public:
void doIt(){
// code
}
}
then pass a ClickFunctor object.
class Button {
public:
char* text;
Button (char* constext, ClickFunctor cf) {
text = constext;
cf.doIt();
}
};
Now, go read the C++ FAQ book on these things. (Update: Marshall calls these things "functionoids" rather than "functors", just to warn you.)

Sorry to burst your bubble, but there's no sane way of doing that. Function names only exist for easy writing. As soon as the compiler gets hold of them, the names become garbled and then turned into function addresses only. The names (at least in C++) are meaningless at runtime.
Your best bet is either a long chain of if / else statements OR possibly a dictionary that maps a string to a function pointer. But you'd have to set up that dictionary manually.

Look, if you are trying to make a button in a c++ ms-dos window, it can't be done unless you have a library that I don't about. But I found a way around that (which is long but gets the job done). What I did was I found the top left position of a button drawn out with simple cout<<"whatevergoeshere";, and the bottom right. Then, by creating a function that returns true when the left button is pressed and if the click is within the arguments provided for the top left corner and bottom right corner, control will move on. It is a pretty complicated process, but I can give you the source code I used in another answer.
(If you wan't me to explain any of this code, I will. I didn't know how to do this either until recently. One thing that is important though is keeping the window in the same place everytime. In the environment I'm using (code::blocks), the window stays in the same place, but moving it can mess up the button. Also remember to continuosly use the system("cls"); to clear the screen so stuff doesn't change position. (system"cls" is located in the cstdio or cstdlib libraries.)

Here is the code I used:
#include <iostream>
#include <windows.h>
#include <cstdlib>
#include <cstdio>
#include <stdio.h>
#ifndef MOUSE_HWHEELED
#define MOUSE_HWHEELED 0x0008
#endif
using namespace std;
int main()
{
POINT point;
HANDLE csbiHandle;
CONSOLE_SCREEN_BUFFER_INFO csbi;
int counter = 0;
DWORD cNumRead, i,fdwMode, fdwSaveOldMode;
INPUT_RECORD irInputBuffer[128];
HANDLE stdHandle;
stdHandle = GetStdHandle(STD_INPUT_HANDLE);
MOUSE_EVENT_RECORD mer;
cout << "|-------------|" << endl
<< "| A |" << endl
<< "|-------------|" << endl;
buttonpress:
ReadConsoleInput(stdHandle, irInputBuffer,128, &cNumRead);
GetCursorPos(&point);
for(i=0; i<cNumRead; i++)
{
switch(irInputBuffer[i].EventType)
{
case MOUSE_EVENT:
{
mer = irInputBuffer[i].Event.MouseEvent;
if(mer.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)
{
cout << "left button press" << endl;
cout << point.x << " " << point.y << endl;
if(point.x>=16&&point.x<=182&&point.y>=30&&point.y<=63){cout << "You clicked A!!" << endl;}
}
else
{
goto buttonpress;
}
break;
}
default:{
printf("unknown\n");
break;}
}
}
goto buttonpress;
return 0;
}

Related

C++ Clear cin buffer without user input

I have a loop running that displays data read from a serial port on a console screen, the same screen lists some options for the user, such as the option to enter a file name that data would be logged to, or exit. The user presses the required key to enter the desired option.
After the key press and the option function entered any key presses can be ignored/discarded but I am not able to find a way to discard the key presses/ clear the cin buffer resulting in the key press being shown on the console.
Is there any way to clear the cin buffer without the user having to take any action? Or possibly some better way to provide the same functionality?
A stripped down version of code here:
(this should display the local time while giving the user the option to enter a "file name" or exit, no serial port stuff included)
#include <iostream>
#include <limits>
#include <chrono>
#include <windows.h>
using namespace std;
// global variables
char SavedFileName[20];
HANDLE hConsole;
CONSOLE_SCREEN_BUFFER_INFO csbi;
// functions
void GetTime(void){
char buffer [20];
time_t now = time(NULL);
strftime(buffer , 20, "%H:%M:%S %d/%m/%Y", localtime(&now));
cout << "local time: " << buffer << endl;
}
bool is1keypressed(void){
if(GetAsyncKeyState(49) & 0x8000){
return true;
}
return false;
}
bool is9keypressed(void){
if(GetAsyncKeyState(57) & 0x8000){
return true;
}
return false;
}
int ClearConsole(void){
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SMALL_RECT scrollRect;
COORD scrollTarget;
CHAR_INFO fill;
// Get the number of character cells in the current buffer.
if (!GetConsoleScreenBufferInfo(hConsole, &csbi))
return 0;
// Scroll the rectangle of the entire buffer.
scrollRect.Left = 0;
scrollRect.Top = 0;
scrollRect.Right = csbi.dwSize.X;
scrollRect.Bottom = csbi.dwSize.Y;
// Scroll it upwards off the top of the buffer with a magnitude of the entire height.
scrollTarget.X = 0;
scrollTarget.Y = (SHORT)(0 - csbi.dwSize.Y);
// Fill with empty spaces with the buffer's default text attribute.
fill.Char.UnicodeChar = TEXT(' ');
fill.Attributes = csbi.wAttributes;
// Do the scroll
ScrollConsoleScreenBuffer(hConsole, &scrollRect, NULL, scrollTarget, &fill);
return 0;
}
int main(){
bool exit = false;
// clear the console
ClearConsole();
// main loop
while(!exit){
// Move the cursor to the top left corner too.
csbi.dwCursorPosition.X = 0;
csbi.dwCursorPosition.Y = 0;
SetConsoleCursorPosition(hConsole, csbi.dwCursorPosition);
// prints the local time each on iteration of loop
GetTime();
// user options
cout << "\npress (1) enter file name - saved file name is: " << SavedFileName;
cout << "\npress (9) to exit." << endl;
// test user options
if(is1keypressed()){
cout << "option 1 - press ENTER to proceed " << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
bool validentry = false;
while(!validentry){
cout << "Enter a file name, max 19 characters: ";
cin.getline(SavedFileName, 20, '\n');
if(cin.fail()){
validentry = false;
cout << endl << "File name is too large, enter 19 characters or less. " << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}else
validentry = true;
}
ClearConsole();
}
exit = is9keypressed();
}
cout << "\n end program" << endl;
return 0;
}
I have experimented with cin.ignore, this leaves the user to press Enter to proceed. Without cin.ignore the user has to delete the "option" characters themselves.
Okay, Windows console stuff. Only works on Windows.
Playing with this stuff is never as easy as advertised, but for what you want to do, we can make some pretty stripped-down Windows-only code. (If, at some future point, you wish to port to Linux, the design here can be pretty easily swapped-out for similar code on Linux.)
Minimal Windows Console Functions
#include <ciso646>
#include <utility>
#include <windows.h>
namespace console
{
inline auto hStdIn () { return GetStdHandle( STD_INPUT_HANDLE ); }
inline auto hStdOut() { return GetStdHandle( STD_OUTPUT_HANDLE ); }
auto CSBI()
{
CONSOLE_SCREEN_BUFFER_INFO _csbi;
GetConsoleScreenBufferInfo( hStdOut(), &_csbi );
return _csbi;
}
void clear_screen()
// Function
// Clears the screen to blanks using the current text color and puts the
// cursor in the upper-left cell.
{
DWORD _count;
DWORD _cell_count;
COORD _home_coords = { 0, 0 };
auto _csbi = CSBI();
_cell_count = _csbi.dwSize.X * _csbi.dwSize.Y;
if (!FillConsoleOutputCharacter( hStdOut(), (TCHAR)' ', _cell_count, _home_coords, &_count )) return;
if (!FillConsoleOutputAttribute( hStdOut(), _csbi.wAttributes, _cell_count, _home_coords, &_count )) return;
SetConsoleCursorPosition( hStdOut(), _home_coords );
}
void goto_xy( int x, int y )
// Function
// Position the text cursor on the screen.
//
// Arguments
// x - column coordinate, starting at zero from the left.
// y - row coordinate, starting at zero from the top.
{
COORD _coords = { (SHORT)x, (SHORT)y };
SetConsoleCursorPosition( hStdOut(), _coords );
}
bool is_key_pressed( int timeout_ms = 0 )
// Function
// Wait for key input to become available.
//
// Argument
// The maxiumum amount of time to wait, expressed in milliseconds.
// May also be one of the following:
// 0 - return immediately.
// -1 - wait indefinitely.
//
// Returns
// true : If key input is available
// false : If not
{
auto is_key_event_waiting = []()
{
DWORD _n;
INPUT_RECORD _rec;
while (PeekConsoleInputW( hStdIn(), &_rec, 1, &_n) and _n)
{
if (_rec.EventType != KEY_EVENT) continue;
if (_rec.Event.KeyEvent.bKeyDown) return true;
if ((_rec.Event.KeyEvent.wVirtualKeyCode == VK_MENU) and _rec.Event.KeyEvent.uChar.UnicodeChar)
return true;
ReadConsoleInputW( hStdIn(), &_rec, 1, &_n );
}
return false;
};
// Get rid of all unwanted events
if (is_key_event_waiting()) return true;
// Else wait until event available or timeout
if (WaitForSingleObject( hStdIn(), (DWORD)timeout_ms ) != WAIT_OBJECT_0) return false;
// We only want key events
return is_key_event_waiting();
}
char32_t read_key()
// Function
// Wait for and return the next key input available.
//
// Returns
// 0 : next read_key() returns a Windows Virtual Key Code, like `VK_F1` or `VK_UP`
// Unicode code point : any normal input key
{
static char32_t _vkey = 0;
if (_vkey) return std::exchange( _vkey, 0 );
DWORD _n;
INPUT_RECORD _rec;
while (true)
{
ReadConsoleInputW( hStdIn(), &_rec, 1, &_n );
if (_rec.EventType == KEY_EVENT)
{
// Key release events for the ALT/MENU key where UnicodeChar != 0
// means an Alt-code was entered on the numeric keypad. We'll report that.
// All other key release events are ignored.
if (!_rec.Event.KeyEvent.bKeyDown)
if ((_rec.Event.KeyEvent.wVirtualKeyCode != VK_MENU) or !_rec.Event.KeyEvent.uChar.UnicodeChar)
continue;
// Normal Unicode code point
if (_rec.Event.KeyEvent.uChar.UnicodeChar)
return _rec.Event.KeyEvent.uChar.UnicodeChar;
// Special keys and function keys
_vkey = _rec.Event.KeyEvent.wVirtualKeyCode;
return 0;
}
}
}
} // namespace console
Notes:
It works on both the Windows Terminal and on both current and past versions of the Windows Console.
There is no initialization or finalization necessary to use this particular magic.
Assumes a human is present. More robust code would check that the standard streams are attached to a console/terminal at initialization and complain (and quit) if not.
Only one thread should ever be messing with the console, so if you are doing anything multi-threaded, only console:: from one of them, ever. (This is pretty much true of any user-application UI.)
Your program, updated
#include <ctime>
#include <iostream>
#include <string>
std::string SavedFileName;
void DrawMenu(){
console::clear_screen();
console::goto_xy(0, 1);
std::cout << "\npress (1) enter file name - saved file name is: " << SavedFileName;
std::cout << "\npress (9) to exit.\n";
}
void DrawTime(){
char buffer [20];
time_t now = time(NULL);
strftime(buffer , 20, "%H:%M:%S %d/%m/%Y", localtime(&now));
console::goto_xy(0, 0);
std::cout << "local time: " << buffer << " ";
}
int main(){
DrawMenu();
bool done = false;
while(!done){
DrawTime();
if (console::is_key_pressed(500))
switch (console::read_key()){
case '1':
console::goto_xy(0, 5);
std::cout << "option 1\nEnter a file name: ";
getline(std::cin, SavedFileName);
DrawMenu();
break;
case '9':
console::goto_xy(0, 5);
std::cout << "option 2\nReally quit (y/[n])? ";
{
auto c = console::read_key();
if (!c) console::read_key();
else done = (c == 'y') or (c == 'Y');
}
if (!done) DrawMenu();
break;
case 0:
// Ignore special and function keys
console::read_key();
break;
}
}
std::cout << "\n\nGood-bye\n";
}
Notes:
I tend to alphabetize #includes, which makes it easier when the list gets long. Also, <chrono> is for time-based C++ functions, but you use the C date-time stuff from <ctime>. Be careful to include the correct headers.
It is a good idea to get used to writing std:: in front of everything. It is generally a bad idea to simply dump the entire Standard Library into your program’s namespace[citation needed].
Strings in C++ are, in general, best handled with the std::string class. Please use it. It’ll make your life infinitely easier.
The Draw*() functions are carefully organized to work together. This requires you to have a good idea of how your output is going to look.
DrawMenu() clears the screen and draws only the menu options
DrawTime() only draws the time
We do not clear the display every frame. Clearing the console is an expensive operation, often producing a noticeable blink. Avoid it if possible. In our case, we only want to update the time every frame, and redraw everything only after we have finished with some user interaction.
The frame rate is set by how long we are willing to wait for input to be available. In this example I choose 500 ms, which is a reasonably snappy response time. Avoid going below 150–250 ms, as that bogs the processor down with your wait loop. (This is why we don’t use _kbhit() from <conio.h>.)
Be careful with names. The way you name things makes a difference.
DrawTime() is a better name than GetTime() because it better informs you what it is doing — drawing the time for the user. It doesn’t actually get (and return) the time (to the caller).One way to think about it is that the fact that getting the time is a subtask of showing the time.
done is a better name than exit because it is a nominative state, not an action. (And also because it does not clash with the Standard Library exit() function name.)
The general rule of thumb is to use nouns for objects and verbs for functions.
Commentary should only exist to clarify code, not to describe it. For example, commentary in the console:: namespace describes how user functions are to be used. The only commentary in the main program reminds us why we are calling console::get_key() again, which is otherwise non-obvious.
Compiling and running the code is as easy as concatenating the two code-blocks together and running it through the compiler.
MSVC
cl /EHsc /W4 /Ox /std=c++17 example.cpp /D_CRT_SECURE_NO_WARNINGS
Clang (or MinGW)
clang++ -Wall -Wextra -Werror -pedantic-errors -O3 -std=c++17 example.cpp -o example.exe -D_CRT_SECURE_NO_WARNINGS

Attempt to print string return results in random characters (long read)

This might take a bit to explain.
I'm making a text-based game for a programming class. I'm attempting to describe the environment the player is currently in, based on their input.
First step:
if (playerChoice == 3) { //If they choose option 3, run the describe environment function
bool restart = CheckSurroundings(PlayerCharacter, ItemList, Map);
}
Next step:
static bool CheckSurroundings(Player &PlayerCharacter, vector <Item> &ItemList, vector<Area> &Map) {
cout << Map[PlayerCharacter.Position()].GetDescription() << endl;
//cut unimportant stuff out
return false;
}
What's above currently outputs a smiley face on my desktop computer, and printed an entirely different character when ran on my laptop.
"Map" is a vector of Area objects:
int mapSize = 17;
vector <Area> Map (mapSize);
SetupMapID(Map, itemList, mapSize);
The inclusion of the function SetupMapID is important. It's where I "set up" the map vector by giving all of the area objects inside the vector their name, description, and additional attributes. This is a preview of what it looks like:
static void SetupMapID(vector<Area> &Map, vector <Item> ItemList, int mapSize) {
Map[0].Name("The Mall Entrance Hallway");
Map[0].Description("The entrance to the mall has been blockaded with various benches, trash cans empty vending machines, and the occaisonal wood plank. This won't keep the zombies out for long.");
Map[0].Accessible(true);
}
Here is the actual area class header file (or, some of it, with what's unimportant cut out)
class Area {
private:
std::string areaName;
std::string areaDescription;
public:
std::string Name() { //GetName
return areaName;
}
void Name(std::string newName) { //SetName
areaName = newName;
}
//Area Description
std::string Description() { //GetDescription
return areaDescription;
}
void Description(std::string newDesc) { //SetDescription
areaDescription = newDesc;
}
};
EDIT: In case someone asks, I DO have a constructor set up, where description is given the string of "A".
Now that all of the context that should need to be given has been given, I can jump back up to the cout statement from above:
cout << Map[PlayerCharacter.Position()].Description() << endl;
Map, is as you saw, a vector of Area objects, and I'm looking to grab the Area object that the Player is currently in, which can be retrieved with PlayerCharacter.Position() (this returns 0 - 15). So "Map[PlayerCharacter.Position()]" gets that Area object. Then, after that. The function Description(), with no params, returns the object's description string. Nothing super complicated, but for reasons unknown to me, instead of returning the string it ought to be returning ("The entrance to the mall has been blockaded..."), it returns a random ASCII character (I think ASCII?). On my desktop computer, it always returns a smiley face.
This is where I'm stuck. Why is it returning a smiley face? The Name() function functions as intended (returns the area's "name" as a string), and everything associated with THAT is called stylistically the exact same.
Please help! I can work around it by just creating an array of strings and throwing the Area's # at it, but I would most prefer avoiding that.
I'm wondering if the problem has something to do with running out of space? My instructor thought it might have something to do with using reference variables, but I have absolutely no clue why Name() works but Description() doesn't.
I spent 5 minutes assembling your snippets (via copying and pasting) into an actual compilable and executable code snippet.
If I compile the below with gcc on Linux, I get the following output (copied and pasted straight from the console window):
The Mall Entrance Hallway
The entrance to the mall has been blockaded with various benches, trash cans empty vending machines, and the occaisonal wood plank. This won't keep the zombies out for long.
So it works, code is below. The broken part is not in what you showed us OR there's an issue with your compiler or environment, which I think is extremely unlikely but now you can just copy the below and test for yourself. I made some extremely simplifying assumptions on the parts you didn't show us, so you're going to have to share those or figure out why your outcome is different. My first suggestion would be to put your actual Player class and Position() function in place of mine. I wonder if perhaps your call to Position modifies the state of the internal member somehow so the first call (for Name) works but the second call doesn't. Just a thought.
#include <iostream>
#include <vector>
using namespace std;
class Area {
private:
std::string areaName;
std::string areaDescription;
public:
std::string Name() { //GetName
return areaName;
}
void Name(std::string newName) { //SetName
areaName = newName;
}
//Area Description
std::string Description() { //GetDescription
return areaDescription;
}
void Description(std::string newDesc) { //SetDescription
areaDescription = newDesc;
}
};
static void SetupMapID(vector<Area> &Map, int mapSize) {
Map[0].Name("The Mall Entrance Hallway");
Map[0].Description("The entrance to the mall has been blockaded with various benches, trash cans empty vending machines, and the occaisonal wood plank. This won't keep the zombies out for long.");
}
class Player {
public:
int Position() {
return 0;
}
};
int main()
{
int mapSize = 17;
vector <Area> Map (mapSize);
SetupMapID(Map, mapSize);
Player PlayerCharacter;
cout << Map[PlayerCharacter.Position()].Name() << endl;
cout << Map[PlayerCharacter.Position()].Description() << endl;
return 0;
}

opencv how can I select a region of image irregularly with mouse event? c/c++ [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I'm learning opencv recently. Is there a way to select a region of image using mouse event? I already try out the triangle one. What if I want to choose a specific region but not a triangle shape? Thank you!
I had a little attempt at this - it is probably not the cleanest code, but should give you some ideas.
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
// Globals
bool finished=false;
Mat img,ROI;
vector<Point> vertices;
void
CallBackFunc(int event,int x,int y,int flags,void* userdata)
{
if(event==EVENT_RBUTTONDOWN){
cout << "Right mouse button clicked at (" << x << ", " << y << ")" << endl;
if(vertices.size()<2){
cout << "You need a minimum of three points!" << endl;
return;
}
// Close polygon
line(img,vertices[vertices.size()-1],vertices[0],Scalar(0,0,0));
// Mask is black with white where our ROI is
Mat mask= Mat::zeros(img.rows,img.cols,CV_8UC1);
vector<vector<Point>> pts{vertices};
fillPoly(mask,pts,Scalar(255,255,255));
img.copyTo(ROI,mask);
finished=true;
return;
}
if(event==EVENT_LBUTTONDOWN){
cout << "Left mouse button clicked at (" << x << ", " << y << ")" << endl;
if(vertices.size()==0){
// First click - just draw point
img.at<Vec3b>(x,y)=Vec3b(255,0,0);
} else {
// Second, or later click, draw line to previous vertex
line(img,Point(x,y),vertices[vertices.size()-1],Scalar(0,0,0));
}
vertices.push_back(Point(x,y));
return;
}
}
int main()
{
// Read image from file
img=imread("demo.jpg");
// Check it loaded
if(img.empty())
{
cout << "Error loading the image" << endl;
exit(1);
}
//Create a window
namedWindow("ImageDisplay",1);
// Register a mouse callback
setMouseCallback("ImageDisplay",CallBackFunc,nullptr);
// Main loop
while(!finished){
imshow("ImageDisplay",img);
waitKey(50);
}
// Show results
namedWindow("Result",1);
imshow("Result",ROI);
waitKey(5000);
}
Explanation of the Callback
At the beginning of main() I do this:
setMouseCallback("ImageDisplay",CallBackFunc,nullptr);
and that tells OpenCV to call the function CallBackFunc() for us whenever the mouse moves or is clicked.
CallBackFunc() is just a normal function like any other function. However, we don't ever call it ourselves - OpenCV calls it for us asynchronously (when we aren't expecting it). Because of that, we can't see any returned value from the function - since we never called it - and that is why it is declared as:
void CallBackFunc(...)
because it returns nothing, or a void, or a dirty great void of nothingness.
Ok, let's move on to the parameters it is called with. Basically, when the designers of OpenCV wrote the setMouseCallback() function, they couldn't know what I would want to pass to it as a parameter - maybe I would want to pass a filename, maybe I would want to pass a text string to draw on the image when the mouse is clicked, maybe I would want to pass a variable that I wanted updated with the mouse position. It could be anything. So, as they didn't know, they decided to say it is a "pointer to anything" and I can use it for whatever I like. Well, nothing is anything, so a pointer to void can point to anything. So, they say just pass a pointer to void that you know the meaning of. Then, inside CallBackFunc() you can just cast the pointer to whatever it was you passed since you know what it is.
So, in summary a pointer to void is just a place-marker for a pointer to something and you can decide what that something is without the OpenCV designers needing to know.
Hope that helps!

C++ code reduction for identical submenus

I am coding my way to the last project of the semester and I have a code duplication issue.
I am using ncurses or pdcurses to make a menu to interact with the user.
The problem: For each choice of the menu(Five in total) I need a submenu. The submenu's only difference from the main menu is, the array of Items to be printed, and the parameters that go into some functions, as a result of the Items array size. Since I need five submenus, I need five times the same code(six if you add the main menu).
Could any of you help me make a function that does the same thing, which I'll then call six times to create my menu?
Here's my code
void Menu(){
const char* Items[]={
"[1]...New tax declaration",
"[2]...Modify tax declaration",
"[3]...Cancel tax declaration",
"[4]...Additional Information",
"[5]...Exit"
};
int Cur=0;
int ch, i;
int flag=0;
do{
werase(wm);
mvwaddstr(wm, 2, 16, "MENU");
for(int i=0; i<5;i++){
if(i==Cur)
wattr_on(wm, A_REVERSE, 0);
mvwaddstr(wm, 4+i, 4, Items[i]);
wattr_off(wm, A_REVERSE, 0);
}
mvwaddstr(wm, 14, 3, "Choice: ");
wprintw(wm, "%1d", Cur+1);
wrefresh(wm);
ch=wgetch(wm);
switch(ch){
case '1':Cur=0;Sub2();break;
case '2':Cur=1;Sub1();break;
case '3':Cur=2;break;
case '4':Cur=3;break;
case '5':flag=1;break;
case KEY_UP:
case KEY_LEFT: Cur--; if (Cur<0) Cur=4; break;
case KEY_DOWN:
case KEY_RIGHT: Cur++; if(Cur>4) Cur=0; break;
case 27: flag=1; break;
case 32:
case 13:
switch (Cur){
case 0:Sub2();break;
case 1:Sub1();break;
case 2:break;
case 3:break;
case 4:flag=1;break;
}
}
}while(!flag);
}
Thank you.
p.s The code is from a book. I have little experience with ncurses
A simple menu-driven program. This is based on using std::map instead of conditional logic. This map stores a list of menuitem structures that define what the menu looks like and what each option does.
This is best explained as we work through the code, so let's dive in!
// headers for everything used in this example
#include <iostream>
#include <string>
#include <map>
#include <functional>
#include <cctype>
// function to perform menu option B sub option 1
void optionB1()
{
std::cout << "perfoming B1" << std::endl;
}
// function to perform menu option B sub option 2
void optionB2()
{
std::cout << "perfoming B2" << std::endl;
}
// function to perform menu option A
void optionA()
{
std::cout << "perfoming A" << std::endl;
}
// defines a menu item. Good naming can often save the need to comment
struct menuitem
{
std::function<void()> doIt; // function to run if option chosen
std::string description; // pretty message describing option
};
// draw menu and wait for the user to select an option.
void domenu(const std::map<char, menuitem> & menu)
{
while (true) // loop until user gives a good option. Or use a retry count.
// You decide.
{
for (auto &items : menu)
{ // for all items in the menu, print out the item and it's description text
// for what first and second mean, read up on std::map and std::pair
std::cout << items.first << ") " << items.second.description << std::endl;
}
char ch;
std::cin >> ch; // get the user's choice
// often you may want to eliminate one of the cases to reduce the amount
// of possible inputs you need to provide handling code for.
// the line below allows us to use the same code for input of A and a.
ch = std::tolower(ch); // convert input to lower case
try
{
menu.at(ch).doIt(); // call the function mapped to user's choice.
// this may do produce something or it may
// display another menu. It could end the wor--
return; // done.
}
catch (...)
{ // print error message on unsupported input
std::cout << "Error. Invalid option!" << std::endl;
}
}
}
// the B menu
std::map<char, menuitem> bmenu
{ // User input doIt function Description
{'1', {optionB1, "Option B1"}},
{'2', {optionB2, "Option B2"}}
// add more options here. Or don't. Up to you.
};
// the main menu
std::map<char, menuitem> mainmenu
{ // User input doIt function Description
{'a', {optionA, "Option A"}},
{'b', {std::bind(domenu, bmenu), "Option B"}}
// OK, so that last one was a bit weird. std::bind makes a function and
// specifies the arguments with which it will be called. This takes
// domenu binds it with bmenu so that std::function<void()> is
// satisfied. As far as the world is concerned, the bound function
// returns nothing and takes no parameters. Very complicated functions
// can be bound so long as the end result returns nothing and requires
// no parameters.
// what it's doing here is allowing us to call domenu to draw the B
// submenu, wait for valid input, and call the chosen function.
};
// good 'ol trusty main so we can test that the above code isn't utter BS.
int main()
{
while (true) // loop forever. Or use whatever exit logic is required.
{
domenu(mainmenu); // kick-start by calling do menu to run the main menu
}
return(0);
}
This will keep the code down to a minimum. All of the duplicated code is reduced to the domenu function and a smurfload of code hidden from sight in the standard library and written by folks who likely have far more experience in getting this stuff right than you or I. Whenever possible, stand on the shoulders of giants.
domenu is driven by lists of options and execution instructions for the option. Want another option? Add an item to a list and possibly provide a new function to fulfill the obligations of that option.
All you have to do is fill in the blanks.

How to make a function change a global array permanently

Recently I've been working on an inventory system for a text-based game that uses a global array for the inventory system and a corresponding function to read true or false in said array. The problem I've run into is this, the function I'm using to modify the array
void playerGet(bool items[], int itemNumber) //this function takes an assigned argument of the array indices variable, and changes that array indices from true, to false.
{
items[itemNumber] = true;
}
only modifies the array within the scope of the function its housed in. The array is defined in a .cpp file like this:
void inventoryArray(bool items[]) //This function establishes all the items in the game, the true false statement expresses whether or not the item is in the player's inventory.
{
items[WEAPON_RELIC_RIFLE] = false;
items[WEAPON_SCALPEL] = false;
items[MISC_ACTION_FIGURE] = false;
items[MISC_FIRE_EXTINGUISHER] = false;
items[MISC_LIFE_RAFT] = false;
}
and is then declared in a .h file like this:
void inventoryArray(bool items[]);
the enums used in the array are defined in a header file like this:
enum equipment //This declares a list of enums for each item in the game, consumables, not included.
{
WEAPON_RELIC_RIFLE, // = 0
WEAPON_SCALPEL, // = 1
MISC_ACTION_FIGURE, // = 2
MISC_FIRE_EXTINGUISHER, // = 3
MISC_LIFE_RAFT, // = 4
MAX_EQUIPMENT
};
the function that reads the inventory array is this:
void twoScavengerCombat(bool items[])
{
for (int item = 0; item < MAX_EQUIPMENT; ++item)
{
if (items[item] == true) //if true proceed
{
switch (item)
{
case 0: //if array indices identifier = 0, print relic rifle
cout << "1: Use the Relic Rifle\n";
break;
case 1:
cout << "2: Use the Scalpel\n";
break;
case 2:
break;
case 3:
cout << "3: Use the Fire Extingusher\n";
break;
case 4:
cout << "4: Use the Life Raft\n";
break;
default:
cout << "Error";
break;
}
}
else
cout << "Option Unavailible\n"; //if false print Option Unavailible
}
compiled, with the array and enums headers declared the main file would look like this:
int toolSearch()
{
bool items[MAX_EQUIPMENT];
inventoryArray(items);
playerGet(items, 0);
}
void twoScavengerCombat(bool items[])\\ declared in this file, but since its just above here i left it as a forward declaration to save space
int main()
{
toolSearch();
twoScavengerCombat(items);
return 0;
}
Ideally this would produce the result: Use Relic Rifle
Option Unavailable
Option Unavailable
Option Unavailable
Option Unavailable
but instead it produces 5 Option Unavailable's. What am I missing?
You would want
//bunch of #include<> directives
bool items[MAX_EQUIPMENT];
int toolSearch()
{
inventoryArray();
playerGet( 0);
}
void twoScavengerCombat()
...
// other functions here
int main()
{
toolSearch();
twoScavengerCombat();
return 0;
}
Note that bool items[MAX_EQUIPMENT]; is not defined in a function. It is off on it's own at the top of the file in plain view of anything defined below it. This is what it means to be global. Anyone and everyone can access it, if they know where it is or you tell them where it is with an extern statement. It is created when the program starts (even before main and that can cause some really fun debugging if the initialization logic of the variable is faulty) and dies only when the program does.
Lightness Races in Orbit delves a bit deeper here, but is more concerned with making a global variable extend past a single file
There is no need to pass items into any function because everyone can see items The downside is there is one and only one items so if you have multiple players each with different item lists, you're going to have problems.
You might want to look into std::vector (resizable array) and std::map (which will allow you to look items up by name items["sword"].attackFoe(foe);) and std::set (which makes it really easy to see what a player has (if (items.find("Vorpal Hand Grenade") != items.end()) BlowStuffUp();) rather than having to search through each item every time.