Monday, July 13, 2015

Digest for comp.lang.c++@googlegroups.com - 20 updates in 4 topics

legalize+jeeves@mail.xmission.com (Richard): Jul 13 05:08PM

[Please do not mail me a copy of your followup]
 
OK, this is a long post that covers ideas from object-oriented design
and test-driven development.
 
Ian Collins <ian-news@hotmail.com> spake the secret code
>> Dependencies on concrete classes are considered harmful and should be
>> replaced with dependencies on abstractions.
 
>By whom?
 
Well, I put Mr. Flibble in my KILL file when he couldn't control
himself in responding to religious flamewar threads, so I didn't see
the original post. I'm also unlikely to respond to anything else he
says on this thread, because he's staying in my KILL file.
 
However, it seems in this quoted statement Mr. Flibble is conflating
(confusing?) two different pieces of advice. First, there is the
dependency inversion principle (DIP) and there is the technique of
dependency injection.
 
"In object-oriented programming, the dependency inversion
principle refers to a specific form of decoupling software
modules. When following this principle, the conventional
dependency relationships established from high-level,
policy-setting modules to low-level, dependency modules are
inverted (i.e. reversed), thus rendering high-level modules
independent of the low-level module implementation details. The
principle states:
 
A. High-level modules should not depend on low-level modules. Both
should depend on abstractions.
 
B. Abstractions should not depend on details. Details should
depend on abstractions.
 
The principle inverts the way some people may think about
object-oriented design, dictating that both high- and low-level
objects must depend on the same abstraction."
 
<https://en.wikipedia.org/wiki/Dependency_inversion_principle>
 
This is the 'D' in the mnemonic 'SOLID' object oriented design
principles. (This is related to, but not identical to, the idea of
dependency injection in order to construct an object that depends on
another object, but we will get to that in the 2nd point below.)
<https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)>
 
I have always interpreted this principle to be talking about "objects"
as compared to "value types". I'm using these terms in the same
sense as they are introduced in "Growing Object-Oriented Software,
Guided by Tests" by Freeman and Pryce. <http://amzn.to/1Sjskpm>:
 
"Values and Objects
 
When designing a system, it's important to distinguish between
_values_ that model unchanging quantities or measurements, and
_objects_ that have an identity, might change state over time, and
model _computational processes_."
 
In a C++ program, value types are things like std::string and containers
like std::vector and so-on. The "objects" are things whose identity
matters or model a computational process. Suppose we have a class
representing a person in a payroll application. This is more
appropriately considered an object and not a value type. In that same
payroll application there may be a class called Money that is used to
represent payments between the employer and the employee. This is
more appropriately considered a value type. In C++ value types are
often called "concrete types", because they have no virtual methods
and don't derive from an interface. They aren't intended to be part
of a runtime polymorphic class hierarchy. The abstraction here is the
base polymorphic interface that all derived members of the hierarchy
implement in order to satisfy the Listkov Substitution Principle
(LSP), the 'L' in 'SOLID'.
 
However, C++ also supports static polymorphism through templates.
In these cases, it is often intentional that there are no virtual methods.
The template mechanisms do not preclude the use of virtual methods as
well for a combination of static and dynamic polymorphism, but this
is a fairly advanced template design pattern. Here the polymorphism
is generally obtained through template parameters and not necessarily
through inheritance. The template arguments are assumed to be models
of a Concept <http://en.cppreference.com/w/cpp/concept>, such as a
Forward Iterator <http://en.cppreference.com/w/cpp/concept/ForwardIterator>.
 
In both cases, good design following DIP says that the details should
depend on abstractions. An object collaborates with other objects
through an interface so that both can vary independently without
impacting the other. In C++, a web of collaborating objects following
DIP gets the benefit of the "compiler firewall" because the
changes to the implementations of collaborating interfaces don't cause
their collaborators to be recompiled because they literally only
depend on the abstraction of the interface. You put the interface in
one header file and the implementation's declaration in another.
 
In the static polymorphism case, reusing the standard algorithms
doesn't require me to change my containers so long as they and their
associated iterators model the appropriate concepts used by the
iterator arguments to the algorithms. Similarly, I can write my own
algorithms that operate seamlessly on the standard containers without
those containers having been constructed with knowledge of my
algorithms. The key mechanism that makes this work is the iterators
which decouple the containers from the algorithms and vice-versa. The
iterators are the abstraction upon which both containers and
algorithms depend in order to cooperate with each other.
 
>> member objects for example but rather should use a factory passed into
>> its constructor.
 
>Why?
 
This is a very Java-esque way of looking at things, but the same
technique can be applied in C++. It only makes sense for "objects"
and not for "value types". There is no reason to introduce a factory
of std::string, for instance, even if I create a value type that
creates std::string dynamically. (Usually a value type will use
composition instead of the heap, but a std::string is itself an
example of a value type that uses the heap for storage.)
 
So why introduce a factory for some type T instead of using 'new T'?
Let's look at it from the perspective of unit testing. When I'm unit
testing, I want to mock out the collaborators of the system under test
(SUT). With dynamic polymorphism where the SUT (of type S) dynamically
creates instances of a object collaborator (of type T, implementing some
abstract interface I) dynamically, how can I sense the interaction with
the dynamically created collaborator? If the concrete type T is used
directly by S with 'new T', then I don't have a nice mechanism directly
in the language for substituting a mock of type I under the circumstances
of my test. S is really collaborating with I, the abstraction
implemented by T, but you can't new up an interface, you can only new
up an implementation of the interface, so it ends up being directly
coupled to T. Anytime the implementation of I changes, T changes and
forces S to be recompiled even though the underlying collaboration --
the interface abstraction I -- didn't change. This is because we
aren't following the DIP and we have details depending on details.
 
However, if S is constructed by taking a factory that creates
instances of type T, then under test circumstances I can provide a
factory that provides mock instances of I and in production
circumstances I can provide a factory that creates instances of T that
implement the abstraction I.
 
With static polymorphism, there isn't any need for a factory because
the type implementing the abstraction is directly supplied by the code
instantiating the template. The expression 'new T' for some template
parameter T in a template function or class isn't directly coupling
the code to some specific implementation of T. In our test code we
simply need to make sure that the expression 'new T' makes sense for
our supplied mock type. Existing mock libraries let us write sensible
expectations for template arguments, even when new instances are
created dynamically, although it can get a bit tedious. There is
probably some work that could be done in existing mock libraries to
make this easier.
 
So following the DIP implies that direct coupling of collaborating
object types leads you to want to introduce factory interfaces so that
instead of directly coupling the two implementations of objects you
can couple them through abstractions instead.
 
In a language like Java, that only supports dynamic polymorphism, that
means that you don't allow 'new' when applied to object types. It is
still allowed for value types. In a language like C++ that fully
supports both static and dynamic polymorphism, you use the appopriate
mechanism at the appropriate time. Virtual functions and dynamic
polymorphism are great for modeling abstractions and collaborating
interfaces. If you aren't dynamically creating collaborators, simply
use dependency injection for your constructors and create the objects
by passing them their collaborators. This technique is often used to
follow the DIP, but the principle and the technique are different.
<https://en.wikipedia.org/wiki/Dependency_injection>
 
There is no need to use dependency injection for value types. I don't
need to sense how a string is used inside an object I'm unit testing,
I simply verify the object from the outside. For an instance of an
object type, I might ask it to do something and verify it from
externally visible properties of the object when I'm testing something
state-oriented. (If the state is only internally visible and doesn't
manifest itself in any way external to the object, then it doesn't make
sense to try and write a test for that.) If I'm testing an object
that models a computational process then I'm most likely interested in
how this object interacted with its collaborators. I can sense that
interaction through the use of mock collaborators.
 
When I'm testing a value type object, it doesn't have any collaborators
by definition. Everything I can do to a value type can be observed
from the outside. Imagine yourself in the (unenviable) job of writing
a set of unit tests to verify the behavior of std::string. You can
cover every member function of std::string without knowing *anything*
about how the string is internally represented. All the required
semantics can be verified from its set of public member functions.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
legalize+jeeves@mail.xmission.com (Richard): Jul 13 05:10PM

[Please do not mail me a copy of your followup]
 
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> spake the secret code
 
>Which is, that practically nobody uses the above [SOLID] principle[s]. ;-)
 
I disagree, the SOLID principles are embraced throughout the standard
library. They just don't do it the 'Java way'.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
legalize+jeeves@mail.xmission.com (Richard): Jul 13 05:28PM

[Please do not mail me a copy of your followup]
 
Ian Collins <ian-news@hotmail.com> spake the secret code
 
>Given the three options of abstract, concrete without non-trivial inline
>member functions or Pimpl classes all are equally simple to mock.
 
I'm guessing you're using a lot of link-time mocking?
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
legalize+jeeves@mail.xmission.com (Richard): Jul 13 05:46PM

[Please do not mail me a copy of your followup]
 
Ian Collins <ian-news@hotmail.com> spake the secret code
 
>I agree. No one who uses TDD writes brain-dead isolated tests.
 
I'm not sure what it means for a test to be "brain-dead isolated".
 
However, there are plenty of times when I've started with tests and
gotten to a good design without designing up-front any interfaces or
abstractions explicitly. Did I have an idea of where I wanted to go
in my head? Sure, but I didn't write it down as a "design document".
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
Mr Flibble <flibble@i42.co.uk>: Jul 13 07:59PM +0100

On 13/07/2015 18:08, Richard wrote:
> [Please do not mail me a copy of your followup]
 
> OK, this is a long post that covers ideas from object-oriented design
> and test-driven development.
 
[snip]
 
I can't remember the last time I read so much that said so little.
 
/Flibble
Mr Flibble <flibble@i42.co.uk>: Jul 13 08:06PM +0100

On 13/07/2015 18:08, Richard wrote:
> (confusing?) two different pieces of advice. First, there is the
> dependency inversion principle (DIP) and there is the technique of
> dependency injection.
 
I am not confused at all: by passing a factory to a constructor you are
effectively using *both* dependency injection AND dependency inversion.
 
[snip]
 
/Flibble
Ian Collins <ian-news@hotmail.com>: Jul 14 08:47AM +1200

Richard wrote:
 
>> Given the three options of abstract, concrete without non-trivial inline
>> member functions or Pimpl classes all are equally simple to mock.
 
> I'm guessing you're using a lot of link-time mocking?
 
Yes. A lot of my work is low level system code and down there I'll be
mocking mainly C libraries.
 
--
Ian Collins
Ian Collins <ian-news@hotmail.com>: Jul 14 08:52AM +1200

Richard wrote:
> gotten to a good design without designing up-front any interfaces or
> abstractions explicitly. Did I have an idea of where I wanted to go
> in my head? Sure, but I didn't write it down as a "design document".
 
I've been down the same path many times, especially what doing XP for
one.. In bigger projects some minimal up-front interface design is
necessary.
 
--
Ian Collins
legalize+jeeves@mail.xmission.com (Richard): Jul 13 10:01PM

[Please do not mail me a copy of your followup]
 
Ian Collins <ian-news@hotmail.com> spake the secret code
 
>I've been down the same path many times, especially what doing XP for
>one.. In bigger projects some minimal up-front interface design is
>necessary.
 
I agree. The larger the system you're designing, the more up-front
work it requires. However I would say that most of the time when I
was required to produce "design documents" it was largely busy work
that could have been avoided by simply pair programming on the work.
YMMV, of course.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
legalize+jeeves@mail.xmission.com (Richard): Jul 13 10:02PM

[Please do not mail me a copy of your followup]
 
Ian Collins <ian-news@hotmail.com> spake the secret code
 
>> I'm guessing you're using a lot of link-time mocking?
 
>Yes. A lot of my work is low level system code and down there I'll be
>mocking mainly C libraries.
 
OK, given that confirmation, have you looked at cgreen? It's a unit
test framework built specifically around link-time mocking and the use
of C. All my TDD work has been in C++ where I could use static or
dynamic polymorphism for mocking out collaborators, so I haven't used
link-time mocking. However, I can see it's usefulness.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
Ian Collins <ian-news@hotmail.com>: Jul 14 10:35AM +1200

Richard wrote:
 
> OK, given that confirmation, have you looked at cgreen? It's a unit
> test framework built specifically around link-time mocking and the use
> of C.
 
No, I haven't. I use my own mocking framework that is unit test
framework neutral (it's been used with CppUnut, CxxTest and Google
Test). I prefer to keep the two coupled so I can use my framework with
whatever unit test setup a client uses.
 
> All my TDD work has been in C++ where I could use static or
> dynamic polymorphism for mocking out collaborators, so I haven't used
> link-time mocking. However, I can see it's usefulness.
 
It's pretty much the only option when you are working with system (or
object only third party) libraries.
 
My approach has been to apply the same techniques to pure C++ code and
mixed C and C++. This leads to an unconventional approach to pure C++
code where it is the individual class member functions that are mocked,
rather than class instances. While this may appear odd, I've found most
test cases revolve around checking passed parameter values and responses
to returned values. In these cases, being able to use the code
generated by the framework rather than having to write mock objects
saves a lot of work.
 
--
Ian Collins
"Lőrinczy Zsigmond" <zsiga@nospam.for.me>: Jul 13 08:41AM +0200

> int index = 5;
> int& ref = vec[index];
 
> Is ref an alias for vec[5] or for vec[index]?
 
Note: references aren't pointers, say the C++-fanboys.
Nonetheless, they are pointers, immutable pointers with special syntax.
So with assignments like this you can very easily get a dangling
pointer: if you add/or delete elements to 'vec', address of vec[5]
might be changed (cf: realloc), so 'ref' will point to an invalid
address.
"Öö Tiib" <ootiib@hot.ee>: Jul 13 04:20AM -0700

On Monday, 13 July 2015 09:43:12 UTC+3, Lőrinczy Zsigmond wrote:
 
> > Is ref an alias for vec[5] or for vec[index]?
 
> Note: references aren't pointers, say the C++-fanboys.
> Nonetheless, they are pointers, immutable pointers with special syntax.
 
That feels oversimplification since there are some other important
differences besides immutability ... worth to note like ... no null references,
no references of references, no arrays of references, no pointers to
references and reference to temporary can extend life-time of that
temporary.
 
Such additional constraints can make references safer to use (for
programmer) and simpler to optimize that usage (for compiler) than
pointers.
Bo Persson <bop@gmb.dk>: Jul 13 02:45PM +0200

On 2015-07-13 08:41, Lőrinczy Zsigmond wrote:
 
>> Is ref an alias for vec[5] or for vec[index]?
 
> Note: references aren't pointers, say the C++-fanboys.
> Nonetheless, they are pointers, immutable pointers with special syntax.
 
The similarity between a pointer and a reference is that they are both
most often implemented by storing the address of an object. That doesn't
mean that they are the same in any other way.
 
You wouldn't say that all other types are the same, because they all
store bits. Would you?
 
 
Bo Persson
"Lőrinczy Zsigmond" <nospam@for.me>: Jul 13 08:44PM +0200

On 2015.07.13. 18:40, Stefan Ram wrote:
 
> Then, a variable like »i« in
 
> { int i = 0; ... }
 
> also is an »immutable pointer with special syntax«.
 
Sure. Provided it contains an address and its value cannot be changed.
"Öö Tiib" <ootiib@hot.ee>: Jul 13 02:03PM -0700

On Monday, 13 July 2015 21:44:37 UTC+3, Lőrinczy Zsigmond wrote:
 
> > { int i = 0; ... }
 
> > also is an »immutable pointer with special syntax«.
 
> Sure. Provided it contains an address and its value cannot be changed.
 
Reference is not said to be object that contains an address of object
(or function). Reference is not object at all unlike pointer. It is
said to be bound to refer to some object (or function) at initialization
but standard does not say how.
 
It is not empty propaganda of "C++ fanboys". It may be address, but
when we look into code generated by compilers then reference to local,
automatic storage variable is usually there as offset to stack (like
that automatic storage variable itself).
Doug Mika <dougmmika@gmail.com>: Jul 13 11:39AM -0700

If a template variable type can be deduced from the variables provided in the constructor of a template class, then is it needed? I thought it wasn't, but when in the following program (below) I replace the line:
map<int, string, function<bool(const int&, const int&)>>
mapIntToString1(ReverseSort<int>);
with:
map<int, string>
mapIntToString1(ReverseSort<int>);
the code no longer compiles? Why isn't the compiler able to deduce from the submitted function ReverseSort<int> that the template variable type is bool(*)(const int&, const int&)? Is the compiler only smart enough to deduce this for template functions?
 
 
#include <iostream>
#include <string>
#include <map>
#include <functional>
 
using namespace std;
 
template<typename KeyType>
bool ReverseSort(const KeyType& key1, const KeyType& key2){
return (key1 > key2);
}
 
int main(int argc, char** argv) {

//The Program
map<int, string, function<bool(const int&, const int&)>>
mapIntToString1(ReverseSort<int>);
mapIntToString(ReverseSort<int>)
 
mapIntToString1[1] = "one";
mapIntToString1[2] = "two";
mapIntToString1[3] = "three";
mapIntToString1[4] = "four";
 
for (auto m : mapIntToString1){
cout << m.first << "," << m.second << endl;
}
}
Victor Bazarov <v.bazarov@comcast.invalid>: Jul 13 03:20PM -0400

On 7/13/2015 2:39 PM, Doug Mika wrote:
> If a template variable type can be deduced from the variables
> provided in the constructor of a template class, then is it needed?
 
It can't. On what basis do you assume that it can? You ask a question
that is not possible to answer because it is based on a wrong premise.
 
> I thought it wasn't, but when in the following program (below) I
replace the line:
> map<int, string>
> mapIntToString1(ReverseSort<int>);
> the code no longer compiles? Why isn't the compiler able to deduce
from the submitted function ReverseSort<int> that the template variable
type is bool(*)(const int&, const int&)?
 
It's not required to, by the language Standard. And the requirement is
absent because it would be much more difficult to define other rules if
it existed.
 
> Is the compiler only smart
enough to deduce this for template functions?
 
Yes, the compiler is obviously dumber than you.
 
The rules say that to define an object of some type you need to provide
the type. Deduction of template arguments is only allowed by the
Standard in some contexts, and the constructor arguments for direct
initialization are not one of those contexts. A constructor is not a
regular function.
 
Find a copy of a good book that explains it and study. A decent book
enumerates the rules and shows what rules exist. A great book also
explains why such rules exist. The former is often enough, the latter
is sometimes too difficult to comprehend.
 
> [..]
 
V
--
I do not respond to top-posted replies, please don't ask
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jul 13 10:11PM +0200

On 13-Jul-15 8:39 PM, Doug Mika wrote:
> submitted function ReverseSort<int> that the template variable type is
> bool(*)(const int&, const int&)? Is the compiler only smart enough to deduce
> this for template functions?
 
The language was not designed to support this, but the need has become
more and more manifest, with all kinds of "make" functions being defined.
 
There is some discussion at SO, at
 
<url:
http://stackoverflow.com/questions/29677505/why-cant-constructors-deduce-template-arguments>
 
There is a current proposal to support this, N4471, referenced from the
SO discussion,
 
<url: http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4471.html>
 
and it lists some of the difficulties, in particular that
 
* The constructor argument type can be indirectly defined in terms of
a template parameter, which then is difficult to deduce.
 
I'm not sure I agree with the comment in the "inject class name" example
though.
 
This looks like still a pretty rough draft that needs to be ironed out.
I can't see that it will make C++17. But never say never! :)
 
 
Cheers & hth.,
 
- Alf
 
--
Using Thunderbird as Usenet client, Eternal September as NNTP server.
ram@zedat.fu-berlin.de (Stefan Ram): Jul 13 04:40PM

>references
...
>are pointers, immutable pointers with special syntax.
 
Then, a variable like »i« in
 
{ int i = 0; ... }
 
also is an »immutable pointer with special syntax«.
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: