Vous êtes sur la page 1sur 17

Agile Development

with C++
Code, Culture, Context and Change

Kevlin Henney
kevlin@curbralan.com

Agenda

• Intent
Š Some observations on agile development with C++
• Content
Š C++ development
Š Culture shock
Š Accidental complexity
Š Dependency management
Š Memetic engineering
Š In closing

XP Day 06 2
C++ Development

One reason that life is complex is that it has a real part and an imaginary part.
Andrew Koenig

XP Day 06 3

Characterising C++

• C++ is a language that is well suited to large-


scale systems-level work
Š For better or for worse, it retains its C heritage, so it
offers good access to underlying machine concepts
Š Broad support for various programming styles
Š Interfaces well with existing libraries and legacy
• However, it is a non-trivial language
Š High degree of complexity to master
Š Most programmers working with it can't

XP Day 06 4
C++ Systems

• C++ is well suited to work that requires the


mixing of machine-level and other abstractions
Š E.g. embedded systems, real-time systems, high-
performance computing, systems programming,
games, and many domains where C is also favoured
• In other domains, C++ is more often than not
let down by the quality of libraries
Š E.g. GUI programming, text processing, file
management and manipulation, database usage

XP Day 06 5

Legacy: the Other Kind of Inheritance

• In part, because of the age of the language,


many C++ systems are classified as legacy
Š They are large, untestable, use divergent coding
styles and are based on an older, less capable subset
of the language
• The technical debt is often large, matched only
by a corresponding fear of change
Š Which leads to a conservative approach to change
that accumulates further technical debt

XP Day 06 6
Agility and C++

• Agility is not a binary option based on


programming language
Š An agile process is as achievable on C++ projects as
with any other language — many developers can
testify to this
• The challenge is often in the culture and with
certain technical details
Š Many C++ developers favour (or blindly use)
techniques that are antagonistic to keeping a code
base supple and testable
XP Day 06 7

Culture Shock

Design and programming are human activities; forget that and all is lost.
Bjarne Stroustrup

XP Day 06 8
Culture Can Decelerate Change

• Culture is perhaps the greatest challenge to


undertaking agile development in C++
Š Although there are different C++ subcultures, many
share a certain conservatism, fear of change and
assumptions about what is possible and what is not
• Assumptions go beyond the code face to the
process model
Š Sequential or chaotic development often ingrained
Š Open source projects offer a more positive model,
as does classic Unix culture and mindset
XP Day 06 9

Some Common (Problem) Attitudes

• Cleverness is valued
Š Template metaprogramming, preprocessor abuse,
cunning bit twiddling, micro-optimisation, etc
• BUFD or 'design' by wizard
Š Design horizons are typically distant or overlooked
• Testing is somebody else's problem
Š Debugging is seen as the typical response to defects
• Maintenance does not involve refactoring
Š Automated refactoring tools are not widespread
XP Day 06 10
Some Possibilities for Change

• Agile macro process with daily heartbeat


Š An iterative development lifecycle based on
feedback at different time scales, incorporating an at
least daily full integration build
• Testing and, ideally, TDD
Š No shortage of testing frameworks and wisdom
• Refactoring and loosely coupled design
Š Lack of good refactoring tools is partly down to
language and partly culture, but a lot of refactoring
is related to skill and attitude
XP Day 06 11

Accidental Complexity

Increasingly, people seem to misinterpret complexity as sophistication, which is


baffling — the incomprehensible should cause suspicion rather than admiration.
Possibly this trend results from a mistaken belief that using a somewhat mysterious
device confers an aura of power on the user.
Niklaus Wirth

XP Day 06 12
Complexity Can Decelerate Change

• Insufficient mastery of the language, libraries


and associated techniques leads to complexity
Š Accidental complexity on C++ projects can be very
high, and the nature of legacy and lack of a
refactoring culture makes this complexity breed
• Complexity also arises from blind use of tools
and following various dysfunctional memes
Š Most wizards are more like "sorcerer's apprentices"
Š Many libraries lead (well, trail) by poor example

XP Day 06 13

Criticality of Knowledge

• However good any other attribute of a project


is, skill is still critical to success
Š Knowledge of tools, knowledge of practice, etc
• Getting the most out of C++ requires profound
skill — knowledge of the language and beyond
Š The complexity of C++ introduces a higher barrier
to entry than many languages — fall short of it, and
you will hit the barrier... hard
Š And beyond the barrier to entry, greater mastery is
needed than is perhaps true of other languages
XP Day 06 14
Insufficient Knowledge
class access_control
{
public:
bool is_locked(const std::basic_string<char> &key) const
{
std::list<std::basic_string<char> >::const_iterator found = std::find(locked.begin(), locked.end(), key);
return found != locked.end();
}
bool lock(const std::basic_string<char> &key)
{
std::list<std::basic_string<char> >::iterator found = std::find(locked.begin(), locked.end(), key);
if(found == locked.end())
{
locked.insert(locked.end(), key);
return true;
}
return false;
}
bool unlock(const std::basic_string<char> &key)
{
std::list<std::basic_string<char> >::iterator found = std::find(locked.begin(), locked.end(), key);
if(found != locked.end())
{
locked.erase(found);
return true;
}
return false;
}
...
private:
std::list<std::basic_string<char> > locked;
...
};

XP Day 06 15

Sufficient Knowledge
class access_control
{
public:
bool is_locked(const std::string &key) const
{
return locked.count(key) != 0;
}
bool lock(const std::string &key)
{
return locked.insert(key).second;
}
bool unlock(const std::string &key)
{
return locked.erase(key);
}
...
private:
std::set<std::string> locked;
...
};

XP Day 06 16
The Bad, the Ugly, and the Good

class handle class handle class handle


{ { {
public: public: public:
handle &operator=(const handle &rhs) handle &operator=(const handle &rhs) handle &operator=(const handle &rhs)
{ { {
if(this != &rhs) if(this != &rhs) handle copy(rhs);
{ { std::swap(body, copy.body);
delete body; representation *old_body = body; return *this;
body = new representation(*rhs.body); try }
} { handle(const handle &);
return *this; body = new representation(*rhs.body); ~handle();
} delete old_body; ...
private:
handle(const handle &); }
class representation
~handle(); catch(...)
... {
{ ...
private: body = old_body; };
class representation throw; representation *body;
{ }
... };
}; }
representation *body; return *this;
}; }
handle(const handle &);
~handle();
...
private:
class representation
{
...
};
representation *body;
};

XP Day 06 17

Working with Power Tools

• C++ is a very powerful and capable language


Š Full mastery makes it a complete and capable tool,
able to address certain classes of problem and
certain styles of expression easily and sustainably
• The other source of complexity is 'cleverness'
Š Because of the barrier to entry and the capabilities
of the language, cleverness is valued
Š Code by über-programmers is often based on
speculative generality and gratuitous use of
advanced techniques and language features
XP Day 06 18
With Great Power...

#ifndef BOOST_LEXICAL_CAST_INCLUDED typename stringstream<CharType>::type interpreter(arg); &>::do_cast(arg); // mpl::apply_if doesn't work well for MSVC 6 here, and neither does
#define BOOST_LEXICAL_CAST_INCLUDED #endif } // inheriting from a metafunction

// Boost lexical_cast.hpp header -------------------------------------------// setup_interpreter<Target, Source>(interpreter); template<typename CharType, typename CharTraits, typename Allocator, typename Source> template<class Target, class Source>
// inline std::basic_string<CharType, CharTraits, Allocator> struct select_base
// See http://www.boost.org for most recent version including documentation. Target result; lexical_cast_impl(std::basic_string<CharType, CharTraits, Allocator> *, Source arg) {
// { typedef typename mpl::if_<is_same<Target, char>, // Target==char?
// what: lexical_cast custom keyword cast if(!(interpreter >> result) || !(interpreter >> std::ws).eof()) return any_to_string_base<std::basic_string<CharType, CharTraits, Allocator>, Source, typename mpl::if_<is_same<Source, char>, // Source==char?
// who: contributed by Kevlin Henney and enhanced by Terje Slettebø throw detail::no_lexical_conversion<Target, Source>(); CharType>::do_cast(arg); direct_cast_base<Target, Source>,
// when: November 2000, March 2003 } typename mpl::if_<is_same<Source, char *>, // Source==char *?
return result; pointer_to_char_to_char_base<Target, Source>,
#include <typeinfo> } template<typename CharType, typename CharTraits, typename Allocator> typename mpl::if_<is_same<Source, const char *>, // Source==const char *?
#include <string> }; inline std::basic_string<CharType, CharTraits, Allocator> pointer_to_char_to_char_base<Target, Source>,
#include <boost/config.hpp> lexical_cast_impl(std::basic_string<CharType, CharTraits, Allocator> *, CharType arg) typename mpl::if_<is_same<Source, std::string>, // Source==std::string?
#include <boost/limits.hpp> ////////////////////////////////////////////////////////////////////////// { string_to_char_base<Target, const Source &>,
#include <boost/static_assert.hpp> // any_to_string_base return char_to_string_base<std::basic_string<CharType, CharTraits, Allocator>, CharType>::do_cast(arg); lexical_cast_base<Target, Source, char>
////////////////////////////////////////////////////////////////////////// } >::type
#ifdef BOOST_NO_STRINGSTREAM >::type
#include <strstream> template<typename Target, typename Source, typename CharType> template<typename CharType, typename CharTraits, typename Allocator> >::type
#else struct any_to_string_base inline std::basic_string<CharType, CharTraits, Allocator> >::type,
#include <sstream> { lexical_cast_impl(std::basic_string<CharType, CharTraits, Allocator> *, CharType *arg) typename mpl::if_<is_same<Target, std::string>, // Target==std::string?
#endif static Target do_cast(Source arg) { typename mpl::if_<is_same<Source, char>, // Source==char?
{ return direct_cast_base<std::basic_string<CharType, CharTraits, Allocator>, CharType *>::do_cast(arg); char_to_string_base<Target, Source>,
#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) || \ typename stringstream<CharType>::type interpreter; } typename mpl::if_<is_same<Source, char *>, // Source==char *?
defined(BOOST_BCB_PARTIAL_SPECIALIZATION_BUG) setup_interpreter<Target, Source>(interpreter); direct_cast_base<Target, Source>,
#include <boost/mpl/if.hpp> if(!(interpreter << arg)) template<typename CharType, typename CharTraits, typename Allocator> typename mpl::if_<is_same<Source, const char *>, // Source==const char *?
#include <boost/type_traits/same_traits.hpp> throw detail::no_lexical_conversion<Target, Source>(); inline std::basic_string<CharType, CharTraits, Allocator> direct_cast_base<Target, Source>,
#endif return interpreter.str(); lexical_cast_impl(std::basic_string<CharType, CharTraits, Allocator> *, const CharType *arg) typename mpl::if_<is_same<Source, std::string>, // Source==std::string?
} { direct_cast_base<Target, const Source &>,
namespace boost }; return direct_cast_base<std::basic_string<CharType, CharTraits, Allocator>, const CharType any_to_string_base<Target, Source, char>
{ *>::do_cast(arg); >::type
// exception used to indicate runtime lexical_cast failure template<typename Target, typename Source> } >::type
class bad_lexical_cast : public std::bad_cast struct string_to_char_base >::type
{ { template<typename Target> >::type,
public: static Target do_cast(Source arg) inline Target lexical_cast_impl(Target *, char *arg) typename mpl::if_<is_same<Target, wchar_t>, // Target==wchar_t?
virtual ~bad_lexical_cast() throw() { { typename mpl::if_<is_same<Source, wchar_t>, // Source==wchar_t?
{ if(arg.length()!=1) return string_to_any_base<Target, char *, char>::do_cast(arg); direct_cast_base<Target, Source>,
} throw detail::no_lexical_conversion<Target, Source>(); } typename mpl::if_<is_same<Source, wchar_t *>, // Source==wchar_t *?
}; return arg[0]; pointer_to_char_to_char_base<Target, Source>,
} template<typename Target> typename mpl::if_<is_same<Source, const wchar_t *>, // Source==const wchar_t *?
namespace detail }; inline Target lexical_cast_impl(Target *, const char *arg) pointer_to_char_to_char_base<Target, Source>,
{ { typename mpl::if_<is_same<Source, std::wstring>, // Source==std::wstring?
template<typename Target, typename Source> template<typename Target, typename Source> return string_to_any_base<Target, const char *, char>::do_cast(arg); string_to_char_base<Target, const Source &>,
class no_lexical_conversion : public bad_lexical_cast struct pointer_to_char_to_char_base } lexical_cast_base<Target, Source, wchar_t>
{ { >::type
public: static Target do_cast(Source arg) template<typename Target> >::type
no_lexical_conversion() { inline Target lexical_cast_impl(Target *, wchar_t arg) >::type
: description( if(arg[0] == Target() || arg[1] != Target()) { >::type,
std::string() + "bad lexical cast: " + throw detail::no_lexical_conversion<Target, Source>(); return lexical_cast_base<Target, wchar_t, wchar_t>::do_cast(arg); typename mpl::if_<is_same<Target, std::wstring>, // Target==std::wstring?
"source type value could not be interpreted as target, Target=" + return arg[0]; } typename mpl::if_<is_same<Source, wchar_t>, // Source==wchar_t?
typeid(Target).name() + ", Source=" + typeid(Source).name()) } char_to_string_base<Target, Source>,
{ }; template<typename Target> typename mpl::if_<is_same<Source, wchar_t *>, // Source==wchar_t *?
} inline Target lexical_cast_impl(Target *, wchar_t *arg) direct_cast_base<Target, Source>,
virtual ~no_lexical_conversion() throw() template<typename Target, typename Source> { typename mpl::if_<is_same<Source, const wchar_t *>, // Source==const wchar_t *?
{ struct char_to_string_base return string_to_any_base<Target, wchar_t *, wchar_t>::do_cast(arg); direct_cast_base<Target, Source>,
} { } typename mpl::if_<is_same<Source, std::wstring>, // Source==std::wstring?
virtual const char *what() const throw() static Target do_cast(Source arg) direct_cast_base<Target, const Source &>,
{ { template<typename Target> any_to_string_base<Target, Source, wchar_t>
return description.c_str(); return Target(1, arg); inline Target lexical_cast_impl(Target *, const wchar_t *arg) >::type
} } { >::type
private: }; return string_to_any_base<Target, const wchar_t *, wchar_t>::do_cast(arg); >::type
const std::string description; // static initialization fails on MSVC6 } >::type,
}; template<typename Target, typename Source> typename mpl::if_<is_same<Target, Source>, // Target==Source?
} struct direct_cast_base template<typename Target, typename CharType, typename CharTraits, typename Allocator> direct_cast_base<Target, const Source &>,
{ inline Target lexical_cast_impl(Target *, const std::basic_string<CharType, CharTraits, Allocator> &arg) typename mpl::if_<is_same<Source, char *>, // Source==char *?
namespace detail static Target do_cast(Source arg) { string_to_any_base<Target, Source, char>,
{ { return string_to_any_base<Target, typename mpl::if_<is_same<Source, const char *>, // Source==const
template<typename CharType> return arg; const std::basic_string<CharType, CharTraits, Allocator> &, CharType>::do_cast(arg); char *?
struct stringstream } } string_to_any_base<Target, Source, char>,
{ }; typename mpl::if_<is_same<Source, std::string>, //
#if defined(BOOST_NO_STRINGSTREAM) template<typename Type> Source==std::string?
typedef std::strstream type; #if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && \ inline Type lexical_cast_impl(Type *, const Type &arg) string_to_any_base<Target, const Source &, char>,
#else !defined(BOOST_BCB_PARTIAL_SPECIALIZATION_BUG) { typename mpl::if_<is_same<Source, wchar_t>, // Source==wchar_t?
typedef std::basic_stringstream<CharType> type; return direct_cast_base<Type, const Type &>::do_cast(arg); lexical_cast_base<Target, Source, wchar_t>,
#endif template<typename Target, typename Source> } typename mpl::if_<is_same<Source, wchar_t *>, // Source==wchar_t
}; inline Target lexical_cast_impl(Target *, Source arg) *?
{ inline std::string lexical_cast_impl(std::string *, char *arg) lexical_cast_base<Target, Source, wchar_t>,
template<typename Target, typename Source, typename Interpreter> return lexical_cast_base<Target, Source, char>::do_cast(arg); { typename mpl::if_<is_same<Source, const wchar_t *>, // Source==const
void setup_interpreter(Interpreter &interpreter) } return direct_cast_base<std::string, char *>::do_cast(arg); wchar_t *?
{ } lexical_cast_base<Target, Source, wchar_t>,
interpreter.unsetf(std::ios_base::skipws); template<typename Target, typename Source> typename mpl::if_<is_same<Source, std::wstring>, //
void lexical_cast_impl(Target **,Source) inline std::string lexical_cast_impl(std::string *, const char *arg) Source==std::wstring?
if(std::numeric_limits<Target>::is_specialized) { { lexical_cast_base<Target, const Source &, wchar_t>,
interpreter.precision(std::numeric_limits<Target>::digits10 + 1); BOOST_STATIC_ASSERT((Target **) 0); // Pointer as target type not supported return direct_cast_base<std::string, const char *>::do_cast(arg); lexical_cast_base<Target, Source, char>
else if(std::numeric_limits<Source>::is_specialized) } } >::type
interpreter.precision(std::numeric_limits<Source>::digits10 + 1); >::type
} inline char lexical_cast_impl(char *, char *arg) inline std::string lexical_cast_impl(std::string *, const std::string &arg) >::type
{ { >::type
template<typename Target, typename Source, typename CharType> return pointer_to_char_to_char_base<char, char *>::do_cast(arg); return direct_cast_base<std::string, const std::string &>::do_cast(arg); >::type
struct lexical_cast_base } } >::type
{ >::type
static Target do_cast(Source arg) inline char lexical_cast_impl(char *, const char *arg) inline std::wstring lexical_cast_impl(std::wstring *, wchar_t *arg) >::type
{ { { >::type
typename stringstream<CharType>::type interpreter; return pointer_to_char_to_char_base<char, const char *>::do_cast(arg); return direct_cast_base<std::wstring, wchar_t *>::do_cast(arg); >::type
} } >::type
setup_interpreter<Target, Source>(interpreter); >::type type;
template<typename Source> inline std::wstring lexical_cast_impl(std::wstring *, const wchar_t *arg) };
Target result; inline wchar_t lexical_cast_impl(wchar_t *, Source arg) {
{ return direct_cast_base<std::wstring, const wchar_t *>::do_cast(arg);
if(!(interpreter << arg) || !(interpreter >> result) || return lexical_cast_base<wchar_t, Source, wchar_t>::do_cast(arg); } template<typename Target, typename Source>
!(interpreter >> std::ws).eof()) } inline Target lexical_cast_impl(Target *, Source arg)
throw detail::no_lexical_conversion<Target, Source>(); inline std::wstring lexical_cast_impl(std::wstring *, const std::wstring &arg) {
inline wchar_t lexical_cast_impl(wchar_t *, wchar_t *arg) { return select_base<Target, Source>::type::do_cast(arg);
return result; { return direct_cast_base<std::wstring, const std::wstring &>::do_cast(arg); }
} return pointer_to_char_to_char_base<wchar_t, wchar_t *>::do_cast(arg); } #endif
}; } }
inline wchar_t lexical_cast_impl(wchar_t *, wchar_t arg)
////////////////////////////////////////////////////////////////////////// inline wchar_t lexical_cast_impl(wchar_t *, const wchar_t *arg) { template<typename Target, typename Source>
// string_to_any_base { return direct_cast_base<wchar_t, wchar_t>::do_cast(arg); inline Target lexical_cast(const Source &arg)
////////////////////////////////////////////////////////////////////////// return pointer_to_char_to_char_base<wchar_t, const wchar_t *>::do_cast(arg); } {
} return detail::lexical_cast_impl(static_cast<Target *>(0), arg);
template<typename Target, typename Source, typename CharType> inline std::wstring lexical_cast_impl(std::wstring *, wchar_t arg) }
struct string_to_any_base inline wchar_t lexical_cast_impl(wchar_t *, const std::wstring &arg) { }
{ { return char_to_string_base<std::wstring, wchar_t>::do_cast(arg);
static Target do_cast(Source arg) return string_to_char_base<wchar_t, const std::wstring &>::do_cast(arg); } // Copyright Kevlin Henney, 2000-2003, Terje Slettebø, 2003. All rights reserved.
{ } //
#ifdef BOOST_NO_STRINGSTREAM #else // Permission to use, copy, modify, and distribute this software for any
typename stringstream<CharType>::type interpreter; template<typename CharType, typename CharTraits, typename Allocator> // purpose is hereby granted without fee, provided that this copyright and
inline CharType // Simulated partial specialisation // permissions notice appear in all copies and derivatives.
if(!(interpreter << arg)) lexical_cast_impl(CharType *, const std::basic_string<CharType, CharTraits, Allocator> &arg) // select_base //
throw detail::no_lexical_conversion<Target, Source>(); { ////////////////////////////////////////////////////////////////////////// // This software is provided "as is" without express or implied warranty.
#else return string_to_char_base<CharType, const std::basic_string<CharType, CharTraits, Allocator> #endif

XP Day 06 19

Comes Great Responsibility

#ifndef BOOST_LEXICAL_CAST_INCLUDED struct stream_char<std::wstring>


#define BOOST_LEXICAL_CAST_INCLUDED {
typedef wchar_t type;
// Boost lexical_cast.hpp header -------------------------------------------// };
// #endif
// See http://www.boost.org for most recent version including documentation.
// See end of this header for rights and permissions. template<typename TargetChar, typename SourceChar>
// struct widest_char
// what: lexical_cast custom keyword cast {
// who: contributed by Kevlin Henney, typedef TargetChar type;
// enhanced with contributions from Terje Slettebø, };
// with additional fixes and suggestions from Gennaro Prota,
// Beman Dawes, Dave Abrahams, Daryle Walker, Peter Dimov, template<>
// and other Boosters struct widest_char<char, wchar_t>
// when: November 2000, March 2003 {
typedef wchar_t type;
#include <string> };
#include <typeinfo> }
#include <boost/config.hpp>
#include <boost/limits.hpp> namespace detail // stream wrapper for handling lexical conversions
#include <boost/type_traits/is_pointer.hpp> {
template<typename Target, typename Source>
#ifdef BOOST_NO_STRINGSTREAM class lexical_stream
#include <strstream> {
#else public:
#include <sstream> lexical_stream()
#endif {
stream.unsetf(std::ios::skipws);
#if defined(BOOST_NO_STRINGSTREAM) || \
defined(BOOST_NO_STD_WSTRING) || \ if(std::numeric_limits<Target>::is_specialized)
defined(BOOST_NO_STD_LOCALE) || \ stream.precision(std::numeric_limits<Target>::digits10 + 1);
defined(BOOST_NO_CWCHAR) else if(std::numeric_limits<Source>::is_specialized)
#define DISABLE_WIDE_CHAR_SUPPORT stream.precision(std::numeric_limits<Source>::digits10 + 1);
#endif }
~lexical_stream()
#ifdef BOOST_NO_INTRINSIC_WCHAR_T {
#include <cwchar> #if defined(BOOST_NO_STRINGSTREAM)
#endif stream.freeze(false);
#endif
namespace boost }
{ bool operator<<(const Source &input)
// exception used to indicate runtime lexical_cast failure {
class bad_lexical_cast : public std::bad_cast return stream << input;
{ }
public: template<typename InputStreamable>
virtual ~bad_lexical_cast() throw() bool operator>>(InputStreamable &output)
{ {
} return !is_pointer<InputStreamable>::value &&
}; stream >> output &&
(stream >> std::ws).eof();
namespace detail // actual underlying concrete exception type }
{ bool operator>>(std::string &output)
template<typename Target, typename Source> {
class no_lexical_conversion : public bad_lexical_cast #if defined(BOOST_NO_STRINGSTREAM)
{ stream << '\0';
public: #endif
no_lexical_conversion() output = stream.str();
: description( return true;
std::string() + "bad lexical cast: " + }
"source type value could not be interpreted as target, Target=" + #ifndef DISABLE_WIDE_CHAR_SUPPORT
typeid(Target).name() + ", Source=" + typeid(Source).name()) bool operator>>(std::wstring &output)
{ {
} output = stream.str();
virtual ~no_lexical_conversion() throw() return true;
{ }
} #endif
virtual const char *what() const throw() private:
{ typedef typename widest_char<
return description.c_str(); typename stream_char<Target>::type,
} typename stream_char<Source>::type>::type char_type;
private:
const std::string description; // static initialization fails on MSVC6 #if defined(BOOST_NO_STRINGSTREAM)
}; std::strstream stream;
} #elif defined(BOOST_NO_STD_LOCALE)
std::stringstream stream;
namespace detail // selectors for choosing stream character type #else
{ std::basic_stringstream<char_type> stream;
template<typename Type> #endif
struct stream_char };
{ }
typedef char type;
}; template<typename Target, typename Source>
Target lexical_cast(Source arg)
#ifndef DISABLE_WIDE_CHAR_SUPPORT {
template<> detail::lexical_stream<Target, Source> interpreter;
struct stream_char<wchar_t> Target result;
{
typedef wchar_t type; if(!(interpreter << arg && interpreter >> result))
}; throw detail::no_lexical_conversion<Target, Source>();
return result;
template<> }
struct stream_char<wchar_t *> }
{
typedef wchar_t type; // Copyright Kevlin Henney, 2000-2003. All rights reserved.
}; //
// Permission to use, copy, modify, and distribute this software for any
template<> // purpose is hereby granted without fee, provided that this copyright and
struct stream_char<const wchar_t *> // permissions notice appear in all copies and derivatives.
{ //
typedef wchar_t type; // This software is provided "as is" without express or implied warranty.
};
#undef DISABLE_WIDE_CHAR_SUPPORT
template<> #endif

XP Day 06 20
Dependency Management

Express independent ideas independently.


Bjarne Stroustrup

XP Day 06 21

Coupling Can Decelerate Change

• Dependency management is critical to agility


Š This is true in general... just more so in C++
Š Header file inclusion, visibility of private data,
templates and inlines, separation of function
declaration and definition, etc, all introduce
additional (but not insurmountable) challenges
• Dependency management should be pursued
aggressively — even pathologically
Š Dependencies have many effects — longer build
and development times, shorter lunch times
XP Day 06 22
Single-Responsibility Headers

• When it comes to partitioning code across


headers, many projects are pretty good
Š Clearly separated subsystems, single-responsibility
headers with corresponding .cpp files
• But many are pretty poor
Š Arbitrary partitioning (e.g. all constants bundled
together in the same header)
Š No partitioning (Visual C++ has a lot to answer for)
Š Fragmented partitioning (e.g. a separate header for
each nested class)
XP Day 06 23

Lightweight Headers

• Headers also need to be lightweight


Š Avoid defining inlines unless strictly necessary
Š Be judicious with definition of templates — but this
does not mean avoid templates
Š Use forward declarations where possible, both
instead of headers and to indirect private
representation (à la Cheshire Cat or Pimpl)
Š Push as much as possible into the .cpp file, e.g. use
of anonymous namespaces rather than classes for
holding and defining private static constants

XP Day 06 24
Inheritance Tax

• Inheritance is the strongest form of coupling in


a C++ class system
Š In spite of much advice to reduce coupling, cut
dependencies and use delegation, many current
coding recommendations do the opposite
Š Class hierarchies can become so jammed with
purpose that they lead to (develop)mental gridlock
• Fortunately, rearranging the type system to
introduce more 'space' is compiler checkable
Š The absence of tests need not be a problem
XP Day 06 25

Interface Classes

• Fully abstract classes offer the simplest


technique for fully isolating dependencies
Š Isolate both internal and external dependencies
Š Allows use of Mock Objects rather than
preprocessor hacks for testing
Š Simplifies evolution and response to change
• The use of interface classes needs to be
uncompromising
Š No implementation means no implementation

XP Day 06 26
Memetic Engineering

Do not use Hungarian notation.


http://msdn2.microsoft.com/en-us/library/ms229045.aspx

XP Day 06 27

Bad Memes Can Decelerate Change

• There are many unquestioned design styles


and ingrained habits that cause problems
Š E.g. Hungarian Notation reduces readability,
distracts from the root problem (e.g. long functions)
and adds friction to refactoring
Š E.g. public data is still surprisingly popular — and
still has a habit of needing to be changed, even
when all concerned were sure it never would
• There are many other (counter)examples,
including NVI and Singleton
XP Day 06 28
Non-Valuable Idiom

• Some have proposed Non-Virtual Interfaces


(NVI) as a good practice guideline
Š NVI suggests virtuals should be private and
wrapped in public non-virtuals
Š It has structural similarities with Template Method,
but has a distinct form and (in)distinct motivation
• NVI lacks clear benefits and sound motivation
Š Based on speculative generality and does not solve
a particular problem well — the supposed need for
it normally is normally a sign of a deeper problem
XP Day 06 29

Decluttering
class command class command
{ {
public: public:
void execute() virtual void execute() = 0;
{ virtual void rollback() = 0;
do_execute(); virtual ~command();
} };
void rollback()
{
class command_processor
do_rollback();
{
}
public:
virtual ~command();
void execute(command *);
private:
void rollback();
virtual void do_execute() = 0; ...
virtual void do_rollback() = 0; };
};

class command_processor
{
public:
void execute(command *);
void rollback();
...
};

XP Day 06 30
Deglobalisation

• Singleton is a popular technique — and classic


symptom of design problems
Š Similar statements are true of the Monostate pattern
• Modifiable static state makes evolution and
testing unnecessarily challenging
Š Much static state implies a missing Manager object
Š Context-related globals are better off passed as
Context Objects and global services should follow a
plug-in rather than a hardwired style, e.g. Strategy

XP Day 06 31

In Closing

The only thing to do with good advice is to pass it on. It is never any use to oneself.
Oscar Wilde

XP Day 06 32
Taming Complexity

• Part of C++'s challenge is in understanding its


(many varied and subtle) mechanisms...
Š Of which there are more than enough to distract or
detract from other programming goals
• And in part in finding the simplifying
assumptions and styles that marshal them
Š It is these that afford responsiveness to change
• But keep in mind that not all impediments to
change and agility are to be found in the code
XP Day 06 33

Vous aimerez peut-être aussi