Questions on a text-based menu class - I don't know what to do
I am trying to create a text-based menu class to give myself a way to create more dynamic menus in programs. Basically the menu class has a vector of option classes that contain the menu-option's name, function to call etc.
The problem is that I want to be able to associate any function with an option. For example, say that option 1 calls func1( int, string ) and option 2 calls func2( string, ostream&, int ) and so on. Right now I am passing a function pointer but I don't think it is possible to have variable types of arguments with a function pointer.
class Option
{
private:
int option_number; // for display and vector purposes
string name; // title for user to read
void (*func_pointer)( void ); // function to be executed
public:
Option( int option_number, string name, void (*func_name)() ); // constructor, fills option vars
void callFunction(); // calls function stored in option
// Getters
int getOptionNumber();
string getOptionName();
void (*getFunctionPointer())();
};
void MenuType:isplayMenu()
{
for ( int i = 0 ; i < options.size() ; i++ )
{
cout<<options[i].getOptionNumber()<<". "<<options[i].getOptionName()<<endl;
}
cout<<"Enter a menu option: ";
int option_choice;
cin>>option_choice;
cin.ignore();
options[option_choice-1].callFunction();
}
/***** Used in a program like this *****/
int main()
{
MenuType first_menu;
first_menu.addOption( 1, "option 1", function ); // but here i would like to be able to put function( arg1, arg2 ). how?
first_menu.addOption( 2, "option 2", function2 ); // but be able to put function2( arg3, arg4, arg5 ) here
first_menu.addOption( 3, "option 3", function3 );
first_menu.displayMenu();
}
Is there a better way to do this? I'm sure there is.
Re: Questions on a text-based menu class - I don't know what to do
Kindly post code in code tags.
How do you plan to actually call the function associated? Like how would you know that an int and a string is needed, and what they are for?
It seems to me that all these functions should be self-contained, so they really should not take any arguments.
Originally, I wanted to suggest the use of function objects instead, but then I ran into the problem of how would you call operator() with different arguments, or even anticipate what arguments are needed. You could store whatever initial values are needed by using a constructor, but that is different from calling a function with a set of arguments.
C + C++ Compiler: MinGW port of GCC
Build + Version Control System: SCons + Bazaar
Re: Questions on a text-based menu class - I don't know what to do
The best way to do this is a delegate system.
Code:
class IDelegate
{
public:
virtual void Invoke(void * lpArgs) = 0; // It's virtual, implementers hold any data they want, can spread out args from a structure, pass structure, etc...
};
template <typename Type,typename ArgType> class CDelegate : public IDelegate
{
public:
typedef void (Type::*MemberCallback)(ArgType *);
CDelegate(Type * pObject,MemberCallback pfnCallback);
virtual void Invoke(void * lpArgs)
{
if (m_pObject != 0 && m_pfnCallback != 0)
{
(m_pObject->*m_pfnCallback)(reinterpret_cast<ArgType *>(lpArgs));
}
}
private:
Type * m_pObject;
MemberCallback m_pfnCallback;
};
For example. You can make multiple template classes to encompass any amount of options you want. Remember, it's designed around an abstract class. The thing is, you pass a structure with the data you need, either just reinterpret_cast to the appropriate structure type, or spread out args as necessary. You can even make a TCallback or something template class that doesn't use the arguments at all.
You could also add to the IDelegate interface an interface function that returned a void pointer (or a typed base class pointer) to the type of args that the delegate requires. The function that returns these args could be named something to the effect... "RetrieveUserDataArgs" and get the data from the user.
OR...
Here's an idea. Retrieve the data arguments from the user in the handler function itself.
Last edited by JamesSchumacher; April 15th, 2008 at 02:23 PM.
Re: Questions on a text-based menu class - I don't know what to do
ok so this is for a computer science class where I am writing a gradebook program that deals with different students, teachers, and courses. This is not specifically part of the assignment but I thought it would be really cool/useful if it works.
I'm not sure exactly what you are asking but I'll try to explain further.
Say I want have a menu for viewing teachers profiles:
I'm not sure how to call the function with these arguments. And I don't know how I could consolidate the functions to have no arguments. For example, the printProfile( ostream&, TeacherType& ) function above really needs the teacher object so that it can print more than one teacher's profile.
Re: Questions on a text-based menu class - I don't know what to do
Originally Posted by rustyj110
Is there a way to tackle this problem without function pointers?
Take a look at my proposed delegate system. It makes the functions pointers internal to an object. Therefore, another data structure holds the object through an IDelegate pointer, therefore... It calls Invoke() to invoke the function pointer through the IDelegate interface.
In this case I think it is applicable for what you want to do, but a more general solution would be to use something like what JamesSchumacher described. If you use raw pointers as in my example, remember to delete the MenuFunction pointers.
Last edited by laserlight; April 15th, 2008 at 02:40 PM.
C + C++ Compiler: MinGW port of GCC
Build + Version Control System: SCons + Bazaar
Re: Questions on a text-based menu class - I don't know what to do
Ok so I did a lot of research into virtual functions and I think I understand them at least a little. I reformatted James' code to try to make it work for functions that are not a member of any class. I got it to work with no arguments, but once I try to add arguments everything gets off. It has a few compile errors right now.
Any ideas? I really appreciate all of your guys' help.
Second of all, I see that since you defined it like I suggested, when you defined <void,int> to intend on a void return value with an integer argument, you did not make the connection that it made it require a function that took an int *.
This is what is the other error is, I am sure of it.
Re: Questions on a text-based menu class - I don't know what to do
Ok, so I ended up changing the code quite a bit. And it work! well.. for the most part... I have one more problem.
here is the code:
Code:
#include "iostream"
#include "string"
#include "ostream"
#include "fstream"
using namespace std;
// I found a way to do everything without a virtual function
template <typename Type, typename Argtype> class MenuFunction1arg
{
public:
typedef Type (*FunctionType)(Argtype);
MenuFunction1arg( FunctionType call_back, Argtype Arg1 )
{
fnArgs = Arg1;
p_call_back = call_back;
}
Type CallFunction()
{
return (*p_call_back)(fnArgs);
}
private:
Argtype fnArgs;
FunctionType p_call_back;
};
void display(int p) // example function
{
cout<<"Number: "<<p<<endl; }
void print( ostream& fout ) // example function, using ostream&
{
fout<<"This works."<<endl;
}
int main()
{
MenuFunction1arg<void,int> first( display, 25 ); // This works
first.CallFunction();
MenuFunction1arg<void,ostream&> second( print, cout ); // This does not work
second.CallFunction();
}
Ok so I changed it so that the constructor makes a copy of the argument and saves it to an object Argtype fnArgs. This works for almost all arguments, but the problem occurs when I pass an ostream& into the constructor, because it is not able to make a copy of the ostream& object. I don't think it can copy object references.
I would really like to find some way to make this function because I need to pass ostream& objects to almost all of the functions I want to call in my program.
How else can I do this?
I guess I could declare the ostream objects globally and include some other type of indicator variable in the function to tell it which ostream to use... ? That doesn't seem like good coding.
Re: Questions on a text-based menu class - I don't know what to do
Originally Posted by rustyj110
Ok, so I ended up changing the code quite a bit. And it work! well.. for the most part... I have one more problem.
here is the code:
Code:
#include "iostream"
#include "string"
#include "ostream"
#include "fstream"
using namespace std;
// I found a way to do everything without a virtual function
template <typename Type, typename Argtype> class MenuFunction1arg
{
public:
typedef Type (*FunctionType)(Argtype);
MenuFunction1arg( FunctionType call_back, Argtype Arg1 )
{
fnArgs = Arg1;
p_call_back = call_back;
}
Type CallFunction()
{
return (*p_call_back)(fnArgs);
}
private:
Argtype fnArgs;
FunctionType p_call_back;
};
void display(int p) // example function
{
cout<<"Number: "<<p<<endl; }
void print( ostream& fout ) // example function, using ostream&
{
fout<<"This works."<<endl;
}
int main()
{
MenuFunction1arg<void,int> first( display, 25 ); // This works
first.CallFunction();
MenuFunction1arg<void,ostream&> second( print, cout ); // This does not work
second.CallFunction();
}
Ok so I changed it so that the constructor makes a copy of the argument and saves it to an object Argtype fnArgs. This works for almost all arguments, but the problem occurs when I pass an ostream& into the constructor, because it is not able to make a copy of the ostream& object. I don't think it can copy object references.
I would really like to find some way to make this function because I need to pass ostream& objects to almost all of the functions I want to call in my program.
How else can I do this?
I guess I could declare the ostream objects globally and include some other type of indicator variable in the function to tell it which ostream to use... ? That doesn't seem like good coding.
It will work if you initialize the ostream reference (&) in the initializer list of the constructor instead of the body of code for the constructor. References as member variables of a class have to be initialized in the constructor initializer list.
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.