C++: Difference between NVI and Template Method Patterns?
Asked Answered
Z

2

10

What is the difference between NVI ( Non-Virtual Interface ) and the Template Method patterns?

They seem very similar and I've read both that they're basically the same and that they're subtly different with Template being somehow more general.

Zephyrus answered 20/6, 2010 at 9:20 Comment(1)
I would not use the wikipedia link as a reference, their use of locks is brittle in the face of exceptions...Barney
P
16

NVI is an idiom, Template Method is a pattern. NVI is an implementation of the Template Method Pattern using dynamic dispatch in C++; it is also possible to create template methods in C++ using template metaprogramming to eliminate the dynamic dispatch.

A pattern is more general than an idiom, and languages may use different idioms to implement the pattern.

Permanency answered 20/6, 2010 at 10:11 Comment(5)
So you're saying that NVI is basically a language specific implementation of the Template Method pattern and beyond that there's no real difference? How would you use C++ templates to achieve the same result?Zephyrus
@Robert S. Barnes As far as I can see, there isn't an obvious way of using C++ templates as template method. Template method says 'do this, then do this other thing', and whilst you can create a functor for one or the other of the things to do, there's no real relationship with the type parameters that C++ templates give you.Permanency
I guess I'm still not understanding what you meant here: "it is also possible to create template methods in C++ using template metaprogramming to eliminate the dynamic dispatch."Zephyrus
Normally in a template method, there's some non-virtual calls and some virtual calls. You can put the template method inside a template, or call against template specialisations, so that the calls are resolved at compile time, but you can't directly create a template specialisation which is a template method - you can call template specialisations from the template method but the template specialisation is not the template method itself. e.g. template <typename T> T fma ( T a, T b, T c ) { return a + b * c; } will call the correct specialisations or overloads of T::operator+.Permanency
Worth to mention that a pattern is not always more general than an idiom. It might be the other way around. CRTP and Mixin are idioms (not specific patterns) but a Composite pattern utilizes CRTP and a Static Decorator pattern utilizes Mixin inheritance. For some code constructs like RAII I've seen both concepts ascribed interchangeably. The bottom line is the community / the school isn't consistent on which one should be "higher" level, more general, unless they are just names we should not pay so strict attention to what level of abstraction they represent.Recompense
B
12

As has been said, NVI is a progamming idiom, related to a category of languages. It's been promoted by Herb Sutter among others, because it helps enforcing contracts:

  • class invariants
  • function contracts (assertions over the parameters passed and the return value generated)
  • repetitive operations (like logging)
  • control over the exceptions generated ( bad idea though ;) )

However, the implementation may actually differ considerably, for example another example of NVI implementation is to combine it with Pimpl:

class FooImpl;

class Foo
{
public:
  enum type { Type1, Type2 };

  Foo(type t, int i, int j);

  int GetResult() const;

private:
  FooImpl* mImpl;
};

And for the implementation:

struct FooImpl
{
  virtual ~FooImpl();
  virtual int GetResult() const;
};

class FooType1: public FooImpl
{
public:
  FooType1(int i, int j);
  virtual int GetResult() const;
private:
  /// ...
};

I've always found that it conveyed the point better. Have you figured it out ?

The main point is that virtual is an implementation detail. And exposing implementation details in the interface is a bad idea, because you may wish to change them.

Furthermore implementation details tend to mess with binary compatibility. For example adding a new virtual method in a class may change the layout of the virtual table (common implementation technic) and thus botch the binary compatibility. On gcc you need to make sure that you add it last (among the virtual) if you wish to preserve the compatibility.

By using the NVI + Pimpl combination above, there is no virtual at all (not even private) in the class exposed. The memory layout is backward and forward compatible. We have achieve binary compatibility.

Here, we use several patterns at once:

  • Template Method
  • Strategy (since we can swap the pointer at will)
  • Factory (to decide which implementation we get)
Barney answered 20/6, 2010 at 11:11 Comment(7)
+1 for the "virtual is implementation detail" and the ABI consideration.Archiepiscopal
+1. I have to disagree with you about making separate pimpl-like classes for the virtual interface, however. Most obvious downside is that it doubles the number of classes required. Also if FooImpl wasn't opaque, it might tempt people to use it directly and skip Foo. However, it's unlikely that it would be opaque since the point of NVIs are to allow people to override the virtual implementations, so FooImpl would have to be publicly accessible. Even though it has its own downsides, I think implementing NVI in one class and avoiding public virtual functions all together is a better approach.Donatus
It's also an easier policy to enforce: no public virtual functions as opposed to one that makes special cases for pimpl-like classes which provide the public virtual interface.Donatus
The problem of having virtual function is not with ease of use, it's with ABI. The advantage of FooImpl being opaque is that you confine it to your binary and thus have no issue of ABI when you deliver new version of the binary to your clients.Barney
Great, but how does NVI differ from Template Method? It seems like you're saying that NVI is a specific way of implementing the Template Method in a certain class of languages?Zephyrus
Template Method is a fancy word. In C++ every time you use the virtual keyword you provide a hook of customization, which is essentially what the Template Method boils down. So the Template Method is a pattern while NVI is a guideline about the use of the virtual keyword in C++.Barney
I know this is old, but why exactly is controlling what exceptions get generated a bad idea?Macfadyn

© 2022 - 2024 — McMap. All rights reserved.