Thursday, February 18, 2016

Digest for comp.lang.c++@googlegroups.com - 25 updates in 2 topics

Wouter van Ooijen <wouter@voti.nl>: Feb 18 08:33AM +0100

Op 17-Feb-16 om 11:19 PM schreef Ian Collins:
> are small and ideally suited to an incremental test/code/refactor cycle
> such as TDD. Being close to a pure coding exercise, any objections to
> TDD and design are irrelevant at this level.
 
Let's say I write code for a UART to transmit data at a (run-time)
specified baudrate. What would your (first) test be?
 
Wouter
Ian Collins <ian-news@hotmail.com>: Feb 18 08:42PM +1300

Wouter van Ooijen wrote:
>> TDD and design are irrelevant at this level.
 
> Let's say I write code for a UART to transmit data at a (run-time)
> specified baudrate. What would your (first) test be?
 
It wouldn't be the first test (there would be more generic configuration
to test first), but the last test for the requirement would be that the
drive sits the correct bits in the the appropriate configuration
register(s).
 
--
Ian Collins
Ian Collins <ian-news@hotmail.com>: Feb 18 08:45PM +1300

Ian Collins wrote:
 
> It wouldn't be the first test (there would be more generic configuration
> to test first), but the last test for the requirement would be that the
> drive sits the correct bits in the the appropriate configuration
 
It's been a long day... s/drive sits/driver sets/
 
 
--
Ian Collins
Wouter van Ooijen <wouter@voti.nl>: Feb 18 08:58AM +0100

Op 18-Feb-16 om 8:42 AM schreef Ian Collins:
>> specified baudrate. What would your (first) test be?
 
> It wouldn't be the first test (there would be more generic configuration
> to test first),
 
such as?
 
> but the last test for the requirement would be that the
> drive sits the correct bits in the the appropriate configuration
> register(s).
 
But how do you determine what is the correct setting (or: how do you
make sure that the settings you have determined have the desired effect)?
 
Wouter
Wouter van Ooijen <wouter@voti.nl>: Feb 18 08:58AM +0100

Op 18-Feb-16 om 8:45 AM schreef Ian Collins:
>> to test first), but the last test for the requirement would be that the
>> drive sits the correct bits in the the appropriate configuration
 
> It's been a long day... s/drive sits/driver sets/
 
9:00 here, a fresh day just started :)
 
Wouter
Ian Collins <ian-news@hotmail.com>: Feb 18 09:17PM +1300

Wouter van Ooijen wrote:
 
>> It wouldn't be the first test (there would be more generic configuration
>> to test first),
 
> such as?
 
That depends on the device, if the UART is stand alone, there might not
be any but if it is part of a processor, there will probably
configuration registers that have to be setup before the UART can be used.
 
>> register(s).
 
> But how do you determine what is the correct setting (or: how do you
> make sure that the settings you have determined have the desired effect)?
 
The device specification!
 
I actually wrote tests for a UART driver on an ARM STM32 processor last
year. To verify the settings I had a block of mapped memory at the
appropriate address for the device registers and simply tested for the
correct values getting written.
 
--
Ian Collins
Wouter van Ooijen <wouter@voti.nl>: Feb 18 09:22AM +0100

Op 18-Feb-16 om 9:17 AM schreef Ian Collins:
 
>> But how do you determine what is the correct setting (or: how do you
>> make sure that the settings you have determined have the desired effect)?
 
> The device specification!
 
Of course, but how do you verify that you derived the correct (and
complete) conclusions from the device specification? In my experience
*that* is where the errors can be made! (I often make errors - or rather
omissions - in that aspect. I don't recall ever making an error afer
that: once the correct values are determined writing them is trivial.)
 
Wouter
Ian Collins <ian-news@hotmail.com>: Feb 18 09:24PM +1300

Ian Collins wrote:
> year. To verify the settings I had a block of mapped memory at the
> appropriate address for the device registers and simply tested for the
> correct values getting written.
 
An example:
 
void
TestUART::testConstructDeviceOneAssignsCorrectBaudRateDivider()
{
const auto baudRate = 100;
const auto clockFrequency = 1000;
 
test::STM32F3xxuPClockDriver_GetClock::willReturn(clockFrequency);
 
STM32F3xxUARTDriver( STM32F3xxUARTDriver::USARTPort1, nullptr, 0,
baudRate );
 
TS_ASSERT_EQUALS( clockFrequency/baudRate, usart1->BRR );
}
 
test::STM32F3xxuPClockDriver_GetClock is a mock for another interface,
usart1 is an address defined in one of the headers.
 
--
Ian Collins
Ian Collins <ian-news@hotmail.com>: Feb 18 09:28PM +1300

Wouter van Ooijen wrote:
> *that* is where the errors can be made! (I often make errors - or rather
> omissions - in that aspect. I don't recall ever making an error afer
> that: once the correct values are determined writing them is trivial.)
 
By sanity checking against real hardware. We had some test code from a
spike build that validated our understanding of the real hardware.
 
Oh and did I mention pair programming? That's be best way of avoiding
those silly errors!
 
--
Ian Collins
Wouter van Ooijen <wouter@voti.nl>: Feb 18 09:39AM +0100

Op 18-Feb-16 om 9:28 AM schreef Ian Collins:
>> omissions - in that aspect. I don't recall ever making an error afer
>> that: once the correct values are determined writing them is trivial.)
 
> By sanity checking against real hardware.
 
Now that the test I would start with.
 
Note that I mentioned 'run-time specified baudrate'. Would you test
(whether register-values-based or with real hardware) be for one
specific baudrate? And would the code you write be for one baudrate only
or for the allowed range?
 
Wouter
Ian Collins <ian-news@hotmail.com>: Feb 18 11:22PM +1300

Wouter van Ooijen wrote:
> (whether register-values-based or with real hardware) be for one
> specific baudrate? And would the code you write be for one baudrate only
> or for the allowed range?
 
Now that is really hard to answer, there are so many ifs buts and
maybes! In the example I cited, there was only one baud rate, so we
only had to test that the correct number was written to the register.
if there were several discrete values, how many to test would depend on
how the value was derived.
 
I would probably test the setting of the register and the the algorithm
used to calculate the value. The algorithm is just like any other and
not specifically driver/embedded code code.
 
 
Either way my point - that TDD is just as relevant and useful for low
level embedded code as it is for business logic - still stands. Code is
code. How you implement the details of the tests may vary, but the
process does not.
 
--
Ian Collins
Ian Collins <ian-news@hotmail.com>: Feb 18 11:24PM +1300

Wouter van Ooijen wrote:
> Op 18-Feb-16 om 8:45 AM schreef Ian Collins:
 
>> It's been a long day... s/drive sits/driver sets/
 
> 9:00 here, a fresh day just started :)
 
23:23 here, time to leave the Linux kernel bugs to fester until morning!
 
--
Ian Collins
Wouter van Ooijen <wouter@voti.nl>: Feb 18 11:57AM +0100

Op 18-Feb-16 om 11:22 AM schreef Ian Collins:
> level embedded code as it is for business logic - still stands. Code is
> code. How you implement the details of the tests may vary, but the
> process does not.
 
Some messages ago you said that you follow the 3 rules, I am trying to
find how you do that in a meaningfull way. Rule 2 says
 
"You are not allowed to write any more of a unit test than is sufficient
to fail."
 
And 3 says
 
"You are not allowed to write any more production code than is
sufficient to pass the one failing unit test."
 
IMO whenever you add a new baudrate to the set suite, the simplest way
to make that test pass is to add an if statement or maybe a switch case
or a table entry. Writing a formula that calculates the settings from
the baudrate is more complex (reequires more code), so doing that
violates rule 3?
 
Wouter
legalize+jeeves@mail.xmission.com (Richard): Feb 18 05:48PM

[Please do not mail me a copy of your followup]
 
Wouter van Ooijen <wouter@voti.nl> spake the secret code
>> and get him to champion it. If you can convince Uncle Bob, I'm very
>> interested in hearing about it.
 
>I'd rather convert [...]
 
Three words in and we're already into a straw man.
 
You present an opinion that is against TDD and you put words in Uncle
Bob's mouth so that you can conveniently make him out to be a zealout
and demolish the straw man.
 
When I point out that Uncle Bob is adopting a position based on
evidence and experience (not just his, but the experience of many
people), you invent another straw man turning it into a religious
debate.
 
Positions adopted based on evidence and supporting experience (the team
at IBM, the team at MS, etc.) are not religious positions.
 
When I see you respond this way, it makes you out to be the religious
zealout, not Uncle Bob.
--
"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>
Wouter van Ooijen <wouter@voti.nl>: Feb 18 07:04PM +0100

Op 18-Feb-16 om 6:48 PM schreef Richard:
 
> You present an opinion that is against TDD and you put words in Uncle
> Bob's mouth so that you can conveniently make him out to be a zealout
> and demolish the straw man.
 
I did not put any words in his mouth (can you quote any that I did put
in?), I just expressed (in a tongue-in-cheeck way) that I don't have
much faith in converting him.
 
Maybe more interesting is what I wrote after that fragment: I don't like
the three rules when they are interpreted literally. The few talks by
Uncle Bob that I have seen seem to indicate that he does.
 
In the follow up I had a reasonable discussion with Ian, which we will
probably continue when our timezones permit it. If you want to
contribute to the real discussion you are welcome.
 
My main pain point is that to me the rules seem to force me to write
only one failing test case, and then just correct the code to pass that
case. Other people talk about refactoring, but the three rules don't
mention that, so following the 3 rules (and only the 3 rules, as Bob
repeatly states in his talks) religiously seems (to me) to rule out
refactoring, which seems absurd to me.
 
Wouter van Ooijen
Ian Collins <ian-news@hotmail.com>: Feb 19 09:29AM +1300

Wouter van Ooijen wrote:
> or a table entry. Writing a formula that calculates the settings from
> the baudrate is more complex (reequires more code), so doing that
> violates rule 3?
 
Not if it still passes the tests. Being able to refactor your code at
any stage and know that you haven't broken it is one of the benefits of
the process. So yes, you might start off with a chain of ifs and then
go back and change the code to use a better algorithm.
 
That's OK, that's good. One of the biggest problems I see with people
new to TDD is that they done do what I consider the most important step
- refactoring. So they end up with the messy code TDD's detractors
complain about.
 
Write a test
Pass the test
Commit
Refactor
Commit
 
--
Ian Collins
Wouter van Ooijen <wouter@voti.nl>: Feb 18 10:00PM +0100

Op 18-Feb-16 om 9:29 PM schreef Ian Collins:
>> the baudrate is more complex (reequires more code), so doing that
>> violates rule 3?
 
> Not if it still passes the tests.
 
Rule 3 at http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd is
"You are not allowed to write any more production code than is
sufficient to pass the one failing unit test."
 
In your opinion, does that include refactoring?
 
Wouter
Ian Collins <ian-news@hotmail.com>: Feb 19 10:10AM +1300

Wouter van Ooijen wrote:
> "You are not allowed to write any more production code than is
> sufficient to pass the one failing unit test."
 
> In your opinion, does that include refactoring?
 
No. I would amend rule 3 to say
 
"You are not allowed to add functionality to any production code than is
sufficient to pass the one failing unit test."
 
Or even
 
"You are not allowed to change the visible behaviour of any production
code than is sufficient to pass the one failing unit test."
 
Although that may be a bit too vague.
 
I prefer to teach the write a test, pass the test cycle as adding
functionality rather than adding code. Code is too specific in my
opinion: you might add functionality by removing code!
 
--
Ian Collins
Vir Campestris <vir.campestris@invalid.invalid>: Feb 18 09:27PM

On 17/02/2016 22:19, Ian Collins wrote:
> are small and ideally suited to an incremental test/code/refactor cycle
> such as TDD. Being close to a pure coding exercise, any objections to
> TDD and design are irrelevant at this level.
 
I'm working on an ALSA driver for an embedded device at the moment.
 
I can't find any decent specs, the hardware doesn't seem to quite
behave, and the calls from the kernel are... odd.
 
I have a slight feeling that the whole of Linux was thrown together by a
bunch of hackers (good ones) and that the kernel for my device has been
changed by other hackers. None of them wrote down what they were
intending to do, so when I look at different drivers that do things in
different ways I can't really tell which is the new way, and which the old.
 
I can't really write any tests at all - I can't really tell what it's
supposed to do, beyond make a noise. And since the I2S DMA is broken
that's a long way off.
 
Andy
Vir Campestris <vir.campestris@invalid.invalid>: Feb 18 09:30PM

On 17/02/2016 11:26, Juha Nieminen wrote:
> I don't think anybody (who knows anything) is claiming that TDD is
> the silver bullet, the perfect tool for every possible situation.
 
Extreme Programming. (We did the training course - then decided not to
do it)
 
Andy
"Öö Tiib" <ootiib@hot.ee>: Feb 17 04:15PM -0800

On Thursday, 18 February 2016 00:58:07 UTC+2, JiiPee wrote:
> I have had this many times that I have some global (or static) non-class
> function which I want to get access to a certain class but not its
> private members. Just to illustrate (not a perfect example..):
 
I have only had such desire with "frozen" code that may not be
changed but that clearly lacks some vital functionality.
The 'private' isn't security. It just avoids accessing by accident
what you don't really want to access. In C++ everything is
accessible when you want to:
 
#include <iostream>
#include <string>
 
// frozen class
class Human
{
public:
Human(std::string name, int id)
: m_name(name)
, m_ID(id)
{}

std::string getName() const {return m_name;}
 
private:
std::string m_name;
int m_ID; /// humans id there are no way to access
};
 
 
// robbing template (from Johannes Schaub)
template<typename Tag, typename Tag::type M>
struct Rob
{
friend typename Tag::type get(Tag)
{
return M;
}
};
 
// preparations to rob Human::m_ID
struct Human_id
{
typedef int Human::*type;
friend type get(Human_id);
};
template struct Rob<Human_id, &Human::m_ID>;
 
 
// robbery in action:
bool checkHumansLegalStatus(const Human& human)
{
int id = human.*get(Human_id());
std::cout << "robbed: " << human.getName() << " " << id << '\n';
return id == 42;
}
 
int main()
{
Human jipee {"Jippee", 42};
return checkHumansLegalStatus(jipee);
}
 
 
> What to do? I dont want it to be a friend, to expose ALL privates ...
> would be nice if C++ had a "conditional friend" .. so I could say which
> members it can get access to , like here.
 
I do not actually understand what you want or don't want. The issue may
be that your "class" is getters/setters and its *actual* logic of checking
its invariant is elsewhere outside. IOW design error.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 18 02:56AM +0100

On 2/17/2016 11:57 PM, JiiPee wrote:
> to other data members which it does not need). Protected kind of
> security would be pretty close what would do the job, but there is no
> "protected friend".
 
 
Apparently you want checkHumansLegalStatus to have the same kind of
access as a function in a derived class would have to members of an
instance of that derived class.
 
There are many ways, and I would probably just use a `static_cast` with
a little bit of irrelevant formal UB.
 
But, working entirely within the C++ type system, can go like this:
 
 
<code>
#include <string>
 
namespace zulu {
using std::string;
 
class Human
{
private:
string name_;
int id_;
 
protected:
auto id() const
-> int
{ return id_; }
 
public:
auto name() const
-> string
{ return name_; }
 
Human( string const& name, int const id )
: name_( name ), id_( id )
{}
};
 
namespace impl {
class Human_x
: public Human
{
public:
static
auto is_legal( Human const& human )
-> bool
{
int const id = (human.*&Human_x::id)();
return (id == 42);
}
};
} // namespace impl
 
auto is_legal( Human const& human )
-> bool
{
struct Access: Human
{
static
auto is_legal( Human const& human )
-> bool
{
int const id = (human.*&Access::id)();
return (id == 42);
}
};
return Access::is_legal( human );
}
} // namespace zulu
 
#include <iostream>
using namespace std;
auto main() -> int
{
zulu::Human const a( "a", 666 );
zulu::Human const b( "b", 42 );
 
cout << boolalpha << is_legal( a ) << ' ' << is_legal( b ) << '\n';
}
</code>
 
 
Cheers & hth.,
 
- Alf
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 18 02:59AM +0100

On 2/18/2016 2:56 AM, Alf P. Steinbach wrote:
 
Oh sorry, that `namespace impl` shouldn't be there.
 
That's what I wrote first, before I moved that class inside the function.
 
Jeez, why doesn't Thunderbird at least WARN me about these things?
 
 
Cheers!,
 
- Alf
JiiPee <no@notvalid.com>: Feb 18 04:29PM

but we dont need that
class Human_x
 
it runs without that
JiiPee <no@notvalid.com>: Feb 18 04:28PM

On 18/02/2016 01:56, Alf P. Steinbach wrote:
 
> cout << boolalpha << is_legal( a ) << ' ' << is_legal( b ) << '\n';
> }
> </code>
 
ok, just compiling and checking... seems to run correctly
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: