CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 15 of 15
  1. #1
    Join Date
    Apr 2008
    Posts
    9

    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.

    Here is the code... Any ideas?

    /******************************************
    #include "cstdlib"
    #include "string"
    #include "iostream"

    using namespace std;

    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())();
    };

    class MenuType
    {

    private:
    vector<Option> options;

    public:
    MenuType();
    void addOption( int option_number, string name, void (*func_name)() );
    void displayMenu();

    };

    /******class Option******/

    Option::Option( int option_num, string option_name, void (*func_name)() )
    {
    option_number = option_num;
    name = option_name;
    func_pointer = func_name;
    }

    void Option::callFunction()
    {
    if ( func_pointer != NULL )
    (*func_pointer)();
    }

    // Getters
    int Option::getOptionNumber()
    {return option_number;}

    string Option::getOptionName()
    {return name;}

    void (*Option::getFunctionPointer())()
    {return func_pointer;}

    /******class MenuType******/

    MenuType::MenuType()
    {}

    void MenuType::addOption( int option_number, string name, void (*func_name)() )
    {
    Option newoption( option_number, name, func_name );
    options.push_back( newoption );
    }

    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.

  2. #2
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,765

    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

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

  3. #3
    Join Date
    Nov 2002
    Location
    Los Angeles, California
    Posts
    3,863

    Re: Questions on a text-based menu class - I don't know what to do

    yes that is the question.
    Where are the arguments going to come from that are passed to the function
    Wakeup in the morning and kick the day in the teeth!! Or something like that.

    "i don't want to write leak free code or most efficient code, like others traditional (so called expert) coders do."

  4. #4

    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.

  5. #5
    Join Date
    Apr 2008
    Posts
    9

    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:
    Code:
    int main()
    {
    MenuType first_menu;
    first_menu.addOption( 1, "teacher a", printProfile( cout, teachera ) ); // where: printProfile( ostream& fout , TeacherType& teacher );
    first_menu.addOption( 2, "teacher b", printProfile( cout, teacherb ) ); 
    first_menu.addOption( 3, "teacher c", printProfile( cout, teacherc ) );
    first_menu.displayMenu();
    }
    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.

    With no arguments it could be called as:
    Code:
    void Option::callFunction()
    {
    if ( func_pointer != NULL )
    (*func_pointer)();
    }
    Is there a way to tackle this problem without function pointers?

  6. #6

    Re: Questions on a text-based menu class - I don't know what to do

    Quote 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.

  7. #7
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,765

    Re: Questions on a text-based menu class - I don't know what to do

    What I had in mind concerning function objects is something like this:
    Code:
    class MenuFunction
    {
    public:
        virtual void operator()() = 0;
        virtual ~MenuFunction() {}
    };
    
    class PrintProfile : public MenuFunction
    {
    public:
        PrintProfile(std::ostream& fout, TeacherType& teacher)
            : fout(fout), teacher(teacher) {}
        void operator()()
        {
            fout << teacher; // or whatever
        }
    private:
        std::ostream& fout;
        TeacherType& teacher;
    
        PrintProfile(const PrintProfile&);
        PrintProfile& operator=(const PrintProfile&);
    };
    
    int main()
    {
        MenuType first_menu;
        first_menu.addOption( 1, "teacher a", new PrintProfile( cout, teachera ) );
        first_menu.addOption( 2, "teacher b", new PrintProfile( cout, teacherb ) ); 
        first_menu.addOption( 3, "teacher c", new PrintProfile( cout, teacherc ) );
        first_menu.displayMenu();
    }
    
    void MenuType::addOption( int option_number, string name, MenuFunction* menu_function )
    {
        // ...
    }
    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

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

  8. #8

    Re: Questions on a text-based menu class - I don't know what to do

    This is probably very close to what you are looking for. Take it and learn from it, and develop your own system.
    Attached Files Attached Files

  9. #9
    Join Date
    Apr 2008
    Posts
    9

    Re: Questions on a text-based menu class - I don't know what to do

    Wow -- thanks to both of you! I need some time to try to understand exactly what is going on here but I will let you know how it ends up!

  10. #10
    Join Date
    Apr 2008
    Posts
    9

    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.

    Code:
    #include "iostream"
    #include "string"
    
    using namespace std;
    
    class vFunction
    	{
    		virtual void CallFunction(void * fnArgs) = 0; 
    	};
    
    template <typename Type, typename Argtype> class MenuFunction : public vFunction
    	{
    	public:
    		typedef Type (*FunctionType)(Argtype *);
    		MenuFunction( FunctionType call_back )
    		{
    			p_call_back = call_back;
    		}
    		
    		virtual void CallFunction( void * fnArgs )
    		{
    			(*p_call_back)(fnArgs);
    		}
    		
    	private:
    		FunctionType p_call_back;
    	};
    
    void display(int p)
    { cout<<"Number: "<<p<<endl; }
    
    int main()
    {
    	MenuFunction<void,int> first( display );
    	first.CallFunction(3);	
    }

  11. #11

    Re: Questions on a text-based menu class - I don't know what to do

    First of all, default class member access is private, so where you declare vFunction, you should use a public: member declaration area.

    Code:
    class vFunction
    {
    public:
        virtual void CallFunction(void * lpArgs) = 0;
    };
    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.

    Check your typedef.

    Code:
    typedef Type (*FunctionType)(Argtype *);
    Make display do this:

    Code:
    void display(int * pValue)
    {
        cout << *pValue << endl;
    }
    And, create a temporary integer to store 3, so you can pass it's address.

    Code:
    int arg = 3;
    
    obj.CallFunction(&arg);
    Then, you will also have to put a cast from void * to the ArgType * required by the typedef.

    Code:
    virtual void CallFunction( void * fnArgs )
    		{
    			(*p_call_back)( reinterpret_cast<ArgType *>(fnArgs) );
    		}
    Last edited by JamesSchumacher; April 16th, 2008 at 05:01 PM.

  12. #12
    Join Date
    Apr 2008
    Posts
    9

    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.

  13. #13

    Re: Questions on a text-based menu class - I don't know what to do

    Quote 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.

  14. #14
    Join Date
    Apr 2008
    Posts
    9

    Re: Questions on a text-based menu class - I don't know what to do

    Sweet! It feels so good when something finally works!

    Thanks James!

    For anyone who wants to see, this is what I ended up with:
    Code:
    template <typename Type, typename Argtype> class MenuFunction1arg
    	{
    	public:
    		typedef Type (*FunctionType)(Argtype);
    		MenuFunction1arg( FunctionType call_back, Argtype Arg1 ) : fnArgs( Arg1 ) // <-- added this
    		{
    			//fnArgs = Arg1; // <-- removed this
    			p_call_back = call_back;
    		}
    		Type CallFunction()
    		{
    			return (*p_call_back)(fnArgs);
    		}
    	private:
    		Argtype fnArgs;
    		FunctionType p_call_back;
    	};
    
    void display(int p)
    {
    cout<<"Number: "<<p<<endl; }
    
    float add( float one , float two )
    {
    	return one + two;
    }
    
    void print( ostream& fout )
    {
    	fout<<"This works."<<endl;
    }
    
    int main()
    {
    	MenuFunction1arg<void,int> first( display, 25 );
    	MenuFunction1arg<void,ostream&> second( print, cout );
    	first.CallFunction();
    	second.CallFunction();
    }

  15. #15

    Re: Questions on a text-based menu class - I don't know what to do

    Glad to hear it's working for you.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured