Topic : A Critique of C++
Author : Ian Joyner
Page : << Previous 10  Next >>
Go to page :


requirements of an enterprise can change much more rapidly
than programmers can keep up, especially in a highly competitive and
commercial world.

   So how does C++ fit into this picture? Well it is based on C that was
designed mainly as an implementation and machine-oriented language.  It
is an old language, that did not need to consider the integrated
lifecycle approach.  C++ might have some of the trappings of
object-oriented concepts, but it is the marriage of a problem- oriented
technique with a machine-oriented language.  It addresses
implementation, but not so well the other aspects of the software
lifecycle.  Since C++ is not so well integrated with analysis and
design, the transformation required to go from analysis and design to
implementation is costly.  The semantic gap between design languages and
the implementation language is great.

   We should have learnt from the structured world that this is the
incorrect approach to the software lifecycle.  But in the OO world we
are again falling into the trap of dividing the lifecycle into
artificially distinct activities of OOA, OOD and OOP, instead of
adopting an integrated approach to these.  Modern languages provide a
much more integrated approach to the complete software development
process than C++.  C++ supports classes and inheritance and other
concepts of object-orientation, but fails to address the entire software
lifecycle.

  

3.27.  Reusability and Communication

   Reusability is a matter of communication.

   In order to use a software component, you must be able to understand
it.  The writer must communicate the purpose, intent, and correct usage
of the component to the client.  In the object-oriented world, clear and
concise definition of software modules is not a mere nicety, but
essential for reusability.  Arising out of the issue of reusability is
extendibility.  In order to maximise the reuse of software, it often
must be tailored for new applications.  The client programmer must
decide whether the software component is suitable for the new task.  If
so, what is the best way to extend it? Clear communication to clients is
a courtesy concern.

  

3.28.  Reusability and Trust

   Reusability is a matter of trust.

   Trust results from confidence that safety concerns have been met.  If
you do not have confidence in a software component, then it is difficult
to consider it for reuse.  There could be doubt that the software
component provides enough functionality, or correct functionality.
There could be doubt that the component is efficient enough, or worse it
might crash.  The C/C++ philosophy of not building checks into the
language and compiler because programmers can be trusted, works against
trust and reusability.

   In the real world of reusability, the ideal of trusting programmers
is inappropriate.  Trusting programmers results in less trustworthy
software.  In reality, customers doubt the claims of suppliers.  It is
the onus of the supplier to prove their claims, and thus trustworthiness
of the software.  The client is not required to trust the supplier's
programmers.  Potential clients of a software component, require
assurance that the component is trustworthy.  Trusting programmers is
against the commercial interest of both parties.  This is not to cast
dispersion on programmers, but merely recognises that computers are good
at performing mundane tasks and checks, but people are not.  If people
were good at such things, we would not need computers in the first
place.  Building trustworthy components is a safety concern.

  

3.29.  Reusability and Compatibility

   Different compiler implementations need to be compatible in order to
realise reusability between components.  Different C++ compilers
generate different class layouts, virtual function calling techniques,
etc.  The name encoding schemes used for type safe linkage can also be
different.  If two different compilers generate different run-time
organisations, then different name encodings are desirable as it will
prevent two incompatible libraries from being linked.  The C++ ARM
(p122) states "If two C++ implementations for the same system use
different calling sequences or in other ways are not link compatible it
would be unwise to use identical encodings of type signatures."

   This can be solved in two ways.  Firstly, a library vendor could
provide the entire source of a library so it can be compiled by the
customers compiler.  This is not satisfactory if the sources are
proprietary.  Then the vendor will need a separate release for every
environment, and every compiler in that environment.

   Because of this problem a strong case exists for a universal
intermediate machine readable representation of programs.
Interestingly, some systems are already using C as a 'universal
assembler', notably AT&T C++ and Eiffel.  But this cannot solve the
above problems of compatibility between components without a
standardisation effort on run time layouts and name encoding schemes.

  

3.30.  Reusability and Portability

   Since true OOP ensures that objects are loosely coupled to the
external environment, portability to diverse environments is possible.
C is tightly coupled to the Unix environment, and as such is not
particularly portable to diverse environments.

  

3.31.  Idiomatic Programming

   The ability to program in different idioms is argued as a strength of
C++.  Idiomatic programming, however, is a weak form of paradigmatic
programming.  It is programming in a paradigm without necessarily having
compiler support for that paradigm.  The compiler cannot check for
inconsistencies with the idiom, or paradigm.  Defines can often be used
to invent idioms.  Anyone who has attempted to do object-oriented
programming in a conventional language using defines will realise that
it is impossible to realise all the benefits easily, if at all, without
compiler support.

  

3.32. Concurrent Programming

   In the next ten years multiple processor arrays that execute programs
concurrently will probably become common.  Concurrency requires much
cleaner languages, than the single processor languages of today.
Object-oriented concepts support concurrent programming.  Objects can
execute state changing code independently of each other.  Concurrent
programming will be enabled by the division of the state space of a
system into modules to achieve a high degree of independent processing.
Objects provide a scheme to cleanly divide state spaces.  The demand
that everything be broken down into loosely coupled modules, that only
interact through well defined interfaces might be perceived as
inefficient.  But it is precisely this scheme that will mean that
concurrent solutions can be developed efficiently and transparently to
the programmer.  Concurrency should be transparent to the programmer, as
concurrency is a low level implementation consideration.  That is
concurrency is how a computation is done, not what is to be computed.
The programmer should be concerned with what is to be computed, not how.
How something is computed is the concern of the target environment, ie
the compilers, operating system, and hardware.  When programmers are not
concerned with this level, efficiency and portability follow
automatically.

   The aim of concurrent processing is to keep all the processors in a
processor array as fully utilised as possible, so that processor
resources are not wasted.  This is as good as can be expected.  There is
nothing more mysterious to concurrent programming than the efficient use
of resources.  Keeping all processors busy is an inherently dynamic
problem, which the programmer cannot determine statically at compile
time.  All the processors can be kept busy, as long as there are enough
threads in the system.

   In concurrent programming, a thread is a unit of sequential
execution.  Concurrency is achieved by the splitting of threads.  A
thread can be split when a state changing routine is invoked, but not a
value returning function, because it must wait for the value.  State
changing routines can easily be invoked on another processor.  Object
level granularity seems to be a natural candidate for concurrent
processing.  An object can have only one update thread at a time to
avoid simultaneous update problems.  Other levels of concurrency are
instruction level, and task or process level.  Task or process level is
the level used in conventional multi-processing systems currently
commercially produced, and instruction level is quite difficult, best
being left to instruction pipelines.

   Object level is natural for the programmer, and has the advantage
that a programmer can implement a system without taking into account
parallel processing at all.  The same program will run and produce
identical results irrespective of whether the customer is running a
single processor, or a processor array.

   Side effects must be avoided in concurrent systems.  Suppose a
computation depends on combining the results of two functions f and g,
such as f + g.  If f and g are independent, then they can be computed
concurrently.  If however, f produces side effects that g depends on,
they must be computed sequentially.  F and g are parameters to the +
function.  Routine parameters can be computed concurrently, as long as
the computation of each causes no side effects.  Side effects are
avoided by restrictive practices that C devotees would object to.

   C++ does not preclude the use of a global environment.  Access to
shared global data potentially causes a thread to lock, and if many such
accesses occur, the advantage of concurrency is lost.  This is because
updates to a global

Page : << Previous 10  Next >>