Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- From: "cr88192" <cr88192@xxxxxxxxxxx>
- Date: Wed, 3 Jun 2009 18:34:48 -0700
"H. S. Lahman" <h.lahman@xxxxxxxxxxx> wrote in message
news:IZyVl.468$u86.234@xxxxxxxxxxxxxxxxxxxxxxx
Responding to cr88192...
recently I did a post presenting some of my personal premises onI'm not sure what you mean by sharing data. Usually a public API
library design (summarizing here):
if it is a choice between C and C++ for the public API, choose C (C++
may well be preferable for the lib internals, OTOH);
do not share structured data (structs, ..., except maybe in certain
rare cases...);
do not share complex data (much more complex than simple/flat arrays);
if one really must share complex data, share it in a serialized form;
in the choice between textual and binary serializations, choose text
(some edge cases were described, such as cases where the difference
between text and binary is "blurred"...);
naming should be terse, yet effective (I mentioned
'libprefixCamelCase', which IMO is a good style for public APIs, at the
restriction that it be avoided for lib internals, for various
reasons...);
...
encapsulates something substantial so the data behind the API would not
be visible to those outside. Conversely, any state data owned by clients
outside the API that is needed inside would have to be provided
parametrically through the API.
I've worked on systems where different processes communicated through
shared memory. Is that what you meant?
no, I mean, where both the code inside the API and outside the API make
use of the same memory, or can access data directly as structs...
for example, the client is like:
LIB_SharedFoo *foo;
foo=LIB_AllocFoo();
foo->arr=malloc(16*3*sizeof(float));
foo->sz_arr=16;
foo->arr[0]=10;
foo->arr[1]=7.5;
...
LIB_DoSomething(foo);
personally, I regard this as fairly poor design for library API's.
I agree.
I actually used to do this a lot more, but increasingly I have moved away
from this practice (in favor of abstract handles and accessors) as it
tends to make too many maintainability problems (basically, the frontend
code gets tangled up in the mess of what all data is in the struct and
what are proper rules are for each field, ...).
my newer approach is to perform all accesses of this sort either through
generic or specialized getters and setters (a specialized getter/setter
would be a function specifically for the property in question, whereas a
generic getter or setter may be only specific to a particular type, and
where the field to be get/set is indicated either via a name or a
handle...).
Right. Data in one place (one owner). Data-specific access (either through
type, your specialized, or identity, your generic). Then all one needs is
an addressing mechanism that ensures the client gets to the right data via
the right owner. Make the owner an object and instantiate a relationship
path to get to the right object and one has an OO application. B-)
yep, ok.
That's what problem space abstraction provides. Customers don't likekind of problem if one doesn't know what the architecture will bewhat makes this problem difficult is when this architectural issueI think a large part of the answer lies in good application
effects maybe 75-100 kloc of a 300-kloc subproject, and it is
desirable not to just break the whole thing in ones' attempt to fix
it (IME, often if one breaks the code in the process, this greatly
increases the risk that it will never get fixed, as the tendency then
becomes to start working around the broken code rather than actively
trying to fix it, causing the whole thing to atrophy...).
partitioning (which is more of a Systems Engineering issue than an OO
issue per se). If one treats the partitioning of the application as a
fundamental definition of the architecture, then one just has to
identify subsystems and invariant interfaces for them that will be
stable over time, regardless of how detailed requirements change.
before they start...
change any more that developers do. So when they have to accommodate
change, they do it in a manner that will cause minimum disruption of
their existing infrastructure. If the software architecture emulates the
customer's domain infrastructure faithfully, then when change filters
down to the software if should be relatively easy to accommodate it
because the customer has already figured out the path of least
resistance.
this is only, I think, if one implementing some kind of "business
system..." (or, some other system where there are such "customers" who
would have anything to do with the problem space...).
Useful definitions:
client; the one who pays for the software
customer: the one who specifies the requirements for the software
user: the one who actually uses the software.
Thus a customer does not have to be in an IT domain. (I haven't worked in
IT since the '70s.) But wherever the customer lives there is some domain
infrastructure for their daily work.
yep...
70s, well before my time...
all I will say is that I have existed since during the production run of the
286 but before the start of the production run for the 386...
so, the 286 is older than I am but the 386 is younger...
or, alternatively, I have existed an almost arbitrarily huge amount of time,
depending on ones' views of metaphysics...
I don't know if you've seen it, but there is a famous diagram that
describes the various networking protocols used by telcoms, ISPs, etc..
The version I saw was about 20' long and 5' high and it was taped up
around three walls of a conference room. It has a gazillion different
interlocked protocols sprinkled though several layers, all dazzlingly
color-coded. Awesome complexity. It describes the existing infrastructure
of their domain just like FASB and IRS rules define the insfrastructure of
an IT accounting space.
not seen the chart, but I know of some of the protocols...
once I wrote an OS, and implemented support for ethernet and dial-up, and
even with this one sees more than a few protocols that one would have not
likely been all that aware of otherwise (ARP, PPP, ...).
I guarantee you that no one in those industries is going to try to shove a
new protocol into that diagram without thinking a whole lot about how to
accommodate it. They will find a place to integrate it that will have the
least disruption. So if your mongo networking support system faithfully
reflects the structure of that diagram, it should be <relatively> easy to
introduce that new protocol into the software.
yes, ok.
this more reflects the world I interface with, only replace "protocols" with
"calling conventions" and "languages" and "libraries" and similar.
basically, I had decided, rather than implement a "typical" VM (AKA: with
all the usual issues with interfacing with the outside world), I would work
to try to keep external integration as a fairly high priority (and try to
keep things as modular as was reasonable).
but, alas, this has all complicated things somewhat, and in the case of
x86-64 had led to an ugly hack:
rather than being able to directly use the external calling conventions, a
custom one is used internally, and the dynamic linker automatically stubs
things to interface with the external world.
note that "XCall" (the internal calling convention) involves 2 major
components:
first is the calling convention itself (which on x86-64 combines elements of
x86 cdecl, Win64, and SysV/AMD64);
second is a name-mangling scheme, which is itself critical to the proper
operation of the calling convention (actually, "xcall" may be used on x86,
but in this case is just the name-mangling and a slight tweak of cdecl, and
so serves a role similar to C++ and the "thiscall" calling convention...).
however, it is not as bad, as otherwise I would have had to implement 2
essentially different x86-64 targets: one for Linux and friends; and another
for Win64. as-is, I can't directly use either calling convention, but the
stub machinery works for both of them...
of course, it does lead to a "worst case" when using 2 internal function
pointers, as code may need to perform 2 transitions: XCall->SysV, then
SysV->XCall...
note that each transition is a chunk of automatically generated machine
code...
a partial solution is possible in my case:
void *(__xcall *foo)();
where the '__xcall' keyword allows the function to be called natively (and
thus faster), but will blow up if one uses it with a SysV or Win64 function
pointer (or just casually assigns a function pointer between one and the
other...).
void *(__xcall *foo_f)();
void *(*bar_f)();
void *foo() //internally, xcall is used
{
}
....
bar_f=foo; //OK, bar_f gets a "wrapped" function pointer
foo_f=bar_f; //BAD
foo_f=foo; //OK, raw function pointer
bar_f=foo_f; //BAD
one "trick" here could be to involve internal conversion machinery, which
could make the operation safe but expensive...
alternatively, my planned alterations to the compiler core should be able to
make the actual calling convention part unnecessary (the compiler would be
flexible enough to handle both Win64 and SysV, and choose the one most
correct for the target), however, the name mangling scheme would continue
being useful.
similarly, if I am right my calling convention "should" perform on average
faster than SysV for typical coding practices. but, if I am correct, than a
similar pattern should likely appear:
pure-code benchmarks (not using system calls) should be slightly faster on
Win64 than on Linux x86-64. the reason here is that I suspect the SysV
calling convention, due to likely increase in register shuffling for many
tasks, may actually be slower than passing values on the stack (Win64 uses
far fewer regs for arg passing, so there should be a difference).
but, alas, I don't know if anyone has benchmarked this.
can't find any relevant benchmarks...
it doesn't work as well for compilers, where I guess the better approach
is to study existing implementations...
Depends on the compiler. Generally the compiler for a specific LALR
language is not a good application area for the OO paradigm and its
problem space abstraction. That's because it is very linear where each
statement is pretty much independent of every other statement to be
parsed.
'LALR':
the parser is only a small part of the process (as far as complexity goes).
all of the internal machinery and machine code generation tends to be far
more complex IME...
OTOH, I once worked on a system where there were nearly a dozen different
languages the user could apply. Rather than coming up with compiler for
each one we went the lex/yacc route and provided a generic compiler where
one configured it with an external BNF description of the language to be
compiled. Now the problem space was language compilation rather than a
single "hard-wired" language. So we essentially abstracted the BNF space
with things like Production and Terminal while also abstracting the
compilation mechanics with things like Syntax Table and Token.
In effect that BNF view provided exactly the sort of domain infrastructure
for solving the problem that I talked about above. By building the
software structure around that view, we achieved exactly the sort of ease
of integration we needed. IOW, it became trivial to deal with a new
language; we just needed a new configuration file to specify it. If we
started with a compiler structured for a specific language, it would have
been a nightmare to modify it to work for another language.
yeah...
I use most of the same frontend machinery for the languages I support (there
is a lot of overlap...).
C, C++, C#, and Java: most of the syntax is common...
C# and Java, however, have very different runtime requirements than C and
C++...
it is proving extra difficult trying to figure out how to make all of these
features from otherwise VM languages work more within the confines of a
native-code environment (and a framework originally designed just for C...).
it is all of the backend machinery that gets, painful...
so, alas, the frontend compiler has never really given me much trouble, but
the backend code generator has become terror...
then again, I guess GCC went through a lot of this a few years ago trying to
shoehorn Java into their framework...
At the level of application partitioning it should be possible to
identify subject matters in the customer problem space that are so
fundamental that that there is virtually no chance they will change
unless the customer switches businesses. Partitioning based on those
invariants emulates the customer's infrastructure and provides a stable
skeleton over time.
again, not general to software development in general, but only to cases
where there is some particular relation between the parties involved
(such as implementing business systems, software contracting, ...).
given not all software is developed in this setting, this particular set
of definitions need not apply...
for example, what if there are no "customers" (in a business relation
sense), but only "end users", where it is not the users dictating some
domain, but the developer selling them products with the developer
dictating the rules of the game and how the software is to be used by
said users...
I don't think it matters. In a hybrid HW/SW system the hardware itself
represents a problem space and defines requirements manifested in things
like the hardware bitsheets. IOW, the hardware itself is a customer of the
software in that it defines driver requirements. If the hardware is
complex enough, it will be logically subdivided, just like your PC can be
subdivided into ALU, DMA, Video, etc. components. To the extent that your
software needs to deal with those elements individually, it will pay to
organize the software around them.
Bottom line: for any complex problem there is going to be at least one
complex problem domain where complexity is managed by conceptually
decomposing the domain. Regardless of who actually defines the
requirements, those requirements are rooted in that problem space.
ok...
the issue is you keep bringing up "users" and "customers"...
this totally confuses the issue IMO...
.
- Follow-Ups:
- References:
- Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- From: H. S. Lahman
- Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- From: cr88192
- Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- From: H. S. Lahman
- Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- From: cr88192
- Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- From: H. S. Lahman
- Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- From: cr88192
- Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- From: H. S. Lahman
- Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- From: cr88192
- Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- From: H. S. Lahman
- Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- Prev by Date: Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- Next by Date: Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- Previous by thread: Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- Next by thread: Re: modularity... (was: Re: Looking for real world examples to explain the difference between procedural (structured?) programming and OOP)
- Index(es):