473,408 Members | 2,405 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,408 software developers and data experts.

Good Design in C: Encapsulation

Ahoy:

For as long as I've been using C, I've vacillated on the optimal
degree of encapsulation in my designs. At a minimum, I aggregate data
and code that operate on that data into classlike files; but now and
then I go on an opaque type joyride, and create minimalist header
files that define very clean interfaces.

The problem with that is that it prevents some optimizations:

* Can no longer allocate structures on the stack;
* Can no longer put inline functions in the header file;
* Incurs the overhead of a function call for any data-access.

I'm curious as to how C experts feel about this design concept;
reviewing some old (very old) threads about encapsulation, I see that
many dismiss it as though it were a tenet of some foolish philosophy;
and yet, encapsulation has all sorts of benefits to the author of
libraries in terms of the ability to extend, adapt, & refactor code;
it has also helped me in my own designs by illuminating design
mistakes before they get very far.

Mar 12 '07 #1
32 4159
bluejack wrote:
Ahoy:

For as long as I've been using C, I've vacillated on the optimal
degree of encapsulation in my designs. At a minimum, I aggregate data
and code that operate on that data into classlike files; but now and
then I go on an opaque type joyride, and create minimalist header
files that define very clean interfaces.

The problem with that is that it prevents some optimizations:

* Can no longer allocate structures on the stack;
* Can no longer put inline functions in the header file;
* Incurs the overhead of a function call for any data-access.

I'm curious as to how C experts feel about this design concept;
reviewing some old (very old) threads about encapsulation, I see that
many dismiss it as though it were a tenet of some foolish philosophy;
and yet, encapsulation has all sorts of benefits to the author of
libraries in terms of the ability to extend, adapt, & refactor code;
it has also helped me in my own designs by illuminating design
mistakes before they get very far.
Well I'm an opaque type and encapsulation fan. The last three largish C
projects I worked on used the idiom throughout, with each module having
its own subdirectory with the public headers and nested within that a
subdirectory for private headers and source files.

The main benefits I see are in developer productivity and reduced
dependencies (with tends to improve productivity). Being able to change
core structures without forcing a recompile is a big plus, not so much
on modern systems with small to medium projects, but definitely big
projects and on older tool chains that don't support parallel or
distributed building.

There is a small runtime price to pay, but it has always been one I have
been happy to pay.

By the way, you can avoid dynamic allocation of structures by using
static variables.

--
Ian Collins.
Mar 12 '07 #2
bluejack said:

<snip>
but now and
then I go on an opaque type joyride, and create minimalist header
files that define very clean interfaces.

The problem with that is that it prevents some optimizations:

* Can no longer allocate structures on the stack;
* Can no longer put inline functions in the header file;
* Incurs the overhead of a function call for any data-access.

I'm curious as to how C experts feel about this design concept;
Ambivalent, which is why I tend to do this in the public header:

#if ABANDON_OPACITY_AND_DAMN_THE_CONSEQUENCES
#include "opaquetypesinternalheader.h"
#endif

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Mar 12 '07 #3
On Mar 11, 5:57 pm, Ian Collins <ian-n...@hotmail.comwrote:
By the way, you can avoid dynamic allocation of structures by using
static variables.
You mean, "pre-allocate" a bunch of said structures in the source that
also contains the definition of the structure? True: but not terribly
practical for some uses, and would certainly add complexity to many
environments, esp. multithreaded.

-b

Mar 12 '07 #4
On Mar 11, 5:59 pm, Richard Heathfield <r...@see.sig.invalidwrote:
#if ABANDON_OPACITY_AND_DAMN_THE_CONSEQUENCES
#include "opaquetypesinternalheader.h"
#endif
Heh. I like that. Well, not *like* exactly: it makes for a few extra
files, eh? But it's a clever approach.

-b

Mar 12 '07 #5
bluejack wrote:
On Mar 11, 5:57 pm, Ian Collins <ian-n...@hotmail.comwrote:

>>By the way, you can avoid dynamic allocation of structures by using
static variables.


You mean, "pre-allocate" a bunch of said structures in the source that
also contains the definition of the structure? True: but not terribly
practical for some uses, and would certainly add complexity to many
environments, esp. multithreaded.
One of the projects I mentioned was a static design (16 bit embedded
with limited RAM), so we had to preallocate everything!

--
Ian Collins.
Mar 12 '07 #6
On Sun, 11 Mar 2007 17:29:32 -0700, bluejack wrote:
Ahoy:

For as long as I've been using C, I've vacillated on the optimal
degree of encapsulation in my designs. At a minimum, I aggregate data
and code that operate on that data into classlike files; but now and
then I go on an opaque type joyride, and create minimalist header
files that define very clean interfaces.

The problem with that is that it prevents some optimizations:

* Can no longer allocate structures on the stack;
The problem with malloc() and free() is that they're these global
mechanisms; very blunt. I wrote a library which, aside from implementing
some common alternative allocation strategies, defines a strict interface
for providing other alternatives. Almost all of my new code uses this, and
the instantiation methods for encapsulated code always take a 'struct
arena_prototype' as an argument. Providing a stack allocator would be a
cinch. Moral: if you encounter the same problem over and over again,
abstract ;)
* Can no longer put inline functions in the header file;
Why not? If you think about it, functions you typically want to inline
are short and sweet, and thus should be relatively stable. Just an
observation, not an answer.
* Incurs the overhead of a function call for any data-access.
If your encapsulating something, ostensibly the functionality provides
some useful abstraction over the data. In that case, function call
overhead is probably marginal from the get-go.

Also, ubiquitous inter-unit optimizations (including inlining)
is almost upon us. Say, another two or three years to cover 2/3
of environments? Not so bad.
I'm curious as to how C experts feel about this design concept;
reviewing some old (very old) threads about encapsulation, I see that
many dismiss it as though it were a tenet of some foolish philosophy;
and yet, encapsulation has all sorts of benefits to the author of
libraries in terms of the ability to extend, adapt, & refactor code;
it has also helped me in my own designs by illuminating design
mistakes before they get very far.
Well, that's probably the most important criterion of all.
Mar 12 '07 #7
William Ahern wrote:
On Sun, 11 Mar 2007 17:29:32 -0700, bluejack wrote:
>>* Can no longer put inline functions in the header file;

Why not? If you think about it, functions you typically want to inline
are short and sweet, and thus should be relatively stable. Just an
observation, not an answer.
My take on the above was "Can no longer put inline functions *that
access the opaque type* in the header file".

--
Ian Collins.
Mar 12 '07 #8
On Mar 11, 7:29 pm, "bluejack" <bluuj...@gmail.comwrote:
Ahoy:

For as long as I've been using C, I've vacillated on the optimal
degree of encapsulation in my designs. At a minimum, I aggregate data
and code that operate on that data into classlike files; but now and
then I go on an opaque type joyride, and create minimalist header
files that define very clean interfaces.

The problem with that is that it prevents some optimizations:

* Can no longer allocate structures on the stack;
* Can no longer put inline functions in the header file;
* Incurs the overhead of a function call for any data-access.

I'm curious as to how C experts feel about this design concept;
reviewing some old (very old) threads about encapsulation, I see that
many dismiss it as though it were a tenet of some foolish philosophy;
and yet, encapsulation has all sorts of benefits to the author of
libraries in terms of the ability to extend, adapt, & refactor code;
it has also helped me in my own designs by illuminating design
mistakes before they get very far.

I have not found the run time consequences of encapsulation to
be onerous (or even visible). To a certain extent I consider
that if you need to allocate structures on the stack or use inline
functions on the structure or if the cost of a data-access is
significant, you need to rethink the design.

More problematic is the effect on program modification. With
several layers of encapsulation, making a minor but unanticipated
change can require modification to a dozen files, spread
over several directories.

Encapsulation is great if you can make sure that the user does not
need to see the internals.

- William Hughes
Mar 12 '07 #9
On Mar 11, 7:20 pm, "William Hughes" <wpihug...@hotmail.comwrote:
More problematic is the effect on program modification. With
several layers of encapsulation, making a minor but unanticipated
change can require modification to a dozen files, spread
over several directories.
Tell me more:

I generally see good encapsulation as being something that addresses
this very problem. If the *API* is stable, then minor changes to the
implementation should be *less* likely to require changes outside the
file. And if the API is changing, I would imagine changes to files
that access the API to be less intrusive than if you are changing the
structure *and* the APIs.

Are you picturing a situation in which object A encapsulates object B
which encapsulates object C, such that a significant change in C will
require alterations to higher level APIs as well? I have a hard time
picturing a case where encapsulation would make that worse than if the
structure members were accessed directly. Do you have an example?

Or were you thinking of something else?

Mar 12 '07 #10
On Mon, 12 Mar 2007 15:03:46 +1300, Ian Collins wrote:
William Ahern wrote:
>On Sun, 11 Mar 2007 17:29:32 -0700, bluejack wrote:
>>>* Can no longer put inline functions in the header file;

Why not? If you think about it, functions you typically want to inline
are short and sweet, and thus should be relatively stable. Just an
observation, not an answer.
My take on the above was "Can no longer put inline functions *that
access the opaque type* in the header file".
Ah. Good point. I defer to Richard Heathfield's admission, then ;)

When I would like to use [static] inline functions or macros, I'm
not averse to put some or all of the structure definitions in the header.

The definition for the FILE type is often exposed in stdio.h, but it
doesn't seem that people stick their noses in it that often.

Also, in some circumstances I'm okay with leaving instantiation up to the
user. In that case, I provide static initializers:

struct foo my_foo = foo_initializer;

foo_do_something(&my_foo);

The POSIX threading library works that way:

pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;

assert(0 == pthread_mutex_lock(&my_mutex));

(POSIX condition variables are actually a better example, actually.)
Mar 12 '07 #11
On Mar 11, 10:11 pm, "bluejack" <bluuj...@gmail.comwrote:
On Mar 11, 7:20 pm, "William Hughes" <wpihug...@hotmail.comwrote:
More problematic is the effect on program modification. With
several layers of encapsulation, making a minor but unanticipated
change can require modification to a dozen files, spread
over several directories.

Tell me more:

I generally see good encapsulation as being something that addresses
this very problem. If the *API* is stable, then minor changes to the
implementation should be *less* likely to require changes outside the
file. And if the API is changing, I would imagine changes to files
that access the API to be less intrusive than if you are changing the
structure *and* the APIs.

Are you picturing a situation in which object A encapsulates object B
which encapsulates object C, such that a significant change in C will
require alterations to higher level APIs as well? I have a hard time
picturing a case where encapsulation would make that worse than if the
structure members were accessed directly. Do you have an example?

Or were you thinking of something else?
I was thinking of the case where the modification was to something
that used A, but the modification needed information from C.

E.g. a piece of state in C is needed
by somthing using A, but the need for this piece of state
was not anticipated. If there is no encapsulation I can write
something like

needed_state = a_pointer->b_pointer->c_pointer->low_level_state
With encapsulation I need to write something like

needed_state = a_pointer_get_needed_state(a_pointer)
which needs
needed_state = b_pointer_get_needed_state(b_pointer)
which needs
needed_state = c_pointer_get_needed_state(c_pointer)
and I have to modify a bunch of files.

- William Hughes

Mar 12 '07 #12
On 11 Mar 2007, "bluejack" <bl******@gmail.comwrote:
For as long as I've been using C, I've vacillated on the optimal
degree of encapsulation in my designs. At a minimum, I aggregate
data and code that operate on that data into classlike files; but now
and then I go on an opaque type joyride, and create minimalist header
files that define very clean interfaces.

[snip]
I'm curious as to how C experts feel about this design concept;
Good question!

I default to opaque types and function based interfaces. I've found
over the years that this has advantages that outweigh everything
else, primarily because I create a lot of reusable library code. I
can fix bugs, augment functionality and change implementation without
"recompiling the world" due to a change to a struct defined in a
header. I *know* no module knows the structure's definition when
it's defined in the .c file!

But every rule has it's exceptions, often for the reasons you gave.
I have one module which is fundamental to many of my projects, an
abstract type Buffer. It's used in ISRs in device drivers, among
other places, so function call overhead for accessing variables is
out of the question, and the code must manipulate the Buffer
internals anyway. In that case, I swallow hard, export the struct
definition and provide function macros which do exactly what the
functions do (and implement the functions using the macros to make
sure). So buf_Count(b) (the function) is implemented as:

return buf_COUNT(b);

and buf_COUNT(b) is implemented (in buffer.h) as:

#define buf_COUNT(b) ((b)->end - (b)->start)

(Most of the compilers I use don't support inline functions.) I use
the function where I can and the macro where I must.

But really, cases like this where I've needed to export module
internals are pretty rare, and for me, the reduced coupling provided
by opaque types overwhelms almost every other consideration.

Dave

--
D.a.v.i.d T.i.k.t.i.n
t.i.k.t.i.n [at] a.d.v.a.n.c.e.d.r.e.l.a.y [dot] c.o.m
Mar 12 '07 #13
"bluejack" <bl******@gmail.comwrites:
For as long as I've been using C, I've vacillated on the optimal
degree of encapsulation in my designs. At a minimum, I aggregate data
and code that operate on that data into classlike files; but now and
then I go on an opaque type joyride, and create minimalist header
files that define very clean interfaces.
I use fully opaque types when maintainability is more important
than performance. As performance becomes more important, I
reduce the degree of opacity as necessary.

I prefer to use profiling as the indicator of when performance is
important.
--
"When I have to rely on inadequacy, I prefer it to be my own."
--Richard Heathfield
Mar 12 '07 #14
On 11 Mar 2007 17:29:32 -0700, "bluejack" wrote:
>I go on an opaque type joyride, and create minimalist header
files that define very clean interfaces.
The problem with that is that it prevents some optimizations:

* Can no longer allocate structures on the stack;
You can, e.g.

struct my_struct {
PROPERLY_ALIGNED_TYPE data[32];
};

PROPERLY_ALIGNED_TYPE is just a placeholder. In your *.c file you need
to cast the my_struct object to your hidden real implementation
struct.
>* Can no longer put inline functions in the header file;
* Incurs the overhead of a function call for any data-access.
>I'm curious as to how C experts feel about this design concept;
reviewing some old (very old) threads about encapsulation, I see that
many dismiss it as though it were a tenet of some foolish philosophy;
Encapsulaton isn't useful for all objects but in general I'd agree
with you: the more the better.

Best regards,
Roland Pibinger
Mar 12 '07 #15
David Tiktin wrote:
>
.... snip ...
>
But every rule has it's exceptions, often for the reasons you gave.
I have one module which is fundamental to many of my projects, an
abstract type Buffer. It's used in ISRs in device drivers, among
other places, so function call overhead for accessing variables is
out of the question, and the code must manipulate the Buffer
internals anyway. In that case, I swallow hard, export the struct
definition and provide function macros which do exactly what the
functions do (and implement the functions using the macros to make
sure). So buf_Count(b) (the function) is implemented as:

return buf_COUNT(b);

and buf_COUNT(b) is implemented (in buffer.h) as:

#define buf_COUNT(b) ((b)->end - (b)->start)
Here you can possibly discourage use of the exported struct
definition via some relatively long prefix infested names. Now the
macro becomes:

#define buf_COUNT(b) b->nastyprefix_buffer_the_absolute_end \
- b->nastyprefix_buffer_the_raw_beginning

You can also provide an auxiliary function that exports some
selected offsets in the structure, which only need be called once
to get those values. After which the macro can operate with those
values. My nmalloc package is an example of this, which allows
clean connection of run-time debuggery and does not restrain
altering the main package. See:

<http://cbfalconer.home.att.net/download/>

The malldbg module uses the facility.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>

--
Posted via a free Usenet account from http://www.teranews.com

Mar 12 '07 #16
bluejack wrote:
For as long as I've been using C, I've vacillated on the optimal
degree of encapsulation in my designs. At a minimum, I aggregate data
and code that operate on that data into classlike files; but now and
then I go on an opaque type joyride, and create minimalist header
files that define very clean interfaces.

The problem with that is that it prevents some optimizations:

* Can no longer allocate structures on the stack;
* Can no longer put inline functions in the header file;
* Incurs the overhead of a function call for any data-access.

I'm curious as to how C experts feel about this design concept;
reviewing some old (very old) threads about encapsulation, I see that
many dismiss it as though it were a tenet of some foolish philosophy;
and yet, encapsulation has all sorts of benefits to the author of
libraries in terms of the ability to extend, adapt, & refactor code;
it has also helped me in my own designs by illuminating design
mistakes before they get very far.
Take a look at The ANSI C Numerical Class Library

http://www.netwood.net/~edwin/svmtl/

Look at a header prototype file like

cncl/src/matrix/matrix.hP

You can use this mechanism to help hide the actual object definition
without using opaque data types.
But, unless you *really* need high performance
such as may be requiredfor a numerical class library,
opaque data types usually cost very little.

----== Posted via Newsfeeds.Com - Unlimited-Unrestricted-Secure Usenet News==----
http://www.newsfeeds.com The #1 Newsgroup Service in the World! 120,000+ Newsgroups
----= East and West-Coast Server Farms - Total Privacy via Encryption =----
Mar 12 '07 #17
Ben Pfaff said:

<snip>
I prefer to use profiling as the indicator of when performance is
important.
Well, of course profiling /can't/ tell you when performance is
important. What it /can/ do is tell you what the performance /is/.
Whether it's within acceptable parameters is a decision we have to make
for ourselves; it is not something our profilers can decide on our
behalf.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Mar 13 '07 #18
Richard Heathfield <rj*@see.sig.invalidwrites:
Ben Pfaff said:
>I prefer to use profiling as the indicator of when performance is
important.

Well, of course profiling /can't/ tell you when performance is
important. What it /can/ do is tell you what the performance /is/.
Profiling can tell me whether the performance of any given
component is important to the overall performance of a system.
--
"Given that computing power increases exponentially with time,
algorithms with exponential or better O-notations
are actually linear with a large constant."
--Mike Lee
Mar 13 '07 #19
In article <87************@blp.benpfaff.org>,
Ben Pfaff <bl*@cs.stanford.eduwrote:
>Profiling can tell me whether the performance of any given
component is important to the overall performance of a system.
And often it gives a clue as to whether there is any worthwhile
optimisation to be done: if the time spent is widely spread out, it
may be hard to find any code that repays attention. On the other
hand, if you find that 90% of the time is in one unexpected function,
it's often easy to make a big improvement.

-- Richard

--
"Consideration shall be given to the need for as many as 32 characters
in some alphabets" - X3.4, 1963.
Mar 13 '07 #20
William Hughes wrote:
On Mar 11, 10:11 pm, "bluejack" <bluuj...@gmail.comwrote:
>On Mar 11, 7:20 pm, "William Hughes" <wpihug...@hotmail.comwrote:
>>More problematic is the effect on program modification. With
several layers of encapsulation, making a minor but unanticipated
change can require modification to a dozen files, spread
over several directories.
Tell me more:

I generally see good encapsulation as being something that addresses
this very problem. If the *API* is stable, then minor changes to the
implementation should be *less* likely to require changes outside the
file. And if the API is changing, I would imagine changes to files
that access the API to be less intrusive than if you are changing the
structure *and* the APIs.

Are you picturing a situation in which object A encapsulates object B
which encapsulates object C, such that a significant change in C will
require alterations to higher level APIs as well? I have a hard time
picturing a case where encapsulation would make that worse than if the
structure members were accessed directly. Do you have an example?

Or were you thinking of something else?

I was thinking of the case where the modification was to something
that used A, but the modification needed information from C.

E.g. a piece of state in C is needed
by somthing using A, but the need for this piece of state
was not anticipated. If there is no encapsulation I can write
something like

needed_state = a_pointer->b_pointer->c_pointer->low_level_state
With encapsulation I need to write something like

needed_state = a_pointer_get_needed_state(a_pointer)
which needs
needed_state = b_pointer_get_needed_state(b_pointer)
which needs
needed_state = c_pointer_get_needed_state(c_pointer)
and I have to modify a bunch of files.
It may be more of a hassle, but I still think that it is cleaner to
modify the files since, the system may change and the state is no longer
in C but in D. In which case, your 'needed_state =
a_pointer->b_pointer->c_pointer->low_level_state' either will not
compile or will work incorrectly (the worse of the two) and then you
have a bug that may not be trivial to track down.

I'm a bit proponent of encapsulation. It keeps things neat and tidy
(for the most part) and keeps others hands out of the cookie jar, where
they might find an unexpected bug biting at their fingers (unexpected
consequences). Also, if a bug is found in the encapsulated code, you
fix it in one place instead of all over the system (been there, done
that, don't want to go there again).

For all but the simplest of structs would I consider exposing it, and
even then, I would have to think about it at length to make sure it is
something that will not change over the course of development.
Adrian

--
================================================== ========
Adrian Hawryluk BSc. Computer Science
----------------------------------------------------------
Specialising in: OOD Methodologies in UML
OOP Methodologies in C, C++ and more
RT Embedded Programming
__--------------------------------------------------__
----- [blog: http://adrians-musings.blogspot.com/] -----
'--------------------------------------------------------'
My newsgroup writings are licensed under the Creative
Commons Attribution-Noncommercial-Share Alike 3.0 License
http://creativecommons.org/licenses/by-nc-sa/3.0/
================================================== ========
Mar 13 '07 #21
David Tiktin wrote:
Good question!

I default to opaque types and function based interfaces. I've found
over the years that this has advantages that outweigh everything
else, primarily because I create a lot of reusable library code. I
can fix bugs, augment functionality and change implementation without
"recompiling the world" due to a change to a struct defined in a
header. I *know* no module knows the structure's definition when
it's defined in the .c file!

But every rule has it's exceptions, often for the reasons you gave.
I have one module which is fundamental to many of my projects, an
abstract type Buffer. It's used in ISRs in device drivers, among
other places, so function call overhead for accessing variables is
out of the question, and the code must manipulate the Buffer
internals anyway. In that case, I swallow hard, export the struct
definition and provide function macros which do exactly what the
functions do (and implement the functions using the macros to make
sure). So buf_Count(b) (the function) is implemented as:

return buf_COUNT(b);

and buf_COUNT(b) is implemented (in buffer.h) as:

#define buf_COUNT(b) ((b)->end - (b)->start)

(Most of the compilers I use don't support inline functions.) I use
the function where I can and the macro where I must.

But really, cases like this where I've needed to export module
internals are pretty rare, and for me, the reduced coupling provided
by opaque types overwhelms almost every other consideration.
What you describe is not bad, you are still using encapsulation, just
not a black box type. As long as users of the library are honest and
don't touch the struct (get out the ruler if you have to ;)) then things
will continue to go smoothly. Even if a bug is encountered, those
source files that depend on the macros are recompiled and you have a
clean, globally repaired system.

When writing code, you have to take what comes at you and do what is best.
Adrian

--
================================================== ========
Adrian Hawryluk BSc. Computer Science
----------------------------------------------------------
Specialising in: OOD Methodologies in UML
OOP Methodologies in C, C++ and more
RT Embedded Programming
__--------------------------------------------------__
----- [blog: http://adrians-musings.blogspot.com/] -----
'--------------------------------------------------------'
My newsgroup writings are licensed under the Creative
Commons Attribution-Noncommercial-Share Alike 3.0 License
http://creativecommons.org/licenses/by-nc-sa/3.0/
================================================== ========
Mar 13 '07 #22

"bluejack" <bl******@gmail.comwrote in message
I'm curious as to how C experts feel about this design concept;
reviewing some old (very old) threads about encapsulation, I see that
many dismiss it as though it were a tenet of some foolish philosophy;
and yet, encapsulation has all sorts of benefits to the author of
libraries in terms of the ability to extend, adapt, & refactor code;
it has also helped me in my own designs by illuminating design
mistakes before they get very far.
Are you sure those old threads are not attacking "object-orientation"?
Object-oriented code is encapsulated, but not all encapsulated code is
object-oriented.

Personally I never use opaque pointers because, if you make a mistake in the
interface, it is then impossible to fix without rewriting the opaque code.
That might cause other problems. However I keep the pointers opaque in that
calling code shouldn't need to access them in everyday use.

--
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

Mar 13 '07 #23
Malcolm McLean wrote:
>
"bluejack" <bl******@gmail.comwrote in message
>I'm curious as to how C experts feel about this design concept;
reviewing some old (very old) threads about encapsulation, I see that
many dismiss it as though it were a tenet of some foolish philosophy;
and yet, encapsulation has all sorts of benefits to the author of
libraries in terms of the ability to extend, adapt, & refactor code;
it has also helped me in my own designs by illuminating design
mistakes before they get very far.
Are you sure those old threads are not attacking "object-orientation"?
Object-oriented code is encapsulated, but not all encapsulated code is
object-oriented.

Personally I never use opaque pointers because, if you make a mistake in
the interface, it is then impossible to fix without rewriting the opaque
code.
So how's that different from changing the the code if you change a struct?
That might cause other problems.
Such as?
However I keep the pointers
opaque in that calling code shouldn't need to access them in everyday use.
You either expose the type, or you don't. You can't have it both ways.
Once a type is exposed, it will get used.

--
Ian Collins.
Mar 13 '07 #24
On Mar 12, 9:07 pm, Adrian Hawryluk <adrian.hawryluk-at-
gmail....@nospam.comwrote:
William Hughes wrote:
I was thinking of the case where the modification was to something
that used A, but the modification needed information from C.
E.g. a piece of state in C is needed
by somthing using A, but the need for this piece of state
was not anticipated. If there is no encapsulation I can write
something like
needed_state = a_pointer->b_pointer->c_pointer->low_level_state
With encapsulation I need to write something like
needed_state = a_pointer_get_needed_state(a_pointer)
which needs
needed_state = b_pointer_get_needed_state(b_pointer)
which needs
needed_state = c_pointer_get_needed_state(c_pointer)
and I have to modify a bunch of files.

It may be more of a hassle,

Which was my point. A different question is whether the hassle is
worth it. My answer would depend on how likely the above scenario
is thought to be (a judgment call, and they call this computer
*science*). If it is thought to be likely, then encapsulation
may not be appropriate. In my experience you add about an order
of magnitude to the amount of time needed to make a simple change.
On the other hand, encapsulation goes a long way to insuring
that your "simple change" does not have unintended consequences,
and has other advantages (some of which you note)
thus I do support encapsulation as a default.

- William Hughes


Mar 13 '07 #25

"Ian Collins" <ia******@hotmail.comwrote in message
>
>Personally I never use opaque pointers because, if you make a mistake in
the interface, it is then impossible to fix without rewriting the opaque
code.

So how's that different from changing the the code if you change a struct?
>That might cause other problems.

Such as?
Let's say the opaque code is a bitstream. Accepts bits, and stores them in
an array of bytes. A fairly general-purpose routine.
It is called by a Huffman compressor, and it is also called by the code I am
working on, which is an encryption algorithm.

I haven't provided an "ungetbit" and for some reason my encryption routine
needs it. My choices are to hack into the bitstream struct in the encryption
module, or rewrite the bitstream. Choice two is the better one, except

The Huffman routine is very serious and used in scientific work. The
encryption routine is just a bit of messing about which I might use for
something that ships but might not. So if I rewrite the code, and break the
Huffman routine, people are going to get really annoyed. Fork development?
The last thing you want is two similar bitstream files swilling around.
Retest the whole thing so it impossible that the Huffman routine will break?
Yes, that would be best, but it's a big job.

Because each bitstream is allocated privately with its own call, however
much I mess about with the internals, the encryption routine won't break
anything else. So a quick hack to implement an ungetbit() is the best
answer, or at least a defensible answer.
>
>However I keep the pointers
opaque in that calling code shouldn't need to access them in everyday
use.
You either expose the type, or you don't. You can't have it both ways.
Once a type is exposed, it will get used.
You don't guarantee to maintain anything except the interface. So caller
uses struct members directly at his own risk.
--
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

Mar 14 '07 #26
Malcolm McLean wrote:
>
"Ian Collins" <ia******@hotmail.comwrote in message
>>
>>Personally I never use opaque pointers because, if you make a mistake in
the interface, it is then impossible to fix without rewriting the opaque
code.


So how's that different from changing the the code if you change a
struct?
>>That might cause other problems.


Such as?
Let's say the opaque code is a bitstream. Accepts bits, and stores them
in an array of bytes. A fairly general-purpose routine.
It is called by a Huffman compressor, and it is also called by the code
I am working on, which is an encryption algorithm.

I haven't provided an "ungetbit" and for some reason my encryption
routine needs it. My choices are to hack into the bitstream struct in
the encryption module, or rewrite the bitstream. Choice two is the
better one, except

The Huffman routine is very serious and used in scientific work. The
encryption routine is just a bit of messing about which I might use for
something that ships but might not. So if I rewrite the code, and break
the Huffman routine, people are going to get really annoyed. Fork
development? The last thing you want is two similar bitstream files
swilling around. Retest the whole thing so it impossible that the
Huffman routine will break? Yes, that would be best, but it's a big job.
So long as the bitstream has been implemented with proper unit tests,
just add the new ungetbit() interface, make sure all the tests pass and
move on.

--
Ian Collins.
Mar 14 '07 #27
"Ian Collins" <ia******@hotmail.comwrote in message
>
So long as the bitstream has been implemented with proper unit tests,
just add the new ungetbit() interface, make sure all the tests pass and
move on.
But if it hasn't?
Bitstreams can be unit tested quite easily and it is arguably a good idea to
keep a suite handy. In fact the last time I broke a bitstream it was to
honour the MPEG / JPEG rule about consecutive runs of ones or zeroes. So
that would have entailed a complete rewrite of the unit tests. In fact I
didn't write any.

Not everything can be unit tested as easily as that. Higher level modules
often depend quite intimately on other modules in the system and cannot be
tested in isolation from them without effectively rewriting the whole
program. This isn't ideal, and good programming often involves avoiding such
a situation, but it is often inevitable.
--
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm


Mar 14 '07 #28
Malcolm McLean wrote:
"Ian Collins" <ia******@hotmail.comwrote in message
>>
So long as the bitstream has been implemented with proper unit tests,
just add the new ungetbit() interface, make sure all the tests pass and
move on.
But if it hasn't?
Bitstreams can be unit tested quite easily and it is arguably a good
idea to keep a suite handy. In fact the last time I broke a bitstream it
was to honour the MPEG / JPEG rule about consecutive runs of ones or
zeroes. So that would have entailed a complete rewrite of the unit
tests. In fact I didn't write any.
...and you broke it!
Not everything can be unit tested as easily as that. Higher level
modules often depend quite intimately on other modules in the system and
cannot be tested in isolation from them without effectively rewriting
the whole program. This isn't ideal, and good programming often involves
avoiding such a situation, but it is often inevitable.
Normal practice is to mock the lower layers. I've yet to come across a
problem that can't be tackled test first.

--
Ian Collins.
Mar 14 '07 #29
Ian Collins said:
Normal practice is to mock the lower layers.
IRTA "mock the lower lawyers". Not that I have a problem with that...

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Mar 14 '07 #30
Richard Heathfield wrote:
Ian Collins said:

>>Normal practice is to mock the lower layers.


IRTA "mock the lower lawyers". Not that I have a problem with that...
Irish Rider Training Association?

--
Ian Collins.
Mar 14 '07 #31
On Thu, 15 Mar 2007 10:46:15 +1300, in comp.lang.c , Ian Collins
<ia******@hotmail.comwrote:
>Richard Heathfield wrote:
>Ian Collins said:

>>>Normal practice is to mock the lower layers.


IRTA "mock the lower lawyers". Not that I have a problem with that...
Irish Rider Training Association?
"I read that as"

--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
Mar 14 '07 #32
Ian Collins wrote:
Richard Heathfield wrote:
>Ian Collins said:
>>Normal practice is to mock the lower layers.

IRTA "mock the lower lawyers". Not that I have a problem with that...

Irish Rider Training Association?
The IRT A train?

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>

--
Posted via a free Usenet account from http://www.teranews.com

Mar 15 '07 #33

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
by: Charles Hartman | last post by:
I know the answer to this is going to be "It depends . . .", but I want to get my mind right. In Fowler's *Refactoring* I read: "Older languages carried an overhead in subroutine calls, which...
7
by: Pelle Beckman | last post by:
Hi, I've just finished writing a template (whew!) and would like some opinions on the design. The goal was to be able to do fairly simple singletons (no excplicit thread-safety, etc) and...
13
by: KV | last post by:
I'm new to OO Design, and I'm fixing to start writing my very first C# program. Given the complexity of OO programming, I would like to run something by this group and get general input. My...
4
by: GS | last post by:
Hi, I'd rather start from a good design and go from there so would be greatfull for any input. I have a simple ASP.NET application and would like to make solution elegant. I store settings in...
1
by: rko | last post by:
Szenario a project for up to 4 million (prospective) users data packages that are send and recieved range from 100k to 250 mb system must be highly secure (patient data) users must be verified ...
0
by: PJ6 | last post by:
Does anyone know of a good, simple example of a custom server-side (2.0) container control, where you can drag drop other controls into an editable region, and what you see at design time is what...
1
by: =?ISO-8859-1?Q?Michael_Bernhard_Arp_S=F8rensen?= | last post by:
Hi there. As a newbie, I need to learn about callbacks and queues(syntax and examples) working together. At work we talk a lot about design patterns. Does any of you know a good site about...
2
TheServant
by: TheServant | last post by:
Hi everybody, I posted a similar question about 6~12 months ago with no definate answer, so I will give it another go, as the choice has popped up again in my programming life: If the number of...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.