7

I would like to have the mutators (setters) in my class to return this to allow for jQuery-like a.name("something").address("somethingelse"); I have a parent class (Entity) and several childclasses (Client, Agent etc.). The mutators for most things are inherited from the Entity class (like name or address), but they return an Entity object, so I can't call Client mutators on them.

In other words:

// name mutator
Entity& Entity::name( const string& name ) {
    // [...] checks
    _name = name;
    return *this;
}

// budgetRange mutator
Client& Client::budgetRange( const long int& range ) {
    // [...] checks
    _budgetRange = range;
    return *this;   
}

then when I call it:

Client a; a.name("UserName123").budgetRange(50);

The compiler (logically) says, that the Entity object has no budgetRange member (because name returns an Entity, not a Client).

My question now is: how could I implement something like this? I thought about overloading all the Entity functions in the childclasses but that wouldn't be nice and would be against the idea of inheritance :)

Thank you in advance for your ideas :D

Asmodiel
  • 836
  • 1
  • 10
  • 21
  • google also for `method chaining` and `named parameter idiom`, which is what you are doing. Note that, when inheriting from `Client` your code will break again using CRTP solution below. – cheind Dec 20 '11 at 13:40

3 Answers3

8

You should use the CRTP.

template<class Derived>
class Entity
{
    Derived* This() { return static_cast<Derived*>(this); }

public:
    Derived& name(const string& name)
    {
        ...
        return *This();
    }
};

class Client : public Entity<Client>
{
public:
    Client& budgetRange(const long& range)
    {
        ...    
        return *this;   
    }
};

If you want to use virtual functions, you can also add abstract base class, like this:

class AbstractEntity
{
public:
     virtual void foo() = 0;

     virtual ~AbstractEntity();
};

template<class Derived>
class Entity : AbstractEntity
{...};
Abyx
  • 10,859
  • 4
  • 36
  • 74
  • I really like this idea (especially the This() function ^^). Thanks a lot, that's what I was looking for :) – Asmodiel Dec 20 '11 at 13:22
  • Excellent stuff. I've been searching for a good example of CRTP. Thanks. – sje397 Dec 20 '11 at 13:26
  • 1
    @sje397 another good example of CRTP is a generic tree class, that provides concrete node implementations via `template class node`. For example: `D &node::left()` can provide the concrete node impl. That way you can stuff all the tree logic into the generic node class. – cheind Dec 20 '11 at 13:46
  • @Christoph Heindl: Another great example. Thanks. – sje397 Dec 20 '11 at 13:47
  • Note though that your classes no longer have a polymorphic base class now. They're entirely independent of one another. You've got one distinct base class per derived class now. – Kerrek SB Dec 20 '11 at 13:56
  • Should you not `class Client : _public_ Entity`? – Sebastian Mach Dec 20 '11 at 15:02
  • @phresnel, I'd like all of them to be `struct`s, but a lot of people like to write `class` and `public` keywords – Abyx Dec 20 '11 at 18:02
3

The "curiously recursive template" pattern could help here; make the base class a template, parametrised by the derived class, along the lines of:

template <typename Derived>
struct Entity {
    Derived & name(std::string const & name) {
        // stuff
        return static_cast<Derived&>(*this);
    }
};

struct Client : Entity<Client> {
    Client & budget(long range) {
        // stuff
        return *this;
    }
};

Client().name("Mike").budget(50); // should compile

This will only work if all your types inherit directly from Entity. If you need the types to be polymorphic (i.e. all share a common base class), then you'll need to add another non-template base class, and have Entity inherit from that.

Mike Seymour
  • 235,407
  • 25
  • 414
  • 617
  • Thank you very much for you answer, but I'll choose Abyx' one as he included the this() idea :) Upvoted though. – Asmodiel Dec 20 '11 at 13:25
3

Now that nearly everything has already been said, I want to add a piece of answer that allows one to use the CRTP over multiple levels of inheritance:

The above CRTP implementations break when one wants to inherit from Client, since Derived will refer to Client. In case you want to be able to carry the named parameter idiom over multiple levels of inheritance using CRTP pattern, you need to code your classes like so

template<class Derived>
class Entity_T
{
protected:
    Derived* This() { return static_cast<Derived*>(this); }
public:
    Derived& name(const string& name)
    {
        ...
        return *This();
    }
};

template<class Derived>
class Client_T : public Entity_T<Derived>
{
    Derived& budgetRange(const long& range)
    {
        ...    
        return *This();   
    }
};

To provide the user a template-free version of Client_T add

class Client : public Client_T<Client> {};

Whether or not this is worth the enlarged code base is entirely up to you. Note, that I haven't compiled the code above.

cheind
  • 695
  • 5
  • 11