2

I am trying to implement a very clean Command Pattern in a library.

I have the following structure right now (a few parts are still being finished up):

  1. users (client-code) have some Object, call it "Manager"
  2. Manager holds a collection of shared_ptr<Foo>
  3. Manager provides access to the collection by returning shared_ptr<Foo>
  4. I have a Command abstract class and a hierarchy of commands for actions to perform on Foo
  5. Client code should not call Command::execute(), only Manager should, Manager::execute(shared_ptr<Command>), so that it can handle undo/redo

I would like to follow the following rules:

  1. users (client-code) have some Object, call it "Manager"
  2. Manager holds a collection of shared_ptr<Foo>
  3. Manager provides access to the collection by returning shared_ptr<const Foo>
  4. I have a Command abstract class and a hierarchy of commands for actions to perform on Foo
  5. Client code cannot (without workarounds) call Command::execute(), only Manager can, Manager::execute(shared_ptr<Command>), so that it can handle undo/redo and get non-const smart pointers
  6. A Manager must be able to allow Command objects to access and modify shared_ptr<Foo> even though the user initializes Command objecst with shared_ptr<const Foo>

I am just trying to figure out the best way to handle giving out shared_ptr<const Foo> while allowing number 5 and 6 to work.

Is there any example/design pattern that does this which I could learn from? Is this a good idea compared to what I already have/am working on?

Matt
  • 5,478
  • 9
  • 56
  • 95
  • Instead of giving out `shared_ptr`, could you instead give out a key or index into the container from where it came? This would prevent having to deal with `const_cast` and its potential pitfalls. – Emile Cormier Apr 22 '11 at 19:50
  • @Emile Cormier I think that actually just handing out keys or indices would be a good idea. The only problem (possibly), is that I think I may want client code to be able to access the const members to read values. That's why having const pointers is nice. I would like to avoid const_cast by doing a lookup by pointer potentially. – Matt Apr 23 '11 at 04:59

3 Answers3

2

I think this passkey pattern should be the right thing for you:

class CommandExecuteKey{
private:
  friend class Manager;
  CommandExecuteKey(){}
};

class Command{
public:
  // usual stuff
  void execute(CommandExecuteKey);
};

class Manager{
public
  void execute(Command& cmd){
    // do whatever you need to do
    cmd.execute(CommandExecuteKey());
  }
};

Now, Command doesn't know anything about Manager, only about the key that is needed for the execute function. The user won't be able to call the execute method directly, as only Manager can create CommandExecuteKey objects, thanks to a private constructor and friendship.

int main(){
  Command cmd;
  Manager mgr;
  //cmd.execute(CommandExecuteKey()); // error: constructor not accessible
  mgr.execute(cmd); // works fine, Manager can create a key
}

Now, for your 6th point:
When you get the command in, search all your shared_ptr<Foo>s for the correct object (using the saved shared_ptr of the command as a search-key) and then pass that mutable one from your internal shared_ptrs back to the command.

Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • It seems that Command still *transitively* knows about Manager (via CommandExecuteKey). But this an interesting pattern, nonetheless. I've never heard of it before. – Emile Cormier Apr 22 '11 at 18:57
  • 1
    @Emile: Well, you know whom you hand your front-door keys to, too, no? ;) One advantage is, that you can easily swap the underlying friended class without `Command` ever noticing. The same with your front-door, it will never know whom you hand keys to. – Xeo Apr 22 '11 at 18:58
  • Hehe. :-) Oh I see now. It's like a friendship limited only to certain parts of the class. It's like me wanting to hand out my front door keys without having to give out my blood type and SSN. +1 – Emile Cormier Apr 22 '11 at 19:01
  • @Emile: Correct, it allows fine grained control over who can do what with your class. :) The linked question goes into further explanations. – Xeo Apr 22 '11 at 19:07
  • I do like this method, but I felt that the chosen answer fit better with what I want. Still, this is a really cool trick, thanks! – Matt Apr 24 '11 at 04:54
1

I'm not following your question 100%, but here goes...

The only thing I can think of for #5 is to make Command::execute private/protected and make Manager a friend of Command. The downside of this approach is that you've now introduced a dependency from Command to Manager.

As for #6, if the user's shared_ptr<const Foo> objects were originated from Manager's shared_ptr<Foo> collection, then Manager should be able to safely const_pointer_cast shared_ptr<const Foo*> back into shared_ptr<Foo*>. If Manager attempts to const cast a shared_ptr<const Foo*>, where the pointee is an actual constant object, you'll get undefined behavior.


I've thought of another solution for #5:

Define an ExecutableCommand class, derived from Command. ExecutableCommand has an added method to invoke the command, to be used only by Manager. Clients can only access ExecutableCommand objects via pointers/references to Command. When a Manager wants to invoke a Command, it downcasts it to a ExecutableCommand to gain access to the invocation interface.

Working example (including const_pointer_cast for #6):

#include <iostream>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>

using namespace std;
using namespace boost;

//------------------------------------------------------------------------------
struct Foo
{
    Foo(int x) : x(x) {}
    void print() {++x; cout << "x = " << x << "\n";} // non-const
    int x;
};

//------------------------------------------------------------------------------
struct Command
{
    // Interface accessible to users
    std::string name;

private:
    virtual void execute() = 0;
};

//------------------------------------------------------------------------------
struct ExecutableCommand : public Command
{
    // Only accessible to Manager
    virtual void execute() {} // You may want to make this pure virtual
};

//------------------------------------------------------------------------------
struct PrintCommand : public ExecutableCommand
{
    PrintCommand(shared_ptr<const Foo> foo)
        : foo_( const_pointer_cast<Foo>(foo) ) {}

    void execute() {foo_->print();}

private:
    shared_ptr<Foo> foo_;
};

//------------------------------------------------------------------------------
struct Manager
{
    void execute(Command& command)
    {
        ExecutableCommand& ecmd = dynamic_cast<ExecutableCommand&>(command);
        ecmd.execute();
    }

    void addFoo(shared_ptr<Foo> foo) {fooVec.push_back(foo);}

    shared_ptr<const Foo> getFoo(size_t index) {return fooVec.at(index);}

private:
    std::vector< shared_ptr<Foo> > fooVec;
};

//------------------------------------------------------------------------------
int main()
{
    Manager mgr;
    mgr.addFoo( shared_ptr<Foo>(new Foo(41)) );

    Command* print = new PrintCommand(mgr.getFoo(0));
    // print.execute() // Not allowed
    mgr.execute(*print);

    delete print;
}
Emile Cormier
  • 28,391
  • 15
  • 94
  • 122
  • Wouldn't it be a problem that `Command` is a base class, so making `Manager` a `friend` wouldn't inherit (friendship doesn't inherit)? – Matt Apr 22 '11 at 18:58
  • If Manager only invokes commands via the Command base class, then this is not a problem. – Emile Cormier Apr 22 '11 at 19:23
1

Since it wouldn't make any sense to me otherwise, I'm going to assume that

  • your library provides the Manager class (or at least a base class), and
  • clients have to use that class to invoke a Command.

In that case, maybe something like this could work:

void Manager::execute(Command& cmd, shared_ptr<Foo const> const& target)
{
    shared_ptr<Foo> mutableTarget = this->LookupMutableFoo(mutableTarget); // throws if not found
    cmd.execute(mutableTarget); // client cannot invoke without mutable pointer
}

// or, if the "target" needs to be stored in the Command you could use something like this:
void Manager::execute(Command& cmd)
{
    shared_ptr<Foo> mutableTarget = this->LookupMutableFoo(cmd.GetTarget()); // throws if not found
    cmd.execute(mutableTarget); // client cannot invoke without mutable pointer
}

I'm not sure though if using const is the best solution here. Maybe you should wrap your Foo objects in e.g. ClientFoo objects. The manager only hands out pointers to ClientFoo. The manager can then (e.g. via friend) get the Foo from the ClientFoo, and use it to invoke the Command.

Paul Groke
  • 6,259
  • 2
  • 31
  • 32
  • That's almost how I'd like it. I'm trying to decide though as to whether all Commands will have a single `shared_ptr` target so I can pass it in to execute or not. It is nice that essentially no client can invoke `command.execute(...)` because they cannot get the non-const pointer. – Matt Apr 22 '11 at 19:03
  • Also, why do you recommend a wrapper class over `const`? I can see how I can get the same end result either way (I only expose methods that are const in `Foo`). – Matt Apr 22 '11 at 19:04
  • Mainly because the `LookupMutableFoo` method feels kind of "dirty" (or at least strange) :) In addition, using a distinct class is much more flexible then using `const`. But as I wrote, I'm not sure - I'd have to know much more about what you're trying to do to be sure. – Paul Groke Apr 22 '11 at 19:52
  • If I have a `std::set >` and I want to find a `std::shared_ptr`, will that work "out of the box? I know that Set is efficient at finding items (it's stored as a red-black tree or something?), but also that those types may be "incompatible". I was just wondering if I could use `set::find`. – Matt Apr 23 '11 at 05:03
  • No, won't work. You can `const_pointer_cast` the `shared_ptr`, and then just make sure that it's contained in the `set`. Or you can use a boost intrusive set: http://www.boost.org/doc/libs/1_46_1/doc/html/intrusive/advanced_lookups_insertions.html#intrusive.advanced_lookups_insertions.advanced_lookups – Paul Groke Apr 23 '11 at 12:30
  • @pgroke I see. Luckily I realized it made sense to store a `std::map >` where I can get the `Key` from a `const Foo`. Now the table lookup makes sense, and client code can access the members of `const Foo` (rather than just holding some key) – Matt Apr 24 '11 at 04:53