Policy based design and best practices - C++
Asked Answered
N

4

37
struct InkPen
{
  void Write()
  {
    this->WriteImplementation();
  }

  void WriteImplementation()
  {
    std::cout << "Writing using a inkpen" << std::endl;
  }

};

struct BoldPen
{
  void Write()
  {
    std::cout << "Writing using a boldpen" << std::endl;
  }
};

template<class PenType>
class Writer : public PenType
{
public:
  void StartWriting()
  {
    PenType::Write();
  }
};

int main()
{
  Writer<InkPen> writer;
  writer.StartWriting();
  Writer<BoldPen> writer1;
  writer1.StartWriting();
  return 0;
}

I wrote the above code as part of learning policy based designs. I have few questions on the above code

1 - Does this implementation look correct? I mean: does it really look like a policy based design?

2 - I can now hook any kind of pens to writer. But what will I do when I got a pen with no default constructor (only parameterized constructors)? How will I handle this situation?

template<class PenType>
class Writer : public PenType
{
public:
  void StartWriting()
  {
    PenType::Write();
  }
};

3 - When the above code is used like

Writer<InkPen> writer;

I guess compiler will replace PenType with InkPen. If yes, why I am not able to call just Write() from StartWriting() instead of prefixing base class name (PenType::Write())?

4 - I think policy based design forces you to derive from classes which is semantically invalid. In the above code, a writer is derived from a pen only because writer uses a pen. But saying writer is a pen is semantically invalid. Is there any other better way to address this or I am missing something here?

Any thoughts?

Nagpur answered 16/5, 2009 at 15:52 Comment(1)
Instead of thinking "writer is a pen" think "writer is a pen user". Does that help? Also, in BoldPen the Write function could be static, saving the overhead of the unused this pointer.Bulbiferous
P
30

Here's how I would implement the class:

template<class PenType>
class Writer
{
public:
  Writer(const PenType& pen = PenType()) : pen(pen) {}

  void StartWriting()
  {
    pen.Write();
  }

private:
  PenType pen;
};

This allows the user to pass a specific Pen object to the constructor, if it either doesn't have a default constructor, or you don't want it to be used, and second, it still allows you to omit the PenType object if you're happy to let it create one with the default constructor. The C++ standard library does the same in many classes (think of the allocators for container classes for example).

I removed the inheritance. It didn't really seem to add anything (and might cause problems. You probably don't want the user of the Writer class to call the PenType::Write function directly. You could use private inheritance instead, but often, composition is a simpler and more conventional design.

In general, policy-based design does not require inheritance. Adding it as a member works just as well. If you do go for inheritance, make it private so you don't get the problem you mentioned as #4.

Promising answered 16/5, 2009 at 16:41 Comment(9)
I am reading Andrei Alexandrescu's "Modern C++ Design: Generic Programming and Design Patterns Applied". In this book, he has used public inheritance for policy designs rather than adding as member. This is the reason why I followed the same practice.Nagpur
Hm, curious. Usually, most people would say the policy object is only relevant internally in the Writer class. It shouldn't be visible to the outside world. He probably had a reason for that implementation, I just can't see it in the general case. ;)Promising
The policy is visible to the client because it is the client that decides what policy is used, therefore the client knows about any specific behaviour of the policy that has been chosen. e.g. a fountain pen that needs refilling would be refilled by the client, and the writer would not have to consider refilling.Cabe
@diano: If the client passes in a fountain pen, he also knows how to refill the pen. Refilling the pen should be the responsibility of the caller, since a generic Writer shouldn't need to know how to refill a fountain pen, replace battery of an electronic pen, etc. Even if the Writer exposes Pen's interface like Refill(), the Writer itself can't use it anyway (the Writer won't know when to refill) so its use is limited. On the other hand, inheriting the Writer from a Pen allows WriterA to be used as a Pen for another Writer. Whether or not it is useful depends on your situation.Resuscitate
The reason to inherit policy classes is outlined in section 1.6 (Enriched Policies) of Modern C++ Programming: 1) It allows a policy to add per-policy APIs to the policy-using class, and 2) if the user switches from an enriched policy to one that is not, and still has code that uses the per-policy APIs, the compiler clearly flags them as errors so they get fixed. While one could implement these per-policy APIs as entirely external functions, it is much simpler and clearer to make them part of the policy.Bulbiferous
One gotcha with enriched policies is the destructor: since there's a subclass relationship, one could typecast the policy user to the policy, then delete it, causing an improper deletion. The usual solution, declaring a virtual destructor for the policy, forces a virtual table overhead that is usually not desired. The solution is to declare a protected, non-virtual (and usually empty) destructor for the policy, preventing policy objects from being deleted (or instantiated, for that matter). This is covered in section 1.7 of Modern C++ Design.Bulbiferous
As for jalf's solution, one downside is that it wastes storage (one byte minimum, IIRC) on a PenType object that doesn't actually store anything. I don't think inheritance has the same problem.Bulbiferous
@MikeDeSimone: That would be the reason to use private inheritance instead of composition for such a situation. If you expect the policy class to be empty in at least some situations and you care about object size, you are forced to derive.Antiperistalsis
@DavidStone This came to my mind as well. Empty policy class (with no non-static member variable) would increase the size of main class when using composition, which is IMO quite significant drawback.Otherwhere
A
19

This looks like a nice example of policy-based smart pointer implementation: link. Andrei Alexandrescu describes policy-based smart pointer implementation in one of his books. As to your questions now. I have some experience in this stuff but not enough to take my words for granted:

Ad 1 & 4. I guess policy-based design is more about templates than inheritance. You write a template class and template arguments are policy classes, like that:

template<class FooPolicy, class BarPolicy>
class Baz {
    // implementation goes here
};

Then you use methods from policy classes in your class:

void Baz::someMethod(int someArg) {
    FooPolicy::methodInit();
    // some stuff
    BarPolicy::methodDone();
}

I use static methods in this example because often policy doesn't require any state. If it does, you incorporate policy's state by composition, not by inheritance:

template<class FooPolicy, class BarPolicy>
class Baz {
  private:
    FooPolicy::State fooState; // might require 'typename' keyword, I didn't
                               // actually tried this in any compiler
    // rest of the Baz class
};

Ad 2. You can write a template specialization - for a particular combination of main class and it's policies you can write a special version of any method or constructor, AFAIK:

template <>
Baz<SomeConcreteFooPolicy, SomeConcreteBazPolicy>::Baz(someArgument)
   : fooState(someArgument)
{
    // stuff here
}

Hope it helps you a bit,

Mike

Activate answered 16/5, 2009 at 16:22 Comment(1)
Is policy-based design different from STL's use of function objects? I feel like it is the same pattern to inject behaviour into a type through composition.Whitsuntide
E
5

1 - Is this implementation looks correct? I mean is it really looks like a policy based design?

Policy classes derive their usefulness from combining behaviors to produce a rich variety of combinations. When you have a single template parameter like this, it's not much of a policy class.

2 - I can now hook any kind of pens to writer. But what will I do when I got a pen with no default constructor (only parameterized constructors)? How will I handle this situation?

Again, this is an odd example of a policy class. However, to directly answer your question, you can provide a constructor which accepts PenType. You should probably also avoid inheriting from PenType and store it as a member instead (no need to tightly couple your policy class with its policies).

I guess compiler will replace PenType with InkPen. If yes, why I am not able to call just Write() from StartWriting() instead of prefixing base class name (PenType::Write())?

When you inherit from a class template, you have to specify this->member or BaseClass::member.

4 - I think policy based design forces you to derive from classes which is semantically invalid. In the above code, a writer is derived from a pen only because writer uses a pen. But saying writer is a pen is semantically invalid. Is there any other better way to address this or I am missing something here?

Store PenType as a member as suggested above. Always prefer composition to inheritance as it avoids the tight coupling relationship of inheritance.

Exhalant answered 29/6, 2010 at 2:32 Comment(0)
A
5

I know this thread is old, but there is a major flaw in the initial post and this thread is one of the top results of Google...so:

Do not use public inheritance for policy-based design! This would say "is-a" instead of "has-a" / "uses-a". You should therefore use private inheritance!

Anguished answered 17/6, 2013 at 19:13 Comment(3)
It's actually pretty common to have public inheritance for policy-based design. This is so that the individual policy classes can add new methods that the host class will inherit and that the caller(s) can invoke, all without the host class having to be aware of these new methods. Although this breaks the is-a relationship, it does make everything much more extensibleSolis
Policy-based classes are not objects like they are in strict OOP. Policy classes will frequently represent a verb instead of a noun.Full
This is not a flaw. What reasoning do you have there is flaw? There is nothing wrong with using public inheritance for policies.Bibcock

© 2022 - 2024 — McMap. All rights reserved.