>> Our speaker today is Kevlin Henney.
Kevlin is a consultant and an author.
If--I won't even try to list all of the different
places that he's been published.
If you've been following C++, then you're--you've
undoubtedly read the several of his articles
already.
He always has interesting things to say.
And today, he's going to be talking about
different styles for using C++.
>> HENNEY: Okay.
Thank you, Matt.
Okay.
It's quite a lot to get through so I may well
skip some slides, and we'll see how we go.
I'll play this one by here.
I want to explore some of these questions
of programming styles to C++ that tend to
pop up.
There's some--sort of a growing interest in
programming styles and the multiplicities
of styles available to developers, and this
seems to be reaching out across languages.
There's also a strong recognition that many
languages have tried because they have pushed
themselves to supporting many styles in the
past.
It turns out that some of this interest is
not new and there--but there's also a certain
amount of confusion in this space.
Not least of which--thanks to our friend,
the word paradigm which has really messed
things up properly.
I'll have a quick look at that and describe
why I favor the word style.
But also describe the notion why I--why most
languages can claim to support multiple styles.
But the focus here is on C++.
In the absence of a perfect language, we have
a language that is used in production in a
number of places and has a very strong track
record.
There are many, many styles that we could
be looking at.
I have constrained this to just three broad
groups; two of which are properly styles in
terms of a way of thinking.
The third, which was just a bucket placeholder
for two quite different styles, which are
put under the language mechanism.
That's the only one defined by language mechanism,
and so were template-based styles.
There are many more styles we could be approaching.
There are things that I'm not going to cover,
and particularly areas where the support is
not core in the language or the support is
not there in the language at all.
You can sort of fake up a functional programming
style; sort of, kind of, but it's never going
to be the same as using Scheme or Haskell.
That's not a style that is supported.
If we talk about concurrency, there are so
many overwhelmingly different styles of concurrent
programming, and strictly speaking, none of
them is supported in standard C++.
Although we have ways of wrapping things and
doing things, the most pervasive style here
is a substandard, free threading style, which
kind of fits out as an offshoot of procedural.
Again, and I'm not going to talk about that
one because certainly it itself is a talk.
And I presume that you've got some kind of
idea of programming for style.
I want to get a sense of how many of you use
C++ either sort of--yes, 20% or more of your
time is taken up.
Okay.
So, there's--it's something--okay.
That's reasonable.
Okay.
That's good.
So, that will allow me to shoot pass a lot
of slides and just go, "Yes, you know this."
I want to, first of all, look at this question
of styles.
Convenient enough, there's a word that covers
what I'm interested in.
Stylistics, the study of the literary styles
of particular genres or writers.
This captures probably a little more closely
the way that we look at work with different
approaches to programming.
Then, the common use of the word paradigm.
Trouble with Paradigm.
Paradigm is a term from linguistics.
So surely, it should apply to programming.
Well, not really.
That's not how it's used.
The sense in which people have kind of tried
to use it.
It was popularized by Thomas Kuhn in his 1962
book, I think, [INDISTINCT] "The Structure
of Scientific Revolutions," a book that--I
wouldn't say it's [INDISTINCT] reading but
I will actually claim to have read it.
It's--and yes, I got something from that many
years ago.
The--and that was what it defines the modern
use of the term.
Now, what is interesting is that even there,
you think, we'll that--that's it.
There's a concrete solid definition.
But even then, that doesn't work.
Many people, when they look at it from the
kind of Kuhnian sense, are trying to say that
there's one way of doing things, that there's
one kind of dominant and overarching way of
doing things.
Now, in programming, this has never really
been the case.
The world of programming is diverse.
And one area where people are programming
may--can be completely disjoint to another
area.
You got a COBOL programmer talking to an APL
programmer, going back a few decades?
No, unlikely.
They do not have a common language or even
a common way of speaking.
That has not changed.
There has never been a single overarching
model.
That may be something that we teach a lot,
that may be something that is in the press
and is in vogue, and is sexy; but there's
never been one single way of doing things.
The other way in which people use it, is they
try and use it based on the first point.
They try and use it to make a marketing point.
And it's related to the term paradigm shift.
And that notion of shift happens.
The idea is, well, we're all doing this; but
now, it's all going to be different.
We're all going to be doing that.
That's mostly for dramatic effect or to make
yourself just sound a little bit clever.
You know, there's another way of doing something.
Probably more usefully, the--sometimes, people
try and relate it to language features.
And again, that's a very mechanistic way of
looking and I don't enjoy looking at languages
just purely from the point of view of mechanism.
It's how the mechanisms are into play.
So for example, overload--being able to overload
functions is not a paradigm.
Although I know that I've seen that played
in the book.
Perhaps, the most useful one and the one where
it was originally used with great effects,
is a 1978 Turing Award Winner--his name just
escaped my mind just at the moment.
And he wrote the paper--Robert Floyd, here
it is.
He wrote a paper to--where he effectively
used paradigm to mean pattern or architecture
style if we use a more modern vocabulary.
And what is interesting is that, he related
this to what many people try and use the branding
multi-paradigm form.
Now, a paradigm makes you try and sound smart.
But multi-paradigm is designed to make you
sound really smart.
Okay.
So, we already have confusion over the term
paradigm.
Multi-paradigm just multiplies the confusion.
Sometimes, it's a set of language features
there.
The challenge here is that, if you're saying
that a paradigm is related to a style or a
pattern, then clearly, a single paradigm approach
is a failure mode.
If you only have one way of doing something
then that is a problem.
So by definition, anything that is multi-paradigm
is normal.
Single paradigm is a lack of imagination and
ability.
A programming language, that its general purpose
is by definition multi-paradigm.
I know a very few general purpose program--well,
I don't know general purpose program in languages
that could ever able be said to fulfill only
one paradigm.
A part of the problem with this is, the term
paradigm is not a normalized term.
It's not disjoint.
You don't have this mutually exclusive with
that.
You look at things, you see them at different
levels overlapping with the rest of it.
Not useful term, too much confusion.
Let's use something else.
Focusing on Style.
From the dictionary, we can see that it talks
about the notion of style, a manner in which
something is done, distinctive appearance
and design.
If we start looking at building architecture
and talk about building styles or the sort
of the schools of design that exist, that
gets us a little bit closer to what we're
talking about.
And there is no notion, we only have one way
of doing something.
These are all complimentary.
They can co-exist, and indeed, that's the
normal state of being.
There is nothing mortem about it.
There is certainly nothing post-mortem about
it, although that is a term that seems to
be gaining some popularity in certain courteous.
So, what we can say is that individuals have
styles.
Well, lovely little word that's not commonly
used, idiolect.
The problem is that idiolectic style is problematic.
I'm sure you've already encountered code that
falls into this category.
Oh, yeah, yeah, that's the way he codes for
a particular way of doing it.
The school of thought establishes an idiom,
so a group kind of approach.
But even when we say procedural style, that
doesn't tell you that there is one way to
do something.
You get a C++ program using a procedural style.
I ask you, what that has in common with the
Fortran programmer using a procedural style?
Having been a Fortran programmer, I can--I
can tell you, these are worlds apart.
Having gone from Fortran to C, I recognized
that all of my C code initially looks like
C-tran.
C in respect with a very heavy Fortran accent.
It was unrecognizable as proper C, although
syntactically correct.
When I did Fortran again, a year later, my
Fortran was unrecognizable to a Fortran programmer,
and that was one of the first things my colleague
changed.
So, to even say procedural style doesn't nail
it down.
Each language pretty much comes with its own--its
own notion.
Now, of course, any real problem is going
to demand a little bit more imagination.
The idea that if you only--there's nothing
more dangerous than an idea if you have only
one idea.
So, you are expecting, in a series pieces
of software, to mix styles at different levels
in some way.
Some of it may be individual preference; but
in other cases, we'd like to think that it's
anchored in the problem.
The challenge here is that, when people mix
styles, it does take skill and taste.
These are two things that are not necessarily
easy to teach but can be acquired over time.
And obviously, there's a certain sense of
subjectivity to taste, but there is some into
subjective agreement.
And when people often use this notion of mixing
styles, and sometimes I--I've heard people
say, "Yeah, we're doing multi-paradigm development."
What that actually means is we haven't got
a clue what we're doing.
We're doing all kinds of different things
and it'll kind of fits together.
The other branding for this is post-mortem.
Again, I'm not interested in that.
What I think a lot of these approaches are
missing is the focus on the problem domain.
What crazy idea, that--your solution styles.
There's somehow hopefully a relationship between
the style that you choose for your solution
and the nature of the problem that you are
solving.
If you are solving a process control problem
and you are bickering over whether procedural
versus object-oriented is the right way to
look at it, you're looking in the wrong place.
Neither of these styles is the dominant architecture
of a process control system, okay?
And there are--that it has aspects of both.
Both of these styles can be incorporated in
the more tactical level.
In other words, it's the wrong discussion.
In other words, what is the problem?
And get a crazy idea.
The notion that we would like to relate the
problem to the solution is something that
has a very strong resonance with patterns
which is something that I'm very interested
and have a--recently long track record with.
But also, other idea is problem frames that
are--relate to the work of Michael Jackson.
And you are invited to guess which Michael
Jackson it is.
You have a number of choices, you can pick
from a menu of four.
There is Michael Jackson "The Beer Hunter"
who sadly died last year, but go to bearhunter.com,
author of some very good books on beer and
whisky for around the world.
Good Christmas present, these books as well.
There is Sir Michael Jackson who's head of
the British Army and head of the UN Force
in Kosovo, it's not him.
And there's Michael Jackson the methodologist.
That sounds promising.
And then there's the other one.
We're going to skip that one.
Now, Michael Jackson the methodologist is
a--and requirements guy, focuses extensively
on this idea that there are different kinds
of problem that require different kinds of
description, okay?
And there are characteristic things that tell
you about the kind of problem it is, and that
will lend itself to suggesting a way of solving
the problem in an appropriate style.
And Robert Floyd, in his paper, on "Paradigms
of Programming," which I want to point out
again, goes back 30 years, he made the point
that one of the challenges that we have in
programming is that we don't have enough paradigms,
using his term.
We do not have a sufficient stock to address
the actual problems.
We have--we have a narrow limited vocabulary.
That was his perspective.
So, if we go and look at something like C++,
there are a number of things that it's quite
good at and aligns very well with.
And we are going to do this kind of idea of
aligning problem solution domains, then--when
you have to do--when you have to talk to the
machine and you care about things that are
machine-level abstractions, then you're on
that level of control.
There's a kind of an essence of control freakery
to the language.
You know, this is not a language for people
who like to drive in automatic.
You need a manual gearshift, okay?
But once you've got that, boy, you can use
it.
So, if you like that, then it will fit.
So, there's a personal fit as well.
But in many other respects, we're talking
about where it fits in the world of the things
we want to build.
Embedded systems is a good fit there.
Real time systems, you have that kind of level
of accuracy.
If you--if you look at languages that have
been general purpose but not designed for
these and you've--for example, if you look
at something like real-time uses of Java,
you practically have to--got the language
and all of the big selling points in order
to get it to work in that--in that environment;
where C++ requires relatively little adaptation
for that point of view, almost none.
It depends on the kind of systems we're talking
about.
High-performance computing, systems programming
in general, algorithmically focused code.
I find that if I have to write an algorithmically
focused code, certain frameworks are pleasing
and easy to use in Java.
If I write algorithmically code in Java, I
don't know if I get--it's difficult to describe
but the experience is boring, the experience
is one of tedium.
I enjoy certain other activities in Java but
algorithmically focused code is just tedious
to write.
It doesn't support the right level of mechanisms;
although extensively, it has a common heritage.
Games programming, not exciting one there;
and obviously, many other domains where C
has been favored in the past.
These are the core areas.
Although C++ has a certain reputations being
a general purpose application language, we
are kind of seeing that one drop off.
And I'm not actually too sad about that, to
be honest.
Having been involved in training and consultancy
for a number of years, I'm quite happy that
there a bunch of people out there not touching--using
C++ for that purpose, so it's making my life
a little bit easier.
So, there are other areas where C++--and it
doesn't really fit.
And some of that is from its own design.
Some of it comes from its history.
Some of it comes from problems with library
design and cultures of library design.
C++ has suffered a diversity and enjoyed a
diversity of different library approaches.
But some areas just never really took off
the way that it could've done, or indeed,
there are other ways in solving these things.
And I'm thinking particularly here, it's nothing
you can't do any of these things.
It's just that when you actually realize when
you work backwards from what you'd like, it
doesn't look like the C++ code.
It's doable.
Of course, it's doable.
Ultimately, anything is doable given a computer.
With smart people, it just might take a bit
longer to do it in dissembler, okay?
But it's all achievable.
That's what we're after, its effectiveness.
So, I'm going to have a quick look at procedural
styles.
So, it seems appropriate to pick a nice little
algorithm to start off with.
This is one of my favorite algorithms.
It's no encapsulates in the STL.
And it's actually a--it's quite a good process
for sort of software development in the small
though difficult to replicate.
And normally, unless, you're a Nobel Laureate
or, you know, Turing Award Winner, maybe you
want to put a step forward, check it, you
know.
So, what do we mean by procedural programming?
It's notion of a hierarchical decomposition
of tasks, okay?
It's about the things that we do.
The things that we do it with tend to be more
slightly more subordinated, they're more passive
entities.
We look at things flowing through this.
In this sense where the notion of the data
structure is considered more passive, there
are some degree of commonality with functional
programming because the idea there is your
primary construct is a thing that does something
or transforms something.
The data flows through the system.
And this is reflected in the styles of analysis
thinking that grew out of this school of thinking,
data flow based approaches.
This is all very much about level.
It's--the dominant element is the thing that
we are doing.
That is decompose the thing that we are doing.
Now, we have a number of choices.
The classic view of procedural programming
is very much the top-down view, the structured
programming view.
I'm a little reserved about using the term
structured programming.
Has anybody actually looked at the book called
"The Structured Programming" that was written
in 1974 by Dijkstra, Hoare and Dahl.
Because in that, you'll actually find the--what
we call object orientation.
It's quite chiefly listed as the third section,
so--because it is programming with structure.
So, structure programming, as a term of differentiation,
I'm not entirely sure it's the right one.
But nonetheless, people often use it in this
way.
There are differences even within this basic
space.
A notion that is often overlooked is there
is a fundamentally different style between
more algorithmic in process based code.
The--for example, there is actually a technical
difference between a procedure and an algorithm.
An algorithm, you wait for it to terminate
because it's supposed to terminate.
That is what characterizes it.
A procedure is not necessarily going to terminate,
and then it leads us into the world of processes.
And indeed, there's a whole different style
out there which we're not going to touch today.
But nonetheless, if you wait for an interactive
system that is written in a procedural style
or indeed any style, if you wait for an interactive
system that characterizes process and processing,
if you wait for that to terminate, you will
be disappointed.
You cannot say, "The post-condition of this
action is," because there is no post-condition.
Post-condition is system shutdown.
So, there's a very different style of expressing
these.
As the algorithmic's view which offers your
one frame of thinking and there's a process
view that offers you a different fit.
So, even within procedural style, there's
a reason that this section is called procedural
styles plural, because there is more than
one.
Again, when we look at things like event-driven
systems, I'm trying to characterize it by
the nature of the problem in the architecture.
We can use procedural styles.
What we end up with is an inversion of control
flow?
Function pointers and in C++ [INDISTINCT]
as well.
Member function pointers support it up to
a point, a little better off when we start
using some of the newer library features.
It's not something that you get out of the
box in C++ in terms of the core language.
But the notion of being able to bind a context
of execution together with the target of execution
of the action, then that's quite nice, that's
very pleasant.
There's a--there's a number of features in
the TL1 library originating from boost and
making their way into the C++0X standard,
which may or may not be hexadecimal.
We're not sure of that value of x.
It's kind of touch and go as I understand
it.
But although we have used this--I mean, if
you think about all the windowing code, it's
clearly event-driven.
The use of callbacks and these approaches.
It fits here.
I would say event-driven systems also fit
with OO, but I've got other things to say
on OO so I won't be revisiting that topic.
But in truth, there is a--there's good support
here but there is a very large hole, from
my point view, having worked with a number
of other sort of languages and context.
The lack of metadata and reflection means
you can't support where style of architecture
known as implicit invocation.
Has anybody here ever used Orc?
Okay, a few hands go up.
There's a big--and of those--has anybody of
those people used Perl?
Yeah, okay.
But, you know, Perl--I never really kind of
got along with Perl.
I thought all kind of certain elegance to
its designed, I think--it has this kind of
implicit invocation approach.
Perl is kind of executable line noise, very,
very strongly kind of top-down procedural
in its approach.
What was nice about Perl--about Orc is that
you would--you would say, right, this is a
language.
It is structured for the execution of matching
regular expressions on input, on a sequence
of inputs.
It's a sequence of transformation.
It's--it fits within a particular architectural
model and it does so very well.
And when does this piece of code get executed?
Well, when it matches these, you have the
notion of a rule or a trigger that it implicitly
matches.
There is no, "If it matches this, than do
this."
It just is.
The structure of the program is characteristically
defined like this.
Now, this is a style that if you--if you have
a language that supports metadata reflection
and indeed an element of meta-programming,
you can get quite a lot out of.
And a lot of--there's a lot of Ruby frameworks
out there that demonstrate the power of this
approach.
And a lot of the model driver architectures
that are perceived as being lightweight in
approach rely on the use of annotations, that
they just allow you to mark up a class.
You can actually do this.
It's a good exercise when you're thinking
of something interesting to do.
Create an Orc like framework that allows you
take a class and annotate all the regular
expressions you would like a particular method
to execute on.
And it's just--and it just is.
It's very, very simple.
There's no registration processes, no if,
else, if.
It's a very different start.
This is a hole in C++.
Now, what I'd like to do is take that event-driven
style or event-driven problem and do something
with it.
And I want to demonstrate some different styles.
Timer on-off.
So, you have heating.
Well, no, you don't, you're in California.
But I'm from the U.K.
We have heating, it gets cold.
You need to turn the heating on, you need
to turn the heating off.
And rather than doing it manually, you'd like
to have this on a timer.
That makes a certain sense.
So, we have our heating class and we can turn
it on and turn it off.
If you favor a flag-driven approach to programming,
it's--you would have enable [INDISTINCT] true
or false.
But let's actually use this kind of sensible
kind of style of program.
An intentional one where we say, "Turn it
on.
Turn it off."
And we got a timer and we choose our time
of day and we initialize that and we have
the notion of being able to run it and cancel
it.
The one I'm interested in though is this.
There is something I would like to do.
I would like to turn that on at one time of
day, and turn it off at another time of day.
What goes in the gap?
Okay.
So, here is a C-style approach.
The C-style way is it has a certain elegance
to it in terms of its brevity.
It is short, it is very sweet.
You have two functions that are based on the
task.
I would like to turn this on; I would like
to turn this off.
It's a very simple view of that level.
And then, we have--yeah, then it kind of goes
a little bit messy.
Void pointers.
Okay.
So, C++ is a statically typed language, and
this works for an awful lot of cases.
But when you really need to sort of start
getting general with certain things, to start
messing about with the bytes, then you need
void pointers.
The other reason that void pointers are often
used is generosity.
I don't want to confuse that with generosity
of templates; but it is that general purposeness.
It's that how you get generics and see as
it were.
You tell the compiler in known and certain
terms.
Thanks for your help, okay, but I'll take
it from here.
Trust me on this, okay.
That's what you're doing.
Now, obviously, it's a trust based game, trust
the programmer.
It is one of the notions, the spirit of C
that we--that we have in C++.
But there is another aspect to this.
I mean, it is--it's difficult to call that
code there and this code here pretty.
It's quite ugly, okay?
And this is--this is messy.
You've got a lot of issues going on here.
You've got a correlation as well.
And this is one of the challenges, is that
there is a--as it were an unnormalized correlation.
What I mean by that is that the top--the thing
you pass in here--the thing you pass in here
are correlated with one another, but you've
just told the type system, "Don't worry about
it."
The type of that depends on what you're--what
you're passing in here.
You better agree.
Now, what is missing here is this correlation.
Now, when we look at this from an object-oriented
point of view, we have a name for a thing.
It's called an object.
You know, the way these things are always
kind of together and we'd like them to be
together in a meaningful way, that's a thing.
So, Classic OO Solution is used in command
pattern.
Now, this tends to be--so this will--this
will work as indeed with the previous one.
But this has required a certain expansion
in the amount of code.
We've introduced a command.
We've got a pure virtual function.
It effectively represents an interface.
Timer is at least simple.
So, if it's--so the--we shifted the complexity
over to this stuff.
And you see the top line represents a contract
that's quite straightforward.
It's this stuff where we're getting the accidental
complexity, the complexity we introduced to
the problem in solving it.
Okay.
That's that--that's just the kind of the noise.
That's the overhead you have to pay to get
this working.
And we've got the thing like the heating point
or all the kinds of other things, and then
it'll do the right thing.
So, type safe, it is--it is correct.
It--it's not as direct as it could be though.
So, if we return to proceduralism but we look
at it from a modern C++ point of view using
either boost functional, TL1 functional, C++0X
function, there is a nice little function
wrapper.
It is the idea of saying, "Well, a function,
we can use an object in a tactical sense and
it wraps it up.
It wraps it up.
It contains the polymorphism, in which we'll
about in the next session.
And now, I've actually got everything wrapped
up.
There--it's quite nice because this is the
only bit of code.
I'm actually showing where it's--it actually
shows you how it execute--or and set up.
So, we got a timer.
I'm going to turn it on.
Here's when it goes on.
We're going to bind the operation to turn
it on against system.
We're going to turn it off.
When it goes off, then we're going to bind
the off operation with the system, and then
we're going to say run, and that's it.
So, there's a certain directness.
This is still procedural code, okay, that
we have not institute--we are using objects
in a relatively tactical sense, but the overwriting
style is that of proceduralism.
But it's done in a style that fits what we're
working with.
Now, Object-Oriented Styles.
And then, again, this is where the plural
really matters.
There's quite a few procedural styles but
the plural really matters when it comes to
object-orientation.
When people talk about the object-oriented
style versus the procedural style, or the
object-oriented paradigm versus the procedural
paradigm, that [INDISTINCT] us over a remarkable
number of differences of perception.
With object-oriented programming, there are
also models that are common to lots of different
object models.
You are partitioning the development and indeed
the runtime into units.
And the development is partitioned into classes
characteristically, that's the typical one,
that's the paradigm of object-oriented programming.
Another use of the word paradigm, example.
Yes?
Yes, not a good word to use.
This is the common example.
There are other object models that don't use
classes and its runtime into objects.
So, you have this notion of characterizing
objects in terms of identity, state and behavior.
For different objects, these way up in different
ways.
So, some objects are stateless.
Some objects, the identity is irrelevant.
And other objects, the behavior is trivial
but the state matters.
There's this kind of shift and interplay between
these three aspects.
So, you have this kind of structure.
And the--a kind of formal way of characterizing
is something that--seeing is the rule of--or
that--kind of the three things--the three
columns of object-orientation; encapsulation,
polymorphism and inheritance.
But if you want to be a little more precise,
it's runtime polymorphism and it's formed
polymorphism that is called inclusion polymorphism.
There are three other types of polymorphism;
parametric, coercion and overload based polymorphism.
C++ does have the benefit supporting all four
of these.
And it's one of the few languages to do so.
So, if we're looking at paradigm, meaning
programming features or mechanisms, then actually,
it's quite rich in this respect.
But as I say, this is only scratching the
surface.
Here are some sort of space of diversity.
You've got a statically typed school and you've
got the dynamically typed school.
When we try and get these people talking to
each other, they have very little to say,
except abuse, okay?
There is--there are--the experience of programming
in small talk.
The experience of programming in Ruby is fundamentally
different to the experience of programming
in C++.
These are very different.
So, people would struggle to recognize these
as being the same kind of object-oriented
thinking.
A lot of people push this a lot 15 years ago.
"Oh, yes, small talk and C++ and objective--and
objective C." And there were a number of other
languages that were puffing around.
The notion is that they are actually very
well different.
They share some common elements.
But it's like, you know, COBOL and Fortran.
They're both procedural and they're from the
1960s.
Well, they must share a lot in common.
Well, yeah.
IBM, you know.
So, that's about it.
They are very different in their outlook.
So, in statically typed models, inheritance
is normally used in--the main reason--one
of the main reasons you use inheritance is
to introduce polymorphism.
In dynamically typed languages, you need it
for that.
The only reason you use inheritance is for
sub-classing as opposed to sub-typing.
It's a very strong distinction.
In statistically typed languages, they may
or may not be support for reflection.
This is something that C++ misses.
It eliminates a whole class of things that
you can do; but of course, we have support
for other things that we can do.
But it does--it does characterize the fact
that this object-orientation is kind of different
to that object-orientation in very fundamental
ways.
There's object based programming.
Object based is often referred to in terms
of just being kind of encapsulation or classic
ADT.
The only kind of polymorphism you're really
thinking about is link-time or compile-time
polymorphism.
It's--there's no--there's no real sense that
you're dealing with runtime substitution of
anything.
That's not really the game.
Inheritance is not a player in the space.
C++ has recent support for that.
And then, we have things like prototype based
approaches, which I guess these days characterize
and it--what can be seen in things like JavaScript.
There is no real concept of class.
Again, that's an object model but not one
that has supported out the box in C++.
And if you do try and support it, it looks
a bit strange, people look at you funny for
doing it.
So, we have a number of things.
You have the data privacy concept in C++.
We have a private keyword; but it's not as
private as you'd like it to be.
We have to recognize that there are techniques
for decoupling C++ code further.
But coupling is one of the dominant forces
that we'll shape or hinder along system and
the style.
But that does have an influence.
We are missing a number of dynamic facilities
in a language, which is a pain in the backside,
to be honest.
The fact that there's no reflection and there's
going to be no reflection in a standard way
does limit certain options.
But again, the presence of virtual functions
and standardized function adaptors is very
useful for a number of other things.
And object-based program, very well supported
indeed.
The no inheritance view, why would this be
useful.
Classically, value-based programming is the
area where this takes off.
This is a sort of a--this is again in the
object-oriented family where it blends nicely
into the generic programming family we'll
look at in a moment.
Now, Object Lifecycle Management tends to
be the challenge that people have.
And sometimes, people look at the world again
in black and white and this side versus that
side.
And it's all about GC versus Explicit Memory
Management.
That's not necessarily the right way to look
at it.
There are four fundamental ways of organizing
lifetimes.
Explicit management, you choose when you get
rid of an object and you do so explicitly.
I would like it at this point in the code
here, and that is decoupled from the point
of which it is created, okay?
It just has to come after it.
Before, it doesn't work and twice definitely
doesn't work.
And that was a bad idea as well, okay?
These are--these are classic failure modes.
Scope bound, this is the simplest of all the
models.
It is automatic, so non-manual, it is deterministic,
likely explicit but it is deterministic.
You scope the life of an object within the
life of another thing.
That thing maybe a bounded scope, a functional
scope or it may be the scope as it with a
lifetime of another object.
My gut instinct is, if you can organize as
much of your code as possible using number
two, that eliminates a large class of hassle
the people encounter.
But if you are used to creating your object
dynamically and then--and that is your favorite
style, then you will find yourself dealing
with the problems of number one.
It's not that you can't do it, it's just sometimes
a bit challenging if you make everything a
pointer that needs to be managed with a delete.
Then, we have Reference counted.
Now, one and two are kind of supported out
of the box of C++.
The third one is a library addition.
This is something that you have to do yourself
or you get from the box shared pointer.
So, it's not supported out of the box.
There are limitations to it, and more becoming
apparent overtime.
I think it is a very effective technique for
the problems that it solves.
But I find people sometimes trying to solve
too much with it.
There are performance costs to this, there
are architectural cost to it.
It's a basically intrusive approach and the--there
is a certain fear sometimes of smart point
to madness.
And they're not always smart, there's--there
is a kind of like, "Yeah, just use it," because
you're not sure about something, use a smart
pointer to do this.
However, that is something that we can support
within a language and it does have applicability.
It can be effective.
And then, we have Garbage collected.
Now, this one's outside the realm of standard
C++, and there's a lot of discussion and debates
about how best to support it.
Garbage collection these days is not the same
as garbage collection was 20 years ago or
40 years ago.
As we move to increasingly concurrent models
of programming, incremental as opposed to
stop the world garbage collectors have a big
win in--for certain kinds of architecture.
And this is a--it will be a shame when all
of that are able support this in a standard
way.
Now, let's have a look at some of the styles
within this broad family of styles.
Inheritance-based approach is inheritance-based
thinking.
This is where many people are first introduced
to object-orientation, where they first get
to grips with having gone beyond the basic
class.
They're object-based approach in C++.
They are then introduced to inheritance.
Now, there's a certain syntactic overhead
doing this.
The default that you expect is not the default
that you get.
But the--once you've got--you've got ahead
around call on public and here's the thing
I'm inheriting from, the normal emphasis is
on being able to compose with respect to code.
The idea of organizing classification as being
a way of organizing the commonality of your
code to avoid duplication, to avoid the joy
of copy and paste, and maybe reduce your macro
account a little bit, you know.
That's--these are all the ideas that this
supports.
So, there is a case here for inheritance-based
program, and you end up with kind of style
and approach, kind of like this.
You end up with a--at least with disciplined
guice, you can end up with a kind of class
hierarchy where you support infrastructure
basic facilities, you build on its services,
then you have your problem domain, your application
space.
And you kind of compose it at this level.
So, it's a composition technique for dealing
with code primarily.
But I would actually say that this whole approach,
even though it's the one that people are often
taught, it is the one that was counted or
reused and all the rest of it.
There's--there really is no such thing as
reuse the way that people describe it.
Reuse is an accident property of anything.
It's difficult to plan for it.
What most people mean when they talk about
reuse is use, you know.
It's a--it's a far less attractive and exciting
term.
When you--when you use a line break, you are
using it for what it was intended for normally.
And you are not reusing it unless you're doing
something really crazy with it.
So, in that sense, reuse in the real world
is about repurposing things.
I can reuse these seats here and rearrange
them to form a bed.
Not a very comfortable bed, but I can do that.
That's not their primary use.
I am reusing them outside their original domain
of replicability.
And what we have here is the notion of what
people are trying to do when they talk about
reuse as create commodity, which is a very
different thing.
So, tomorrow I will fly back to the U.K.,
I will not reuse the plane.
I will just use it for the purpose it was
intended.
There's nothing fancy or exciting about that.
So, this is the kind of the attraction that
people have often ended up with, and it is
a very intrusive approach.
It is sometimes based on coincidental opportunities,
but it does introduce a very strong form of
coupling.
And in C++, that coupling is amplified through
the fact that it's--involves the source code
structure.
So, we need to be keenly aware of that.
But regardless of that, there is this kind
of inheritance-based approach or inheritance
first approach has fallen strongly out of
favor in terms of interface-based programming.
Interface-based programming, which we could
look at from the other side as being part
and parcel of component-based programming
is where we focus and we make a straight separation
of the way you use something, the usage type,
the interface to it from the way that it is
built and implemented.
The underlying concrete class of creation
type that we make a strict separation.
Not just a kind of a, well, we put some of
the implementation details down in the hierarchy.
No.
A really strict separation.
The--we can do this relatively easily in C++.
This is not a problem.
There has been a growing trend towards using
flatter class hierarchies that are wholly
abstract at the top, moving away from the
style where we are trying to accumulate implementation.
It turns out that the challenge is not to
write code, the challenge is to organize code.
And this offers us a more meaningful way of
working with things.
You end up with the equivalent code.
And actually, these two--these two diagrams
are taken from a couple of real projects,
the before and after.
We took inheritance-based code that was tightly
coupled, that was difficult to test and we
teased it apart, retaining little broad layering
structure but making a strict separation between
the concept of the thing that we were dealing
with and the realization possibilities, and
the amount of executable code remains the
same.
It's conserved.
The amount of declarative code does go up
a little bit.
You have more construct.
But the amount of stuff--actually doing stuff
remains consistent.
This is far more extensible.
It also emphasizes something different.
If you go back to this one, there's an intentional--there's
an--there's two intentional points to the
titles.
This is Infrastructure + Services + Domain.
This is Domain x Services x Infrastructure.
This is domain-centered from the point of
view of people who are working with the application.
This is what they see, they are not--they
don't have the weight of the infrastructure
pushing down on them.
And having seen developers struggling with
the fact they're getting all these arbitrary
dependencies through struggling to deal with
those from a testing point of view, this makes
the difference.
They want to see application concepts and
have their code integrate into a more plug-in
style.
That's one of the things that interface-based
approach support very nicely.
The other notion is the freedom of combination,
the flexibility, the degrees of freedom.
This is a plug-in-based approach.
They're not hardwiring assumptions.
Inheritance is a very strong relationship.
This is about composition.
It's not that nobody thought the composition
would be a good idea, it's just that this
style of program naturally--programming naturally
encourages it.
But you can actually see there's a spectrum.
They want to turn out--they want to suggest
that these are two mutually exclusive approaches.
There is a spectrum from the inheritance or
sub classing-based approach to a more sub-typing
and an interface-based approach.
General thinking moves this way, but we do
have some challenges.
This are the--there are the benefits and parallel
development testing, component deployment,
plug-in styles.
So--but it shouldn't make a difference, but
it does.
The lack of it in specific construct in C++
for doing this means a lot of people miss
it.
A lot of people miss it as an opportunity.
It requires more ritual and ceremony as well
as adherence to a--an idiomatic style.
If I'm working in a C# or Java, I just go
interface whatever, open curly, things that
I care about, with semi-colons, I don't have
to worry about putting abstract or anything
like that, and curly, and we are done.
In C++, there is a certain ritual and ceremonies
that I'll have to go through.
It's still called class then I have to argue
with my colleagues at whether or not I'm going
to put an I in front of it.
I must admit I've never been particularly
fond of this approach because it makes everything
bunched up in the wrong place of the alphabet.
And, you know, I came, I sore, I conquered.
No, this is not interesting to us.
Okay.
And that, I don't find a particularly effective
style.
Even worse is where you see companies where
they've got I such and such and then you look
at it and say, "Yeah, but it's got implementation
code in it."
"Yes, it started out an interface, but we
couldn't change the name."
Yes, this is the piece of code that is lying
to you.
It's a convention--it's not as useful as people
think it is.
However, once I've got past that argument,
then I'll go open curly, public, virtual,
I come like with the virtual, equals zero,
semi-colon.
What are we going to do with the destructor?
And how--do we make that--and that's going
to be pure virtual or not, but I still have
to provide a definition.
I can make it protected if I don't want people
to--there's lots of little fascinating decisions
we can go through.
That means there's a little bit of accidental
complexity there.
I've not found it to be a problem personally,
but I do recognize that it is a problem for
many people.
A lot of people just don't get it.
They don't hit the convention.
They don't understand what the implications
are in the long term of actually keeping to
the style; because you're actually telling
people to not write code.
People are paid to write code so they feel
a little bit uncomfortable.
Maybe I could put a little bit of helpful
default stuff in there, you know.
I'll be just helpful.
Programs are terribly helpful to people, but
this is actually a disadvantage.
If you got helpful stuff, put it the next
layer down, keep the top clean.
There are very good reasons for doing this
and there are a number of techniques that
buffer and allow you to change the elements
of the hierarchy and maintain that crisp idea
of, that is just the way you program to it.
What is interesting is that, in spite of the
fact that people do struggle with this in
C++.
When they do it, it tends out to be very effective.
A team that I've consulted for over many years
told me that this was one of the most useful--or,
no, this was the most useful thing I ever
told them.
The possible indication of that is that nothing
else I told them was of any use at all.
That's fine.
You know, getting one hit is good at least.
So--but the notion here is that there is a
style that we are potentially overlooking.
We also have this notion of a lack of proper
modules, like the other side of the interface.
The--actually having a formal unit of composition
for the implementation code, and making a
very clear notion about that.
Naming it, having a concept of dynamic loading
this portable.
It doesn't mean we can't do it.
It does mean that we have to be careful when
we support different platforms, shared objects
on Linux, DLLs, and--or Microsoft Windows,
very different semantics between these.
But nonetheless, there are possibilities here,
but they are largely ever looked because it
doesn't look like you're doing much.
That makes people feel uncomfortable.
Value-based programming, I indicated before
is one of these areas that C++ does have very
good support for.
The idea of--now again, this is not a single
all encompassing idea, this is just a category
of dealing with objects, and they are well
represented in C++.
The idea of values is that they are fine grained
objects that are strongly informational, they're
identities of very little interest, and they
typically reflect on objects in the real world
that have these properties; money, dates and
so on.
Depending on your domain, these have--these
are other values or entities of interest.
But there are particular styles here.
You don't really worry about dynamic memory.
These are things that you can copy around
quite freely, and therefore, copying transparency
can be useful passing by const reference,
all of these things.
This is relatively well understood even if
people don't describe it in these terms.
And the next standard will provide better
opportunities for optimizing this kind of
code, being able to move values, relevant
when we're dealing with all value objects
that do internally manage their own memory
allocation.
It just gets rid of a little bit of potential
waste when you end up with all of these spare
copies floating around.
However, some of the details of page be quite
subtle and I am dreading--I'm dreading the
prospect of actually having to teach some
of these stuff that's going to come up in
the next seminar.
This is kind of a little bit expert-friendly.
It is powerful.
If you know what you want from it, you can
do an awful lot with it.
But I am a little bit concerned at this point,
we--that--what the accidental complexity is.
I don't have enough experience with it to
really comment.
But it does seem interesting that most people
are focusing on--or people are regardless
C++ experts.
So, general Object-Oriented Issues.
Not everything fits this worldview, okay?
And that's acceptable.
Some of that comes about because C++ does
not necessarily have a unified type system.
But in some languages, there is a unification
of the idea of an object and a function, and
there is no contradictions in between these
two.
We can do this through adaptation in C++ but
it requires adaptation as opposed to being
out of the box.
But once you've got it up and running and
once it becomes a culture of a particular
project, then it can be very effective.
But the other aspect is to be keenly aware
that C++ is one of many possible worlds.
This is the kind of the many worlds theory
of objects if you like.
Now, final family of styles.
Well, they're not really families.
Two different styles we're not going to look
at, that are actually unified just because
of the mechanism that you--we use to express
them in C++.
And it's more coincidence than anything else.
There is no such thing as a template paradigm.
There is no such thing.
It is a language mechanism that can be used
for good, for evil, for obfuscation, for clarity,
whatever.
So, let's have a look.
Generic Programming is the one--the name is
unfortunate and this has been recognized by
Alex Stepanov and others that generic programming
is not really the right term because it makes
it sound like it's generics.
This is a style that is focused on algorithms.
It is a style that is general in its outlook.
That is the emphasis.
In C++, we tend to build on compile-time polymorphism
and value-based programming.
In other words, templates, overloading, conversions,
copying and encapsulated memory management.
These are all the key features here.
As a style, well, I'm going to--I'm going
to just skip this slide because we're getting
close to the end.
It's got a lot going for it, but I would say
that it seems to be one I've not seen any
applications that have this as their dominant
structuring principle.
And I'm talking about significant large scale
applications.
So, I'm happy to see some.
But most of the time, I see this.
It tends to be a tactical approach.
The--we have some interesting stuff coming
up in C++0X concepts, and I believe Doug Gregor
has already given a talk here at some point
in the past on that.
So again, I'm going to skip that.
But let's just have a little look at a nice
simple fragment and then the wrap up.
The--this is a little example based on some
code from TL1.
And a random number generators.
There are certain elements of style if you're
using lots of templates.
Remember type [INDISTINCT] is your friend,
often overlooked.
This notion of loop-free programming is the
idea of being able to compose in one place
something, as I said, reminiscent of functional
programming here, being able to compose in
one place.
Something that is centered around an algorithm
of some kind and uses objects that best in
a tactical fashion.
The dominant idea here is the thing you want
to do.
Okay.
Now, mixing styles.
I want to kind of skip that.
I want to kind of briefly talk about template
metaprogramming.
Anybody come across that?
Lots of angle brackets, kind of scary.
Wow, look at that.
That's fantastic.
Okay.
The challenge here is that C++ doesn't really
support metaprogramming in the general sense.
It only supports a particular compile-time
approach.
They could be used for some very, very useful
things.
Some really keen observations--sorry, optimizations.
But what I would like to emphasize here is
that it--it's a--domain of applicability is
not for every programmer.
It is not the kind of thing that you'd use
like an if.
It's not the kind of tool that you so say,
"Yeah, I use that everybody."
If you do, you have to be either really knowing
what you're--really know what you're doing
or not in this case.
You can spot the template metaprogramming
code, can't you?
It's the stuff over here.
I do urge you to look at your code by doing
this technique of zooming right out and reducing
the point size to something little.
We--people do it with helicopters, and they
find the lines of ancient fields still in
the ground from thousands of years ago.
You can do this with code and see the characteristics
from the air.
That's the template metaprogramming.
This is a piece of code that turned into this
piece of code.
There's no template metaprogramming here.
It's just all overloading a couple of objects,
that's it.
And the notion here is that this was unnecessarily
complex for the problems.
It's easy to get blinded by the light.
So, I'm done from that point of view.
I think we may have time for a couple of quick
questions.
There are no questions.
It was all entirely clear.
You understood everything I said, nothing
was contentious.
I believed that for a second.
Okay.
Let's get back to this.
Mixing Generic and OO Styles.
Come on.
Surely, there must be something here.
This shouldn't be difficult because we're
dealing with two orthogonal forms of polymorphism.
That's the theory.
The practice unfortunately is, they really
don't get all.
In practice, we have to write layers of adaptation.
This is annoying, you cannot have virtual
template functions.
This is very annoying.
So in theory, although in the principle they
are actually independent, they do not mix
either.
They stay independent.
That is quite a struggle.
People often write articles and bits of libraries.
Indeed the--one of--one of my contributions
to the Books Library is a type that messes
around with your idea of a polymorphism.
It's kind of, well, you know, it's a value
of any kind but I don't--yes, well, let's
move that around.
There are a number of techniques there.
That function wrapper I showed you early on
is another one.
I invite you to look at the implementation
and see how you feel afterwards.
It's pretty smart.
But again, if that's what it takes to mix,
you have to be very careful.
So, it can be challenging.
In practice, I found you can actually do this.
But you end up with a layering and you reduce
the amount of adaptation.
But you end up with the layering.
I found that objects in the large and generics
in the small, that works very, very nicely
for me.
If I use standard dependency management techniques,
just kind of--like of some Robert Martin type
stuff, they I find that I have--I create myself
a useful space where the generic programming,
it just flows very nicely.
But there's a strict separation and there
is this kind of layering.
Okay.
Is that contentious or do you agree with that
as well?
Come on.
Give me--give me a question.
Matt?
>> Can you give us an example of where you
think that template metaprogramming is a good
idea?
>> HENNEY: There--yeah.
Yeah, okay.
Not here.
Can I--can I give an example of where I think
template metaprogramming is a good idea?
It's difficult to know whether I'm addressing
solution problems or not.
One of the areas where we do find this to
be useful is in selecting types and organizing
types for what--well, for small pointers for
example.
In other words, if I want to get a smart pointer
and I would like it to have--re-export some
capabilities based on its type, the parameterized
type that I give it, then there's no way to
do that without having a compile-time if.
And that compile-time if is going to come
in the form of template metaprogramming.
I found that when I have used--there's--actually,
I lied a little bit, actually.
There is a little bit of a template metaprogramming
here.
I can't remember exactly where it is.
But that's the point, it's very discreet.
I've used it to select between two types,
basic trait style selection.
So, it's very modest to use.
It's just a light sprinkling.
The other area, but I don't have personal
experience in this one--in a practical project
other than personal toy projects, is where
you are using this to--as you were to select
an option that you don't want to take at runtime,
but you'd rather take a compile-time.
I know it was generator constant of some kind.
And you'd rather not have to either pull out
Windows Calculator and calculate it manually
and hard-code it or have the cost of running
it at a compile-time.
Again, some of these features are actually
being--some of the issues I've just described
are actually being addressed in the next C++
standards.
So, you won't necessarily need template metaprogramming.
It's called--more, it's like a bridging solution
in that sense.
>> [INDISTINCT]
>> HENNEY: Okay.
Right.
Thank you very much for your time.
