Circular Dependancies Command Pattern

Hello, I'm certainly not an expert programmer but I've been trying to learn about various design patterns.

My project uses the command pattern but I'm having trouble with some circular dependancies.

In essence here's the format:

I have a board that is loaded with a board state (such that board states can be interchangeable later).
A board state can contain one or many commands. A command inherits from the iCommand interface and executes some custom code.

Calling execute on the board calls execute on the board state which in this example calls execute on the only command.

What I'm trying to achieve though, is have a command that is able to call public functions on the board.

My initial thought was to forward declare the board class and have the iCommand class hold a static variable which is a pointer to a board. That way any commands can inherit from iCommand and also use the pointer.

Here's as far as I've gotten but I can't make it work. I'm trying to call board.test() from inside a concrete command.

Any ideas where this is falling short?

P.s please excuse that this is in C++ and not arduino with setup() loop() I've been testing without the arduino at this stage.

#include <iostream>
using namespace std;

class Board;

class iCommand{
	public:
	static Board* board;
	 virtual void execute(){}
	 iCommand(){}
	};
	
class Command : public iCommand
{
	public:
	
	void execute(){iCommand::board->test();}
	Command(){}
	};
	
class BoardState{
	public:
	iCommand* command = new Command;
	
	void execute(){command->execute();}
	};
	
class Board{
	public:
	BoardState* boardState = new BoardState;
	
	void execute(){boardState->execute();}
	void test(){cout << "Success!";}
	Board(){}
	
};




int main(int argc, char *argv[])
{
	Board board;
	iCommand::board = &board;
	board.execute();
}

Error I'm getting here is

/storage/emulated/0/Download/newfile.cxx:17:32: error: member access into incomplete type 'Board'
        void execute(){iCommand::board->test();}
                                      ^
/storage/emulated/0/Download/newfile.cxx:4:7: note: forward declaration of 'Board'
class Board;
      ^
1 error generated.

I guess to speak to the design pattern a little more, I have an invoker that is also a receiver.

I have solved it and came back in case other people need a possible solution too.

Firstly, after throwing poop at walls until things stuck I found that I needed the Inline keyword for the static class variable... I don't know why that works, but it's giving me something new to research! I thought Inline replaced function calls with copy paste of code but I guess it's different in a class.

Secondly, and maybe somewhat obviously, the Command class didn't know about the definition of the test function at that time. So I simply had to declare the execute function in Command, but define it after the Board class, at which point the compiler new about what the heck test() was inside Board.

Here's the code that now works:

#include <iostream>
using namespace std;

class Board;

class iCommand
{
  public:
	static inline Board *board;
	static void setBoard(Board *newNew)
	{
		iCommand::board = newNew;
		cout << "Set new board ptr" << '\n';
	}
	virtual void execute() {}
};

class Command : public iCommand
{
  public:
	void execute();
};

class BoardState
{
  public:
	iCommand *command = new Command;

	void execute() { command->execute(); }
};

class Board
{
  public:
	BoardState *boardState = new BoardState;

	void execute() { boardState->execute(); }
	void test() { cout << "Success!"; }
};

void Command::execute()
{
	cout << "command executed" << '\n';
	iCommand::board->test();
}

int main(int argc, char *argv[])
{
	Board board;
	iCommand::setBoard(&board);
	board.execute();
}

If anyone has any insights or explanations to add, or can maybe explain what/why is happening better than my feeble attempt, then please feel free.

I tried to sort this out but... Its a mess.

First, STOP trying to write everything in your class definitions. This isn't java. Breaking the code from the definitions will 1/2 your problem right off the bat.

Second, decide WHERE things are being created. AND Who "owes" these new objects? Because if you don't know who's creating them or who owns them, you are going to hemorrhage memory.

Third STOP with the static calls into your objects. All that is doing is confusing the issue. This is like a garage that is supposed to fix a car and doing then FIX on.. All cars? Makes very little sense in very few cases.

Try again, and get things layed out first.

-jim lee

Hey Jim THANKS for the heads up. Kindly, how would you structure this ? (because I don't know what I don't know, obviously so it's structured in the best way I could think)

It's also possible that for the real use case this is the better solution. My 'board' in this case has a whole bunch of functionality outside of firing off commands on button presses.

Imagine something similar a TV remote. What I'm basically trying to do is have a remote class with a set of buttons and hook up a command to each button. But organise it such that any button could have its commands very easily swapped around, or more added. You know the whole open to extension closed for modification malarkey.

I'm not sure if you're familiar with the command pattern or not but it's a pretty standard design pattern I've read about in several books one from the Gang of Four and one called Head First Design Patterns and they both structure it in a very similar way. I learned about it here Command Pattern – Design Patterns (ep 7) - YouTube

If you know of more, of better ways to do things then I'm all ears and eager to get Googling.

I'll tell you this, and I tell most people this but no one ever listens or understands..

STOP trying to code the thing until you fully grok it in human. When you can write a detailed user manual for a moron. Then the actual code will almost fall out.

Get your logic straight, get what you want it to do straight. Get how it interacts with the user straight. This is your framework.

-jim lee

Well, I've learnt something from all this:

grok

grokked; grokking

Definition of grok

transitive verb

: to understand profoundly and intuitively

But I would also be curious to see a real use case for this design pattern applied to managing a micro controller application.

I have all that laid out. And from what I've learnt about design patterns so far the command pattern suits my use case the best, and this is (hopefully) what that pattern looks like.

Commands generally in my case act on a set of data that gets adjusted and sent out of the device but occasionally I need to have the commands act 'internally' if you will. Hence my question pertaining on how to use commands to act up the chain on the board.

Well, I've used it a lot. Specially when I get in over my head. Actually, not a lot now after umpteen years of writing application code. But I can spot the same lost patterns in others. :slight_smile:

My first job out of college, I was given an assembled desktop CNC milling machine and told to "Write the brain for this." Everything from the UI for the milling language editor (and the milling language) down to the motor control.

Oh yes, and I got an original IBM PC to do it with. Sadly, with a bad RAM chip so every time you really pushed it, it would crash. This is where I discovered I should write the user manual first. And that saved my butt.

-jim lee

i think all you did was create a Board class within your iCommand class that is different from the Board class defined outside of iCommand.

not sure what your trying to accomplish. if two classes are calling function/methods within each other, shouldn't they be just one class?

The idea there was for all instances of the iCommand class to have access to a single pointer to an instance of the board class (as for what I'm doing I'll only have one instance of the board class anyway).

I'm not sure how a static class variable pointing to an address of a class instance would create a class within a class?

The above does seem to work. When I execute the single example command I get the "Success" output I was hoping for from inside the Board class.

However, the walls came crumbling down when I tried to partition everything into .cpp and .h files to keep it organised and now I'm getting errors.

I'll attach a zip file containing the mock up I've made. I've changed it slightly to better reflect my use case.

The user can define custom commands in the custom_commands.h and custom_commands.cpp files. This allows the user to enter whatever they need as a command and so long as they inherit from iCommand and implement the execute() function it can be called.

The boardState now has a vector of iCommand* called commandSlots. In this example I've added 5 slots and hooked each slot up to one of the custom commands.

The board will prompt the user a slot to execute and then will perform execute() on whatever custom command has been hooked up to that slot.

In my 'real' use case I have 16 footswitches, and each will have a command fired off when the footswitch button is pressed.

What I'm trying to achieve as before, is to be able to call the board.test() function from inside of one of the custom commands.

I'm using Visual Studio Code on Mac for the below attachment.
Board Application Mock Up.zip (17.0 KB)

You'll see inside the custom_commands.cpp file, inside the execute() function of the ExampleCommandZero class, that I've commented out the function call I'd like to make which is iCommand::board->test(); When I call that I get [custom_commands.cpp] pointer to incomplete class type "Board" is not allowed

=============================

The reason I have laid my code out this way is to make it super easy to extend the project. Simply define new commands in the custom command class and push them back in the boardState constructor. Or swap them as you see to change which slots do which commands.

The plan is to have many boardStates that will eventually inherit from boardState and each will contain their own 'profile' of commands that can be executed. Kind of like a 'mode' maybe. So if the board is in Mode1 then the ModeOneState will have its collection of custom commands. Then if we change the board to Mode2 then the current state will be ModeTwoState and that will have a new set of custom commands etc.

Rather annoyingly, if I cut the definition of the ExampleCommandZero from custom_commands.cpp and paste it just above int main() (after the #include for board.h) then I can uncomment the iCommand::board->test(); and enter choice '0' and I get the 'Success!' message I'm after.

So again, cut the definition and paste inside main.cpp like this:

#include "board.h"

void ExampleCommandZero::execute()
{
  std::cout << "Custom Code Executed by ExampleCommandZero\n\n";
  iCommand::board->test();
}

int main()
{
  Board board;
  iCommand::setBoard(&board);
  board.askForCommandSlotChoice();
  return 0;
}

That gives me the "Success!" output I need, but It's now no longer tidy.

How can I package that away inside the custom_commands.cpp too? I can't put it in the .h file because I get the same pointer to incomplete type error.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.