Accessing a derived class member from a base class

Hi all.

I am trying to implement a simple yet easily extended command parser for AT-commands. I want to implement it so anyone adding new commands has as little work to do as possible. I have a base class called Command which is then extended by each specific command that needs to be implemented. I then have a map that holds a string and a function pointer so that I can easily use each command without a horrible big switch statement. The code is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using namespace std;

enum CommandType {ACTION, SET, READ, TEST};

class Command {
	
	public:
		static string action() {cerr<<"Invalid use of command"<<endl; return "ERROR";}
		static string set() {cerr<<"Invalid use of command"<<endl; return "ERROR";}
		static string read() {cerr<<"Invalid use of command"<<endl; return "ERROR";}
		static string test() {cerr<<"Invalid use of command"<<endl; return "ERROR";}
		
		static string execute(CommandType cmdType) {
			switch(cmdType) {
				case ACTION :
					return action();
					break;
				case SET :
					return set();
					break;
				case READ :
					return read();
					break;
				case TEST :
					return test();
					break;
			}
			cerr<<"Invalid use of command"<<endl;
			return "ERROR";
		}
		
};

class ECommand : public Command {
	
	public:
		static string set() {cout<<"E set"<<endl; return "OK";}
		static string read() {cout<<"E read"<<endl; return "OK";}
		static string test() {cout<<"E test"<<endl; return "OK";}
		
};


ECommand is an example command that has set, test and read functions. To make my program "know" about the new command, I simply add to a map a function pointer:

1
2
map<string, string(*)(CommandType)>cmdMap;
cmdMap["E"] = ECommand::execute;


The problem is, even though I point to execute() via the derived class ECommand, the execute member in the base class will only ever call other base class functions, but I want it to call functions from the derived class. I can obviously see why it's doing what it's doing, but can't think of a way around it. I don't want to have to create instances of my derived classes and store them, because there are going to be about 50 odd commands and I'll have 50 odd instances immediately at the start of my program regardless of whether they are needed or not (hence the static methods).

Any ideas or possibly even completely different ways of doing this would be much appreciated.

Thanks

Donovan
Last edited on
If I understood correctly, you're making new commands by deriving from Command, right?
Personally, I think it would be easier to have a class that contained all commands as methods. Then adding pointers to them to the map.
Then again, I may be missing something.

Anyway, see this http://www.cplusplus.com/forum/beginner/11157/
Sorry, I didn't explain the problem very well. The reason I picked a class for each command rather than just a function is because each command has up to 4 uses, which are represented by the action(), set(), read() and test() members above. Each command uses 1 or more of these, and so will not necessarily implement them all. I did initially just have a pointer to a single function for each command, but then within every command I will need a further switch to decide on it's use. I wanted to take that out and just have it written once. I'm just a bit stumped on a nice clean way of doing this.

Thanks

Donovan
Well, the only way I can imagine that wouldn't use a switch is one function for each "use" of the command (e.g. command_action(), command_set(), command_read(), and command_test()), then instead of passing a command name string and a type, you pass something like "command.s". The map was filled with
cmdMap["command.a"]=&command_action();
cmdMap["command.s"]=&command_set();
cmdMap["command.r"]=&command_read();
cmdMap["command.t"]=&command_test();
If there's a use that doesn't belong with a command, you just don't add it to the list.

That wouldn't use a switch anywhere, but I don't know if it would make it simpler to add commands.
Your problem is easily solved with polymorphism (this is exactly the kind of thing it's for). Simply make action/set/read/test virtual in the base class.

The catch is, a function can't be static and virtual, which means none of these functions can be static. This means you'll need an instance to an object (a 'this' pointer), so you can't just call the function direct. This can be solved by making these classes singleton classes, and having a 'get' style function which returns a pointer to the singleton object.

A bit wordy ... here's the code to illustrate.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Command
{
public:
  virtual string action() { /*...*/ }
  virtual string set() { /*...*/ }
  virtual string read() { /*...*/ }
  virtual string test() { /*...*/ }

  string execute(CommandType cmdType)  // note:  not static!
  {  /* ... */  }
};

// for derived classes

class ECommand : public Command
{
public:
  // singleton 'get me' function
  static ECommand* getobj() { static ECommand obj;  return &obj; }

  // implement whatever virtual functions you want
  virtual string test() { /*...*/ }
};


Then... for your std::map, instead of using a function pointer, use a Command*
1
2
map<string,Command*>cmdMap;
cmdMap["E"] = ECommand::getobj();
This all might be overkill. What else do all these commands share? If every function is essentially self-standing, why not just:

1
2
3
4
5
6
7
8
9
namespace AT {
    int E_set()  { std::cout << "E_set\n";  return 0; }
    int E_read() { std::cout << "E_read\n"; return 0; }
    int E_test() { std::cout << "E_test\n"; return 0; }
};
int main() {
    AT::E_set();
    AT::E_test();
}

If I am understanding what you are after you are trying to implement a template method pattern. However to implement this pattern correctly you will need some kind of polymorthism, either static or dynamic. It looks to me that you are trying to implement said polymorthism statically using dynamic polymorpism contructs. You seem to be half way between the two.

Choose to ether implement the template method using either normal inheritance. In other words ditch the static member functions and just go with virtual member functions (dynamic polymorphism). If you must keep the static member functions you are going to need to use templates in order to implement what you want.

The code at the end will demo what you might want.


#include <iostream>

template< class T >
struct B
{
static void run();
static void run2();
static void run3();
};


struct D : B< D >
{
static void run2();
};


template< class T >
void B< T >::run()
{
std::cout << "B::run" << std::endl;
T::run2();
T::run3();
};

template< class T >
void B< T >::run2()
{
std::cout << "B::run2" << std::endl;
}


template< class T >
void B< T >::run3()
{
std::cout << "B::run3" << std::endl;
}

void D::run2()
{
std::cout << "D::run2" << std::endl;
};

int main()
{
D::run();
return 0;
}


This will output

B::run
D::run2
B::run3

Of course this is how you would implement the polymorthism statically. Refer to previous posts on how you would go about doing dynamic polymorthism

Hope this helps.
Last edited on
Thanks to everyone who took the time to reply to my message. I decided to go with the template method pattern because I want to keep the static methods. Works perfectly so thanks very much. For any future reference if anyone else has the same problem, here is the what the code was changed to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using namespace std;

enum CommandType {ACTION, SET, READ, TEST};
enum Result {OK, ERROR};

template <class T>
struct Command {
	
	static Result action() {cerr<<"Invalid use of command"<<endl; return ERROR;}
	static Result set() {cerr<<"Invalid use of command"<<endl; return ERROR;}
	static Result read() {cerr<<"Invalid use of command"<<endl; return ERROR;}
	static Result test() {cerr<<"Invalid use of command"<<endl; return ERROR;}
	
	static Result execute(CommandType cmdType) {
		switch(cmdType) {
			case ACTION :
				return T::action();
				break;
			case SET :
				return T::set();
				break;
			case READ :
				return T::read();
				break;
			case TEST :
				return T::test();
				break;
		}
		cerr<<"Invalid use of command"<<endl;
		return ERROR;
	}
	
};

struct ECommand : Command<ECommand> {
	
	static Result set() {cout<<"E set"<<endl; return OK;}
	static Result read() {cout<<"E read"<<endl; return OK;}
	static Result test() {cout<<"E test"<<endl; return OK;}
		
};


Thanks again

Donovan
Last edited on
Topic archived. No new replies allowed.