Can You Forward Declare Member Functions?

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

Just define the functions outside of the class definition. This is no different than free function declarations (prototypes) and definitions:

class A;
class B {
 public:
  A *a;
  void fun(); // declaration
};
class A {
 public:
  int i;
};
void B::fun() { // definition
  Serial.println(a->i);
}

It is common practice to move the function definition into a separate implementation file. If you really want it in a header, the definition should be marked inline.

Terrible idea, never delete a raw pointer you were given, you have no idea where it was allocated, it could be on the stack, or using a custom allocator.
Use a smart pointer instead to handle the ownership.

You should never call new and delete explicitly and high-level code. Use RAII containers and smart pointers. If you really need to implement low-level memory management (there's no need for it here), you must follow the The rule of three/five/zero - cppreference.com. The Invoker class violates this rule, which will lead to interesting bugs when you create copies (implicitly or explicitly).

Well... After all that typing and thinking I did... and you solve it with nine words. Top banana!

Coming from the Arduino world I'm not too well versed in memory management. Only hearing about it in C++ tutorials where they drill into you that if you use new, you need to use delete.

Also in the command pattern reference link I posted they use the same format. Is the issue you mentioned apparent in their code too? If so how would you approach the same format of the code without using new/delete?

In “modern C++”, if you're doing memory management manually, you're probably doing it wrong :slight_smile:

This applies to high-level code of course, in specific areas and low-level code there might be good reasons to do it manually.

I might be wrong, but my guess is that the command pattern example on that site was written by someone more familiar with Java than C++. New in Java and C++ are quite different.

If the lifetime of all objects involved is the same, you can just use:

#include <iostream>
#include <string>

class Command {
 public:
  virtual ~Command() = default; // only necessary if you plan on deleting derived objects through pointers to base
  virtual void Execute() const = 0;
};

class SimpleCommand : public Command {
 private:
  std::string pay_load;

 public:
  explicit SimpleCommand(std::string pay_load) : pay_load(pay_load) {}
  void Execute() const override {
    std::cout << "SimpleCommand: See, I can do simple things like printing (" << pay_load << ")\n";
  }
};

class Receiver {
 public:
  void DoSomething(const std::string &a) {
    std::cout << "Receiver: Working on (" << a << ".)\n";
  }
  void DoSomethingElse(const std::string &b) {
    std::cout << "Receiver: Also working on (" << b << ".)\n";
  }
};

class ComplexCommand : public Command {
 private:
  Receiver *receiver;
  std::string a;
  std::string b;
 public:
  ComplexCommand(Receiver *receiver, std::string a, std::string b) : receiver(receiver), a(a), b(b) {}
  void Execute() const override {
    std::cout << "ComplexCommand: Complex stuff should be done by a receiver object.\n";
    receiver->DoSomething(a);
    receiver->DoSomethingElse(b);
  }
};

class Invoker {
 private:
  Command *on_start = nullptr;
  Command *on_finish = nullptr;

 public:
  void SetOnStart(Command *command) {
    on_start = command;
  }
  void SetOnFinish(Command *command) {
    on_finish = command;
  }

  void DoSomethingImportant() {
    std::cout << "Invoker: Does anybody want something done before I begin?\n";
    if (on_start) {
      on_start->Execute();
    }
    std::cout << "Invoker: ...doing something really important...\n";
    std::cout << "Invoker: Does anybody want something done after I finish?\n";
    if (on_finish) {
      on_finish->Execute();
    }
  }
};

int main() {
  Invoker invoker;
  SimpleCommand cmd1 {"Say Hi!"};
  invoker.SetOnStart(&cmd1);
  Receiver receiver;
  ComplexCommand cmd2 {&receiver, "Send email", "Save report"};
  invoker.SetOnFinish(&cmd2);
  invoker.DoSomethingImportant();
}

You might even change the constructor and SetOnX arguments to references instead of pointers, but that's a minor detail.

In some cases, you do need to make sure that the commands live at least as long as the invoker. Then you could save the commands as an owning std::unique_ptr:

#include <iostream>
#include <string>
#include <memory>

class Command {
 public:
  virtual ~Command() = default; // only necessary if you plan on deleting derived objects through pointers to base
  virtual void Execute() const = 0;
};

class SimpleCommand : public Command {
 private:
  std::string pay_load;

 public:
  explicit SimpleCommand(std::string pay_load) : pay_load(pay_load) {}
  void Execute() const override {
    std::cout << "SimpleCommand: See, I can do simple things like printing (" << pay_load << ")\n";
  }
};

class Receiver {
 public:
  void DoSomething(const std::string &a) const {
    std::cout << "Receiver: Working on (" << a << ".)\n";
  }
  void DoSomethingElse(const std::string &b) const {
    std::cout << "Receiver: Also working on (" << b << ".)\n";
  }
};

class ComplexCommand : public Command {
 private:
  Receiver receiver;
  std::string a;
  std::string b;
 public:
  ComplexCommand(Receiver receiver, std::string a, std::string b) : receiver(std::move(receiver)), a(a), b(b) {}
  void Execute() const override {
    std::cout << "ComplexCommand: Complex stuff should be done by a receiver object.\n";
    receiver.DoSomething(a);
    receiver.DoSomethingElse(b);
  }
};

class Invoker {
 private:
  std::unique_ptr<Command> on_start = nullptr;
  std::unique_ptr<Command> on_finish = nullptr;

 public:
  void SetOnStart(std::unique_ptr<Command> command) {
    on_start = std::move(command);
  }
  void SetOnFinish(std::unique_ptr<Command> command) {
    on_finish = std::move(command);
  }

  void DoSomethingImportant() {
    std::cout << "Invoker: Does anybody want something done before I begin?\n";
    if (on_start) {
      on_start->Execute();
    }
    std::cout << "Invoker: ...doing something really important...\n";
    std::cout << "Invoker: Does anybody want something done after I finish?\n";
    if (on_finish) {
      on_finish->Execute();
    }
  }
};

Invoker buildInvokerWithCommands() {
    Invoker invoker;
    invoker.SetOnStart(std::make_unique<SimpleCommand>("Say Hi!"));
    Receiver receiver;
    invoker.SetOnFinish(std::make_unique<ComplexCommand>(receiver, "Send email", "Save report"));
    return invoker;
}

int main() {
   Invoker invoker = buildInvokerWithCommands();
   invoker.DoSomethingImportant();
}

The receiver is saved by value: since it is not polymorphic, there's no need for dynamic allocation or pointers. Just store it as an ordinary member variable.

The invoker owns the two commands, so they are stored as std::unique_ptr. The commands must be alive as long as the invoker is alive, otherwise the invoker could have dangling pointers to commands that no longer exist.
Unique pointers cannot be copied (as the name implies), so you have to move them into place.

Note how the invoker is returned from a function, the commands should have the same lifetime. This is handled by std::unique_ptr automatically.

In C++11, which is the default on most Arduinos (by choice of Arduino, not because newer versions are not supported), you won't have access to std::make_unique, so you'll have to implement your own, or just write a naked new until you can use C++14. (There are some pitfalls to look out for when using new, but they are related to exception handling, so not really relevant on Arduinos without exception support. If you're using exceptions, use make_unique to avoid these pitfalls.)

So, what's a good book for moving on to "Modern C++"? I've been plodding along through various web pages. I find cppreference.com to be overly-pedantic and too dense in formalism and notation. cplusplus.com is a little bit better.

My usual technique of simply working my way through the source code doesn't work well in this case as it seems the entire STL code was intentionally written to be obtuse and impossible to understand.

I did start going through "Effective Modern C++" by Meyers. But, that starts at too high a level.

I need something that starts with the assumption that I'm proficient in "C with Classes" and takes me to the level where the Meyers book will be useful.

Thanks.

It's perfect as a language reference, but not for learning the language.

And also outdated, I wouldn't recommend it. Some of the tutorial pages are okay, but I wouldn't recommend it as a language or library reference, it hasn't been updated in years.

This is a good overview:

Effective Modern C++ is a good book, but it does indeed expect some prior knowledge.
What exactly did you find yourself struggling with in EMC++? You might be able to look for specific things in something like https://learncpp.com. Either way, learncpp is a good tutorial, whether you're starting from zero or from “C with classes”.

Another good source of “modern C++” practices are the CppCon and C++Now conference talks (they are all on YouTube). Most speakers are very experienced C++ programmers, and seeing how they think about code, how they apply best practices, and how they use the standard library is very helpful in my opinion. There are many different talks for different experience levels, if you find yourself struggling with a specific aspect of the language, you might be able to find a “back to basics” talk on the topic.

Although it doesn't explain everything, one of the limitations is that they are only allowed to use reserved identifiers, so they have to use underscores everywhere. Another reason is that they need it to be modular and dry for maintainability. That's no excuse for unreadable code, of course, but it's the way it is. The STL is among the most complex code that uses pretty much all of C++'s features, so I would never recommend it to someone who is learning the language.

In my experience, cppreference has all the information about standard library functions and classes you need in order to use them. It might be pedantic, but that's just because programming languages (and computers in general) are pedantic.
If the implementation of a certain function is simple enough, cppreference sometimes even includes a “possible implementation”, which aims to be readable (in contrast to the actual implementation that ships with your compiler), e.g. std::find.
Thanks to the complete documentation on cppreference, I've never had the need to dig into the GCC implementation of the standard library (except out of curiosity). The C++ standard library is not a library, it is a specification for an interface to a library. So while reading a specific implementation (GCC, Clang and MSVC all have their own implementation) could be interesting, it's not required to use it.

i think the best way to learn is to review other developers code.