Problem with std::map of function pointers

Discussion in 'C++' started by DevNull, Feb 25, 2007.

  1. DevNull

    DevNull Guest

    Hi there everyone,
    I'm creating a very simple immediate mode command interpreter.
    The final purpose is to provide a pluggable control and command
    console for a MUD server I have written.

    The basic theory is we wrap the functions we want exposed to the
    console in a function with a prototype of

    int func(State*)

    the State* is an object that contains both
    args, and results as std::vector<std::string>

    Next we tie the wrapped functions up with a
    std::map<std::string,Func*>

    So that we can call the functions by name.

    The problem I'm presently having is coming from the compiler, and
    reads...
    console.h:39: error: expected constructor, destructor, or type
    conversion before '=' token

    Line 39 reads...
    FunctionList["print"] = &print;

    That should be perfectly legal since my relevant code looks like...

    typedef int(*Func)(const State&);
    typedef std::map<std::string, Func*> FL;
    FL FunctionList;

    int print(State& state){
    for(int count = 0; count < state.argc(); count++){
    std::cout << state.args[count];
    }
    return 0;
    }
    FunctionList["print"] = &print;

    I have googled, and searched through years of newsgroup postings and
    I'm not finding anything that might be causing this problem.
    I'm hoping someone can help.

    Below is a full code listing, it's only 56 lines including includes
    and comments, so I'm hoping no one minds me posting it, my hope is
    that someone can point out something I've missed.

    Thanks in advance for the help!

    #include <iostream>
    #include <vector>
    #include <map>

    class State{

    public:
    std::vector<std::string>
    args,//Argument List
    result; //Final Result

    ~State(){
    while(args.size() != 0){
    args.pop_back();
    }

    while(result.size() != 0){
    result.pop_back();
    }
    }

    inline int argc(){return(args.size());}
    };

    typedef int(*Func)(const State&);

    typedef std::map<std::string, Func*> FL;
    typedef std::map<std::string, Func*>::value_type MapItem;
    FL FunctionList;

    int print(State& state){
    for(int count = 0; count < state.argc(); count++){
    std::cout << state.args[count];
    }
    return 0;
    }

    FunctionList["print"] = &print;

    int execute(Func* func,std::vector<std::string>& args){
    State state;
    state.args.swap(args);
    (*func)(state);
    }

    int parse(std::vector<std::string>& input){
    FL::iterator iter = FunctionList.find(input.at(0));
    if(iter != FunctionList.end()){
    Func* func = iter->second;
    execute(func,input);
    }else{
    std::cout << "Error: Command " << input.at(0) << " was not found!"
    << std::endl;
    }
    }
     
    DevNull, Feb 25, 2007
    #1
    1. Advertising

  2. DevNull wrote:
    > Hi there everyone,
    > I'm creating a very simple immediate mode command interpreter.
    > The final purpose is to provide a pluggable control and command
    > console for a MUD server I have written.
    >
    > The basic theory is we wrap the functions we want exposed to the
    > console in a function with a prototype of
    >
    > int func(State*)
    >
    > the State* is an object that contains both
    > args, and results as std::vector<std::string>
    >
    > Next we tie the wrapped functions up with a
    > std::map<std::string,Func*>
    >
    > So that we can call the functions by name.
    >
    > The problem I'm presently having is coming from the compiler, and
    > reads...
    > console.h:39: error: expected constructor, destructor, or type
    > conversion before '=' token
    >
    > Line 39 reads...
    > FunctionList["print"] = &print;
    >
    > That should be perfectly legal since my relevant code looks like...
    >
    > typedef int(*Func)(const State&);
    > typedef std::map<std::string, Func*> FL;
    > FL FunctionList;
    >
    > int print(State& state){
    > for(int count = 0; count < state.argc(); count++){
    > std::cout << state.args[count];
    > }
    > return 0;
    > }
    > FunctionList["print"] = &print;
    >
    > I have googled, and searched through years of newsgroup postings and
    > I'm not finding anything that might be causing this problem.
    > I'm hoping someone can help.
    >
    > Below is a full code listing, it's only 56 lines including includes
    > and comments, so I'm hoping no one minds me posting it, my hope is
    > that someone can point out something I've missed.
    >
    > Thanks in advance for the help!
    >
    > #include <iostream>
    > #include <vector>
    > #include <map>
    >
    > class State{
    >
    > public:
    > std::vector<std::string>
    > args,//Argument List
    > result; //Final Result
    >
    > ~State(){
    > while(args.size() != 0){
    > args.pop_back();
    > }
    >
    > while(result.size() != 0){
    > result.pop_back();
    > }
    > }
    >
    > inline int argc(){return(args.size());}
    > };
    >
    > typedef int(*Func)(const State&);
    >
    > typedef std::map<std::string, Func*> FL;
    > typedef std::map<std::string, Func*>::value_type MapItem;
    > FL FunctionList;
    >
    > int print(State& state){
    > for(int count = 0; count < state.argc(); count++){
    > std::cout << state.args[count];
    > }
    > return 0;
    > }
    >
    > FunctionList["print"] = &print;
    >
    > int execute(Func* func,std::vector<std::string>& args){
    > State state;
    > state.args.swap(args);
    > (*func)(state);
    > }
    >
    > int parse(std::vector<std::string>& input){
    > FL::iterator iter = FunctionList.find(input.at(0));
    > if(iter != FunctionList.end()){
    > Func* func = iter->second;
    > execute(func,input);
    > }else{
    > std::cout << "Error: Command " << input.at(0) << " was not found!"
    > << std::endl;
    > }
    > }
    >


    It's simple enough, assignments are not allowed at global scope. You
    must put all assignments in a function.

    int main()
    {
    FunctionList["print"] = &print;
    }

    should compile.

    john
     
    John Harrison, Feb 25, 2007
    #2
    1. Advertising

  3. DevNull

    DevNull Guest

    On Feb 25, 4:28 am, John Harrison <> wrote:
    > DevNull wrote:
    > > Hi there everyone,
    > > I'm creating a very simple immediate mode command interpreter.
    > > The final purpose is to provide a pluggable control and command
    > > console for a MUD server I have written.

    >
    > > The basic theory is we wrap the functions we want exposed to the
    > > console in a function with a prototype of

    >
    > > int func(State*)

    >
    > > the State* is an object that contains both
    > > args, and results as std::vector<std::string>

    >
    > > Next we tie the wrapped functions up with a
    > > std::map<std::string,Func*>

    >
    > > So that we can call the functions by name.

    >
    > > The problem I'm presently having is coming from the compiler, and
    > > reads...
    > > console.h:39: error: expected constructor, destructor, or type
    > > conversion before '=' token

    >
    > > Line 39 reads...
    > > FunctionList["print"] = &print;

    >
    > > That should be perfectly legal since my relevant code looks like...

    >
    > > typedef int(*Func)(const State&);
    > > typedef std::map<std::string, Func*> FL;
    > > FL FunctionList;

    >
    > > int print(State& state){
    > > for(int count = 0; count < state.argc(); count++){
    > > std::cout << state.args[count];
    > > }
    > > return 0;
    > > }
    > > FunctionList["print"] = &print;

    >
    > > I have googled, and searched through years of newsgroup postings and
    > > I'm not finding anything that might be causing this problem.
    > > I'm hoping someone can help.

    >
    > > Below is a full code listing, it's only 56 lines including includes
    > > and comments, so I'm hoping no one minds me posting it, my hope is
    > > that someone can point out something I've missed.

    >
    > > Thanks in advance for the help!

    >
    > > #include <iostream>
    > > #include <vector>
    > > #include <map>

    >
    > > class State{

    >
    > > public:
    > > std::vector<std::string>
    > > args,//Argument List
    > > result; //Final Result

    >
    > > ~State(){
    > > while(args.size() != 0){
    > > args.pop_back();
    > > }

    >
    > > while(result.size() != 0){
    > > result.pop_back();
    > > }
    > > }

    >
    > > inline int argc(){return(args.size());}
    > > };

    >
    > > typedef int(*Func)(const State&);

    >
    > > typedef std::map<std::string, Func*> FL;
    > > typedef std::map<std::string, Func*>::value_type MapItem;
    > > FL FunctionList;

    >
    > > int print(State& state){
    > > for(int count = 0; count < state.argc(); count++){
    > > std::cout << state.args[count];
    > > }
    > > return 0;
    > > }

    >
    > > FunctionList["print"] = &print;

    >
    > > int execute(Func* func,std::vector<std::string>& args){
    > > State state;
    > > state.args.swap(args);
    > > (*func)(state);
    > > }

    >
    > > int parse(std::vector<std::string>& input){
    > > FL::iterator iter = FunctionList.find(input.at(0));
    > > if(iter != FunctionList.end()){
    > > Func* func = iter->second;
    > > execute(func,input);
    > > }else{
    > > std::cout << "Error: Command " << input.at(0) << " was not found!"
    > > << std::endl;
    > > }
    > > }

    >
    > It's simple enough, assignments are not allowed at global scope. You
    > must put all assignments in a function.
    >
    > int main()
    > {
    > FunctionList["print"] = &print;
    >
    > }
    >
    > should compile.
    >
    > john


    Wow thank you that did the trick!
    Something like that you'ld think the compile would have a specific
    message for.
     
    DevNull, Feb 25, 2007
    #3
  4. DevNull

    roy axenov Guest

    On Feb 25, 1:28 pm, John Harrison
    <> wrote:
    > DevNull wrote:
    > > I'm creating a very simple immediate mode command
    > > interpreter. The final purpose is to provide a
    > > pluggable control and command console for a MUD server
    > > I have written.

    >
    > > The basic theory is we wrap the functions we want
    > > exposed to the console in a function with a prototype
    > > of

    >
    > > int func(State*)

    >
    > > the State* is an object that contains both
    > > args, and results as std::vector<std::string>

    >
    > > Next we tie the wrapped functions up with a
    > > std::map<std::string,Func*>

    >
    > > So that we can call the functions by name.

    >
    > > FunctionList["print"] = &print;

    >
    > It's simple enough, assignments are not allowed at global
    > scope. You must put all assignments in a function.
    >
    > int main()
    > {
    > FunctionList["print"] = &print;
    > }


    There seem to be a number of other problems with the code.

    > > typedef int(*Func)(const State&);
    > > typedef std::map<std::string, Func*> FL;


    Are you sure you want to store pointers to pointers?

    > > int print(State& state){


    int print ( const State & state )

    > > for(int count = 0; count < state.argc(); count++){
    > > std::cout << state.args[count];
    > > }
    > > return 0;
    > > }

    >
    > > FunctionList["print"] = &print;


    Once you fix constness, & print will be a Func, but you're
    trying to store Func * in your map, so this won't work
    anyway.

    --
    roy axenov
     
    roy axenov, Feb 25, 2007
    #4
  5. DevNull

    DevNull Guest

    On Feb 25, 4:45 am, "roy axenov" <> wrote:
    > On Feb 25, 1:28 pm, John Harrison
    >
    >
    >
    > <> wrote:
    > > DevNull wrote:
    > > > I'm creating a very simple immediate mode command
    > > > interpreter. The final purpose is to provide a
    > > > pluggable control and command console for a MUD server
    > > > I have written.

    >
    > > > The basic theory is we wrap the functions we want
    > > > exposed to the console in a function with a prototype
    > > > of

    >
    > > > int func(State*)

    >
    > > > the State* is an object that contains both
    > > > args, and results as std::vector<std::string>

    >
    > > > Next we tie the wrapped functions up with a
    > > > std::map<std::string,Func*>

    >
    > > > So that we can call the functions by name.

    >
    > > > FunctionList["print"] = &print;

    >
    > > It's simple enough, assignments are not allowed at global
    > > scope. You must put all assignments in a function.

    >
    > > int main()
    > > {
    > > FunctionList["print"] = &print;
    > > }

    >
    > There seem to be a number of other problems with the code.
    >
    > > > typedef int(*Func)(const State&);
    > > > typedef std::map<std::string, Func*> FL;

    >
    > Are you sure you want to store pointers to pointers?
    >
    > > > int print(State& state){

    >
    > int print ( const State & state )
    >
    > > > for(int count = 0; count < state.argc(); count++){
    > > > std::cout << state.args[count];
    > > > }
    > > > return 0;
    > > > }

    >
    > > > FunctionList["print"] = &print;

    >
    > Once you fix constness, & print will be a Func, but you're
    > trying to store Func * in your map, so this won't work
    > anyway.
    >
    > --
    > roy axenov


    Thank you, I have fixed those, they were typos.
     
    DevNull, Feb 25, 2007
    #5
  6. DevNull

    Chris Theis Guest

    "DevNull" <> wrote in message
    news:...
    > On Feb 25, 4:45 am, "roy axenov" <> wrote:
    >> On Feb 25, 1:28 pm, John Harrison

    [SNIP]
    > Thank you, I have fixed those, they were typos.


    Just as a general remark: you might consider making your function table a
    static map, as you most probably need only one unique instance, and wrap in
    a class which also takes take of initializing, function lookup etc. IMO this
    would give you cleaner and more flexible design, but naturally that's a
    personal opinion.

    Cheers
    Chris
     
    Chris Theis, Feb 25, 2007
    #6
  7. DevNull

    DevNull Guest

    On Feb 25, 7:43 am, "Chris Theis" <chris.theis@n o s p am.cern.ch>
    wrote:
    > "DevNull" <> wrote in message
    >
    > news:...
    >
    > > On Feb 25, 4:45 am, "roy axenov" <> wrote:
    > >> On Feb 25, 1:28 pm, John Harrison

    > [SNIP]
    > > Thank you, I have fixed those, they were typos.

    >
    > Just as a general remark: you might consider making your function table a
    > static map, as you most probably need only one unique instance, and wrap in
    > a class which also takes take of initializing, function lookup etc. IMO this
    > would give you cleaner and more flexible design, but naturally that's a
    > personal opinion.
    >
    > Cheers
    > Chris


    Done and thank you for the feedback!
     
    DevNull, Feb 26, 2007
    #7
  8. On Feb 26, 12:37 pm, "DevNull" <> wrote:
    > On Feb 25, 7:43 am, "Chris Theis" <chris.theis@n o s p am.cern.ch>
    > wrote:
    > > Just as a general remark: you might consider making your function table a
    > > static map, as you most probably need only one unique instance, and wrap in
    > > a class which also takes take of initializing, function lookup etc. IMO this
    > > would give you cleaner and more flexible design, but naturally that's a
    > > personal opinion.

    >
    > > Cheers
    > > Chris

    >
    > Done and thank you for the feedback!


    You should also take a look at using functor like objects. They may be
    easier to use and initialise. If you have a functor super-class that
    places itself into the map on initialisation (and removes itself on
    destruction) then your maintenance will be easier because you just
    declare a single const object for each functor type.

    Simplified pseudo-code something like this:

    class Functor;
    map< string, const Functor * > g_functors;

    class Functor {
    protected:
    Functor( string name ) {
    g_functors[ name ] = this;
    }
    virtual ~Functor() {
    }
    public:
    virtual void execute( State * ) = 0;
    };


    Now for each one you just do this:

    const class Print : class Functor {
    public:
    Print() : Functor( "print" ) {}
    void execute( State * ) const {
    // Whatever it does
    }
    } c_print;

    This saves you from adding a function and remembering to add it to the
    map as that is done automatically.

    It also makes it easy to extend as you can just dynamically load
    additional code at the time of executions (i.e. on Windows use
    LoadLibrary to fetch a new DLL, which can even be specified on the
    command line - I'm sure there must be something similar on UNIX
    variants) and all of the new functions in the new code will
    automatically become available.

    This solution is more decoupled that storing raw function pointers. It
    also allows for the objects to be initialised in different ways to
    configure closely related functions. I.e.:

    const class Print : class Functor {
    public:
    Print( ostream &stream, string name ) : Functor( name ),
    m_stream( ostream ) {}
    void execute( State * ) const {
    // Whatever it does
    }
    private:
    std::eek:stream &m_ostream;
    } c_print( std::eek:ut, "print" ), c_log( g_mylogstream, "log" );


    K
     
    =?iso-8859-1?q?Kirit_S=E6lensminde?=, Feb 26, 2007
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Peter Jansson
    Replies:
    5
    Views:
    6,317
    Ivan Vecerina
    Mar 17, 2005
  2. Replies:
    1
    Views:
    428
    red floyd
    Dec 21, 2008
  3. Thomas J. Gritzan
    Replies:
    6
    Views:
    1,025
    James Kanze
    Dec 22, 2008
  4. James Kanze
    Replies:
    0
    Views:
    2,005
    James Kanze
    Dec 21, 2008
  5. tombert
    Replies:
    3
    Views:
    650
    Juha Nieminen
    Aug 25, 2012
Loading...

Share This Page