Excuse C++ rather than Arduino but I'm sandboxing this with 'OnlineGDB'.
I'm using the 'Command Pattern' Design Pattern (You can read about it here: Command in C++ / Design Patterns or plenty of other books/resources online) as the base for my project.
Okay, given the above... I have a 'Command' class and an 'Invoker' class. An 'Invoker' holds a bunch of 'Commands' to be called whenever needed.
#include <iostream>
class Command
{
public:
void execute(){ std::cout << "Command Executed" << '\n'; }
~Command(){ std::cout << "Garbage Collected so don't flame me for using 'new' :P" << '\n'; }
};
class Invoker
{
public:
Command* command;
void execute(){ command->execute(); }
Invoker(Command* command) { this->command = command; }
~Invoker(){ delete command; }
};
int main()
{
Invoker invoker(new Command);
invoker.execute();
return 0;
}
The above code works great, and as expected. For use cases (not mine but to give examples), you can imagine a TV Remote where each button needs to do something unrelated, or maybe a Smart Home controller where one button does lights, another button locks the doors, another button turns the radio on etc.
My issue comes when I want to be able to manipulate something in the Invoker from inside a Command. How would I do that?
Let's say just for the sake of argument that the target here is calling a test() function of the invoker from inside a Command.
My first thought it forward declare the Invoker class, and let Command hold a pointer to an Invoker:
#include <iostream>
class Invoker;
class Command
{
public:
Invoker *invoker;
void setInvoker(Invoker* newInvoker){ invoker=newInvoker; }
void execute(){ std::cout << "Command Executed" << '\n'; }
~Command(){ std::cout << "Garbage Collected so don't flame me for using 'new' :P" << '\n'; }
};
class Invoker
{
public:
Command* command;
void execute(){ command->execute(); }
void test(){ std::cout << "Test Worked!" << '\n';}
Invoker(Command* command) { this->command = command; }
~Invoker(){ delete command; }
};
int main()
{
Invoker invoker(new Command);
invoker.command->setInvoker(&invoker);
invoker.execute();
return 0;
}
So we've forward declared the Invoker class, added a pointer to an Invoker in Command. And for kicks, given a setInvoker() function so we can point the command at something.
Great! That compiles just fine. It's starting to look a little backwards to me, but it seems to work.
We can even call invoker.command->invoker->test(); (Yikes!) at the end in main() but it does work.
Now... My problem is if I try to move the call to test() into the command class. My guess is that at compile-time, Command knows nothing about the test function inside the Invoker class.
So given this final code:
#include <iostream>
class Invoker;
class Command
{
public:
Invoker *invoker;
void setInvoker(Invoker* newInvoker){ invoker=newInvoker; }
void execute(){ invoker->test(); }
~Command(){ std::cout << "Garbage Collected so don't flame me for using 'new' :P" << '\n'; }
};
class Invoker
{
public:
Command* command;
void execute(){ command->execute(); }
void test(){ std::cout << "Test Worked!" << '\n';}
Invoker(Command* command) {this->command = command; }
~Invoker(){ delete command; }
};
int main()
{
Invoker invoker(new Command);
invoker.command->setInvoker(&invoker);
invoker.execute();
return 0;
}
I get:
main.cpp:10:29: error: invalid use of incomplete type âclass Invokerâ
10 | void execute(){ invoker->test(); }
| ^~
main.cpp:3:7: note: forward declaration of âclass Invokerâ
3 | class Invoker;
| ^~~~~~~
So I think for the most part the circular dependency issue is solved with the forward declaration, but it's moving the function call inside Command class that I can't wrap my head around. It seems you can't forward declare the function with something like:
class Invoker;
void Invoker::test();