views:

114

answers:

4

Please consider this -probably poorly written- example :

class Command;

 class Command : public  boost::enable_shared_from_this<Command>
 { 
  public :
   void execute()
   { 
    executeImpl();
                // then do some stuff which is common to all commands ... 
   }

   // Much more stuff ...
     private:
      virtual void executeImpl()=0;
         // Much more stuff too ...
 };

and :

class CmdAdd : public Command
 {
 public:
  CmdAdd(int howMuchToAdd);
  void executeImpl();


  int _amountToAdd;
 };

// implementation isn't really important here .... 

With this, I can simply add a callback using this syntax :

        boost::shared_ptr<Command> cmdAdd(CmdAdd(someValue));
     cmdAdd->execute();

It works flawlessly. My "Command" class does much more things which are common to all commands, such as implementing undo, redo, progress report and so on, but I removed it from the code for the sake of readability.

Now my question is simple : is there a way to rewrite the command class, so that I can replace this call :

boost::shared_ptr<Command> cmdAdd(CmdAdd(someValue));
cmdAdd->execute();

by something like :

CmdAdd(someValue); // preferably
or CmdAdd->execute(someValue)

I've been thinking about that a lot but I have a conceptual problem : I wanted to template my Command class like

template <typename R,typename T1, typename T2, ..., typename Tn> class Command
{
    R1 execute(T1 p1, ...,Tn pn)
    { 
        return executeImpl(T1 p1, ...,Tn pn);
        // then do some stuff which is common to all commands ... 
    }
}

but obviously, there's a problem here : the syntax template <typename R,typename T1, typename T2, ..., typename Tn> isn't legal C++ , AFAIK.

Do I have to write n versions of Command, like :

template <typename R> class Command
template <typename R,typename T1> class Command
template <typename R,typename T1, typename T2> class Command
...

and so on ? (not even sure this is gonna work indeed)

Or is there another, more elegant way to do this ? Is the syntax, mentioned here of any use there ? (function f;)

I've been looking at Loki's type lists and they seem to do the job. But I can't find anything that in Boost. I read on the web that boost::mpl is what one wants to use to implement typelists, but I'm a bit confused by MPL docs ?

Any insights on this ? Regads, D.

+2  A: 

AFAIK You can't really do this with the current standard of C++. Some boost code uses macros and other preprocessing to simulate variadic templates (I think boost::pool or boost::object_pool use something like that).

However, variadic templates are coming in the next standard C++0x and according to this page GCC already provide an implementation starting with v4.3 : http://gcc.gnu.org/projects/cxx0x.html

If you're using it, you can enable it by activating C++0x.

Klaim
+1 This is the true answer. However given the fact that this new C++ is not yet supported by all compilers: How portable do you want your code to be? Does it need to be compilable under most compilers or is a single specific compiler enough for you?
Niels Basjes
Variadic templates do not solve the problem, see my answer.
FredOverflow
A: 

Variadic templates, as Klaim pointed out, are the ultimate solution to this problem. However, there is a way to allow a variable number of template arguments using type lists:

template <class H, class T>
struct typelist
{
    typedef H head;
    typedef T tail;
};

This allows you to write typelist<typelist<int, float>, double>, for instance. It is a real pain in the neck to read and write, however, and is the main reason why boost::function uses the brute force approach (separate class for each number of template arguments): boost::function0, boost::function1, boost::function2, etc. for its backend implementation. It's just so much easier than traversing typelists recursively through template metaprogramming.

As for a general answer, I posted it in the other thread where you originally had this question along with another one.

I was wondering if by using partial template specialization, it's possible to use boost system, but with only one function name, i.e, instead of :function0<>function1<typename T1> have function<>function<typename T1> and the compiler chooses the right version depending on the number of template arguments you provide :MyFunction : public Function<int> -> would "choose" function1<typename T1> If I understand what you say, one should write MyFunction : public Function1<int> for this to work, right ?
Dinaiz
+2  A: 

At first glance, variadic templates seem like the perfect solution. Unfortunately, they don't play well with virtual functions:

template <typename... Args>
void execute(Args&&... args)
{
    executeImpl(std::forward<Args>(args)...);
}

This requires executeImpl to be a virtual member function template, but there is no such thing in C++!

FredOverflow
+1 for the good catch. But the way he shows will still work. Like `template<typename FType> class Command; template<typename R, typename ...P> struct Command<R(P...)> { /* ... */ virtual R executeImpl(P...) = 0; };` Then he can implement it by `class CmdAdd : public Command<void(int)> { void executeImpl(int howMuch) { /* ... */ } };`
Johannes Schaub - litb
@Johannes: Your solution implies that all subclasses use the same function signature, right? Is that what Dinaiz wants? I'm not sure.
FredOverflow
I agree, it's just that there is no base class common to all commands :)
Matthieu M.
@Matthieu that's a good point. @Fred they pass their function type to the `Command` base-class, so each of them can have a different function-type. Signature (regarding its function type) isn't necessarily the same for each subclass. It's a direct translation of the questioner's template code - but i didn't think about @Matthieu's point about a common non-template base class.
Johannes Schaub - litb
+2  A: 

Interesting question :)

First of all, there is an issue you overlooked: you need a common base class for all Command and this class cannot be templated if you are going to use a stack of them (for undo/redo).

Therefore you are stuck with:

class Command
{
public:
  void execute(); 
private:
  virtual void executeImpl() = 0;
};

I can understand your desire an execute function with parameters, but don't forget that anyway you would need to save those parameters for the undo/redo operation. It's just simpler to get them through the constructor.

However, you could still use a templated method to actually invoke a command:

template <class Command>
void execute() { Command cmd; cmd.execute(); }

template <class Command, class T0>
void execute(T0& arg0) { Command cmd(arg0); cmd.execute(); }

/// ...

int main(int argc, char* argv[])
{
  execute<MyLittleCommand>("path", 3);
}

Which is close to the syntax you desired. Note that I have purposely forgotten about the stack here, in my mind you need to pass it to the execute method for registration (once completed).

Not that I would also probably change the Command design to get closer to a Strategy pattern:

struct CommandImpl
{
  virtual ~CommandImpl();
  virtual void executeImpl() = 0;
};

class Command
{
public:
  template <class C>
  static Command Make() { return Command(new C()); }

  template <class C, class T0>
  static Command Make(T0& arg0) { return Command(new C(arg0)); }

  /// ....

  void execute(CommandStack& stack)
  {
    mImpl->executeImpl();
    stack.Push(*this);
  }

private:
  Command(CommandImpl* c): mImpl(c) {}
  boost::shared_ptr<CommandImpl> mImpl;
};

It's the typical combination of Non Virtual Interface and Pointer to Implementation idioms.

Matthieu M.
Very interestind ! Indeed I added a non-template CommandBase class, precisely for containers purposes. Saving/restoring parameters also works flawlessly (unsing a few tricks).I finally choosed to use this approach "template <typename R> class Commandtemplate <typename R,typename T1> class Commandtemplate <typename R,typename T1, typename T2> class Command", along with boost preprocessor. Seems to be the asiest way, but I still got some syntaxic problems.I find your approach much more elegant than mine though, so thanks a lot and I'm gonna have a look at this strtegy pattern :-)
Dinaiz