Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Objects in C

345 views
Skip to first unread message

David Kleinecke

unread,
Apr 18, 2015, 1:26:41 PM4/18/15
to
After all these years uncertainty still reigns about what
object-oriented computing is all about. I have my own opinions
and this groups seems like a good place to present my techniques
and, perhaps, get some useful feedback.

I consider objects a good way to organize processes. I also
prefer to code in C. By C I mean 1990 ANSI C.

By "class" I mean a pair of a struct type and a function. I
would define them both in a .h file along with an optional
enum. An object of the class is a variable of the struct type.

Suppose I want to use foobar objects. To do this I declare

typedef struct {...} FooBar;
void* foobar (int m, void* o, void* d);

where m is a method number (perhaps defined by an enum), o
is variable of type FooBar and d is additional data.

I prefer to clone variables. So I could call method 0 the
clone method and write

FooBar* fb = foobar (0, 0, 0);

Where cloning 0 creates a default FooBar object.

To inherit I just make a call to a parent class. To multiply
inherit it is necessary to change the method numbers to
inherit from all but one of the parent classes.

Some things people insist on - like polymorphism - seem to
me perhaps useful but easy to get along without.

Reactions ?

asetof...@gmail.com

unread,
Apr 18, 2015, 1:54:30 PM4/18/15
to
polymorphism what big word...
Message has been deleted

asetof...@gmail.com

unread,
Apr 18, 2015, 2:52:47 PM4/18/15
to
I foR OO I mean:
Compiler emit code for
Automatic creation, resize, destruction, of one object
Possibly even choose the right
function for apply
but that would be overloading...

All this is important the remain
return 0

Jens Stuckelberger

unread,
Apr 18, 2015, 3:02:43 PM4/18/15
to
On Sat, 18 Apr 2015 10:26:32 -0700, David Kleinecke wrote:

> After all these years uncertainty still reigns about what
> object-oriented computing is all about.

It was supposed to be a silver bullet - you know, solve all
possible programming issues, make code clean, clear and easy to
understand, prevent people from reinventing the wheel, give you fresh,
minty breath, make you irresistible to desirable members of your selected
sex, the works.

Reality put it in its place - good for some undertakings (e.g.
graphics) but just another paradigm for the rest. Some people swear by
it; just as many hate it with passion.



BGB

unread,
Apr 18, 2015, 4:12:42 PM4/18/15
to
and other people swear by "OO languages", taking Java as a golden
example, yet there is little really in Java on this front that can't be
done in C with structs and function-pointers (yes, including things like
interfaces and 'instanceof' and similar...).


of course, a nicer model for mapping OO to C is doing it more like in
Common Lisp, but then people object that this is not OO, as:
BaseClass_DoSomething(obj, ...);
doesn't really look anything like:
obj.doSomething(...);
or:
obj->DoSomething(...);

as, this pretty much just looks like normal C code (and internally
usually just forwards the call to the appropriate vtable entry).


other differences is people can balk when you do your interfaces like:
IFoo **foo;
foo=&obj->iFoo;
(*foo)->DoSomething(...);
rather than something like:
IFoo foo;
foo=(IFoo)obj;
foo.doSomething(...);


of course, what may be overlooked, is that structs and function pointers
allow doing a lot of things, and a lot more efficiently, that would be a
bit ugly and awkward if shoved into the traditional class/instance OO mold.


Message has been deleted

BGB

unread,
Apr 18, 2015, 5:50:08 PM4/18/15
to
On 4/18/2015 3:17 PM, Stefan Ram wrote:
> BGB <cr8...@hotmail.com> writes:
>> and other people swear by "OO languages", taking Java as a golden
>> example, yet there is little really in Java on this front that can't be
>> done in C with structs and function-pointers (yes, including things like
>> interfaces and 'instanceof' and similar...).
>
> You can also »do« classes and interfaces in 6502 assembler,
> but in Java it's more easier to read and write.
>

if a person wants to object to a language under the premise that OO
stuff can't be done in it, it falls flat if OO stuff *can* be done in it.

meanwhile, old-style strictly procedural code is by no means a
necessity, and if code is written this way, typically it is because it
is a better fit to the use-case.


>> of course, a nicer model for mapping OO to C is doing it more like in
>> Common Lisp, but then people object that this is not OO, as:
>
> Alan Kay, who coined the term, wrote about OOP:
>
> »It can be done in Smalltalk and in LISP. There are
> possibly other systems in which this is possible, but
> I'm not aware of them.«.
>

and Lisp OO is very much different than C++ or Java style OO.

Keith Thompson

unread,
Apr 18, 2015, 6:41:49 PM4/18/15
to
David Kleinecke <dklei...@gmail.com> writes:
> After all these years uncertainty still reigns about what
> object-oriented computing is all about. I have my own opinions
> and this groups seems like a good place to present my techniques
> and, perhaps, get some useful feedback.
>
> I consider objects a good way to organize processes. I also
> prefer to code in C. By C I mean 1990 ANSI C.

What's wrong with the later standards?

> By "class" I mean a pair of a struct type and a function. I
> would define them both in a .h file along with an optional
> enum. An object of the class is a variable of the struct type.
>
> Suppose I want to use foobar objects. To do this I declare
>
> typedef struct {...} FooBar;
> void* foobar (int m, void* o, void* d);
>
> where m is a method number (perhaps defined by an enum), o
> is variable of type FooBar and d is additional data.

*Perhaps* defined by an enum?

> I prefer to clone variables. So I could call method 0 the
> clone method and write
>
> FooBar* fb = foobar (0, 0, 0);
>
> Where cloning 0 creates a default FooBar object.

So I have to remember that 0 means "clone" to be able to read your code?
No thanks.

> To inherit I just make a call to a parent class. To multiply
> inherit it is necessary to change the method numbers to
> inherit from all but one of the parent classes.
>
> Some things people insist on - like polymorphism - seem to
> me perhaps useful but easy to get along without.
>
> Reactions ?
>

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

August Karlstrom

unread,
Apr 19, 2015, 5:48:28 AM4/19/15
to
On 2015-04-19 00:41, Keith Thompson wrote:
> David Kleinecke <dklei...@gmail.com> writes:
>> After all these years uncertainty still reigns about what
>> object-oriented computing is all about. I have my own opinions
>> and this groups seems like a good place to present my techniques
>> and, perhaps, get some useful feedback.
>>
>> I consider objects a good way to organize processes. I also
>> prefer to code in C. By C I mean 1990 ANSI C.
>
> What's wrong with the later standards?

The curse of backward compatibility which makes a language grow and grow.


-- August

Johannes Bauer

unread,
Apr 19, 2015, 6:47:56 AM4/19/15
to
On 18.04.2015 19:26, David Kleinecke wrote:

> Suppose I want to use foobar objects. To do this I declare
>
> typedef struct {...} FooBar;
> void* foobar (int m, void* o, void* d);
>
> where m is a method number (perhaps defined by an enum), o
> is variable of type FooBar and d is additional data.

[...]

> FooBar* fb = foobar (0, 0, 0);

[...]

> Reactions ?

Nausea. Plain, pure nausea.

You're trying to poorly emulate functionality that C doesn't provide. In
a way that is neither simple nor clear to anyone not familiar with your
style of OO. I've never seen any "OO-clone" seen in the way that you do
it and would be considerably confused if I stumbled about such
constructs in an unknown codebase.

And I'm not even starting with the whole type safety issue with all your
void pointers. It seems like a horrible beast to maintain where the
disadvantages far outweigh the advantages.

Cheers,
Johannes

--
>> Wo hattest Du das Beben nochmal GENAU vorhergesagt?
> Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
- Karl Kaos über Rüdiger Thomas in dsa <hidbv3$om2$1...@speranza.aioe.org>

BGB

unread,
Apr 19, 2015, 10:36:02 AM4/19/15
to
also, a lot of compilers never bothered (fully) supporting anything
newer than C89/C90.


>
> -- August

Keith Thompson

unread,
Apr 19, 2015, 10:56:00 AM4/19/15
to
That wasn't meant to be a general question about what's wrong with C99
and C11 (that's way too big a topic, or perhaps one for its own thread).
I was specifically asking about David Kleinecke's objections.

David Kleinecke

unread,
Apr 19, 2015, 12:27:23 PM4/19/15
to
I prefer simplicity. Only one of the "improvements" made after 1990 C seems
be actually useful if simplicity is a design criterion. That change is
allowing declarations to be made where they actually used rather than at the
start of the block. That is, of course, close to trivial.

I would rather code in assembly language but I realize, for all the usual
reasons, that that is not currently realistic. Perhaps some day when we all
agree on a single hardware design.

In my code I further simplify by never using "for" or "do .. while". And
I never put more than one statement on a line. So - if I am no exposing the
code I don't write semicolons, braces or parentheses in "if" etc. Adding
them is no problem.

Bottom line: I don't object to the changes in the 1990 standard - I just
feel no need for them.

David Kleinecke

unread,
Apr 19, 2015, 12:44:04 PM4/19/15
to
On Saturday, April 18, 2015 at 11:04:31 AM UTC-7, Stefan Ram wrote:
> David Kleinecke <dklei...@gmail.com> writes:
> >After all these years uncertainty still reigns about what
> >object-oriented computing is all about.
>
> The term was coined by Alan Key in 1967.
>
> In 2003, I asked him what it means to him. He kindly replied:
>
> »OOP to me means only messaging, local retention and
> protection and hiding of state-process, and extreme
> late-binding of all things.«
>
> http://www.purl.org/stefan_ram/pub/doc_kay_oop_en

I didn't get my personal understanding from AK. Rather I deduced it from
the structure of SmallTalk. To me object-orientation means:

objects exchanging information by way of messages

with the implicit assumption that hat is all there is. It seems entirely
disjoint from question like binding.

The very old idea of functional programming essentially creates a new
object whenever a function is called. The object has exactly one method -
the function - and destroys itself upon completion. The new ideas of OOP
are (1) make the objects persistent and (2) encapsulate all data
storage into these persistent objects.

Strictly speaking if there are more than one objects with the same behavior
the code should be duplicated. But this would be silly - hence object
classes - groups of objects (data) sharing code.

That is the goal I I am trying to implement in C.

David Kleinecke

unread,
Apr 19, 2015, 12:53:48 PM4/19/15
to
On Sunday, April 19, 2015 at 3:47:56 AM UTC-7, Johannes Bauer wrote:
> On 18.04.2015 19:26, David Kleinecke wrote:
>
> > Suppose I want to use foobar objects. To do this I declare
> >
> > typedef struct {...} FooBar;
> > void* foobar (int m, void* o, void* d);
> >
> > where m is a method number (perhaps defined by an enum), o
> > is variable of type FooBar and d is additional data.
>
> [...]
>
> > FooBar* fb = foobar (0, 0, 0);
>
> [...]
>
> > Reactions ?
>
> You're trying to poorly emulate functionality that C doesn't provide. In
> a way that is neither simple nor clear to anyone not familiar with your
> style of OO. I've never seen any "OO-clone" seen in the way that you do
> it and would be considerably confused if I stumbled about such
> constructs in an unknown codebase.

I didn't invent cloning. It's a known technique for creating things.

> And I'm not even starting with the whole type safety issue with all your
> void pointers. It seems like a horrible beast to maintain where the
> disadvantages far outweigh the advantages.

How hard would you find GTK+ to maintain?
Message has been deleted

David Kleinecke

unread,
Apr 19, 2015, 1:13:12 PM4/19/15
to
On Saturday, April 18, 2015 at 11:04:31 AM UTC-7, Stefan Ram wrote:

> Imagine a procedural language. Imagine it has a function
> »printf« that you want to extend for a new type. You can't!
> »printf« is a procedure in a library. You can't modify
> procedures in libraries.
>
> In an OOP language, you can add a class T with its own
> »printf extension«. Let c be an object of the class C from
> the standard library. Then, you can send the printf message
> to it in order to print it: in pseudocode:
>
> In prozedural programming, you can't add to a procedure
> without opening and changing it (often one cannot change
> procedures from libraries at all).

In my world the library is an object class and "printf" is one of its
methods. There really isn't a concept of "extending printf".

I going to make some assumptions to be able to reply to your example.

I have a class of objects called A that do something. Then I create a new
class called B that inherits from A. The method codes of B include all
the message codes of A. The function B responds to most calls by simply
calling A with same arguments. But it can replace that methods code if
that is useful. Simple inheritance. Is that what you mean.

I can do multiple inheritance the same way but I can't preserve the code
numbers in more than one parent. If I thought multiple inheritance was
important I could replace the int m by a name (that is a void*) and give
different names to all the different methods.

David Kleinecke

unread,
Apr 19, 2015, 1:14:58 PM4/19/15
to
You answered your own question about enums. If you don't want to
remember 0 means CLONE use an enum. I find it just as easy to put the
code table in the comments.

Johannes Bauer

unread,
Apr 19, 2015, 1:19:33 PM4/19/15
to
On 19.04.2015 18:53, David Kleinecke wrote:

>> You're trying to poorly emulate functionality that C doesn't provide. In
>> a way that is neither simple nor clear to anyone not familiar with your
>> style of OO. I've never seen any "OO-clone" seen in the way that you do
>> it and would be considerably confused if I stumbled about such
>> constructs in an unknown codebase.
>
> I didn't invent cloning. It's a known technique for creating things.

I'm not talking about cloning as a concept within OO, I was talking
about your construct being OOoid (i.e. a "clone" of OO concepts).

>> And I'm not even starting with the whole type safety issue with all your
>> void pointers. It seems like a horrible beast to maintain where the
>> disadvantages far outweigh the advantages.
>
> How hard would you find GTK+ to maintain?

I don't know, never looked at the codebase. But judging from the fact
that they did rewrite the thing completely in GTK 3, I'm guessing at
least the original maintainers found it *very* hard to maintain.

Johannes Bauer

unread,
Apr 19, 2015, 2:52:30 PM4/19/15
to
On 19.04.2015 18:27, David Kleinecke wrote:

> I would rather code in assembly language but I realize, for all the usual
> reasons, that that is not currently realistic. Perhaps some day when we all
> agree on a single hardware design.

Are you joking or actually mean what you say? You invent a (poor) OO
layer on top of C to *increase* abstaction, but what you actually would
want is to code in assembly, i.e. significantly *decrease* abstraction?
How does that make any sense at all?

And in what phantasy world do you live in that you think we will all
"agree" on a single hardware design somewhen in the future?

> In my code I further simplify by never using "for" or "do .. while".

What? You sound like a horrible programmer. I'm dead serious.

You avoid the most basic language constructs because you want to
"simplify"? Do you realize that this will create an enormous burden on
everyone who reads your code? Everyone who just expects certain problems
to be solved in a quasi-standard approach?

Sure, no need for fors or do/whiles. None at all, not necessary to have
a turing complete language. But that's a damn weird subset of C that
you're using.

> And
> I never put more than one statement on a line.

This is the only thing in your entire post that sounds sensible.

> Bottom line: I don't object to the changes in the 1990 standard - I just
> feel no need for them.

Bottom line: I've saved a bullet here that I will shoot in my head if I
ever end up in a position where I need to maintain your code.

Keith Thompson

unread,
Apr 19, 2015, 4:54:54 PM4/19/15
to
David Kleinecke <dklei...@gmail.com> writes:
[...]
> I would rather code in assembly language but I realize, for all the usual
> reasons, that that is not currently realistic. Perhaps some day when we all
> agree on a single hardware design.
[...]

Your preferences differ substantially from mine.

(Johannes Bauer: See, this is how you disagree with someone without
being insulting.)

BGB

unread,
Apr 19, 2015, 5:05:05 PM4/19/15
to
On 4/19/2015 1:52 PM, Johannes Bauer wrote:
> On 19.04.2015 18:27, David Kleinecke wrote:
>
>> I would rather code in assembly language but I realize, for all the usual
>> reasons, that that is not currently realistic. Perhaps some day when we all
>> agree on a single hardware design.
>
> Are you joking or actually mean what you say? You invent a (poor) OO
> layer on top of C to *increase* abstaction, but what you actually would
> want is to code in assembly, i.e. significantly *decrease* abstraction?
> How does that make any sense at all?
>
> And in what phantasy world do you live in that you think we will all
> "agree" on a single hardware design somewhen in the future?
>

... turns into an epic battle between various embedded ISAs, and also
contenders like x86.


eventual winner ends up being a 64-bit extended version of the 6502,
which is in effect just a normal 8-bit 6502 with an ability to map part
of its address space to an larger external cache, which may in turn be
configured to map piecewise into an external 64-bit address space.

this in turn having been a compromise between the desktop PC people
wanting a 64-bit address space, and the small embedded people not
wanting processor cores which cost them more than $0.05 per unit...


big conflicts regularly turn up as to the exact contents of the "System
ROM", as its small size means only certain functionality can be
included, and its specific contents and layout are critical for the
correct operation of code produced by application-level C compilers
(which end up needing to rely on its services for things like addressing
memory, making function calls, or performing arithmetic operations).


>> In my code I further simplify by never using "for" or "do .. while".
>
> What? You sound like a horrible programmer. I'm dead serious.
>
> You avoid the most basic language constructs because you want to
> "simplify"? Do you realize that this will create an enormous burden on
> everyone who reads your code? Everyone who just expects certain problems
> to be solved in a quasi-standard approach?
>
> Sure, no need for fors or do/whiles. None at all, not necessary to have
> a turing complete language. But that's a damn weird subset of C that
> you're using.
>

though, FWIW, "do ... while()" is often less commonly used IME than
"for()" or the normal "while()".


>> And
>> I never put more than one statement on a line.
>
> This is the only thing in your entire post that sounds sensible.
>

... but, why not write code paragraph style? the word-wrap setting feels
so alone and neglected. ...



>> Bottom line: I don't object to the changes in the 1990 standard - I just
>> feel no need for them.
>
> Bottom line: I've saved a bullet here that I will shoot in my head if I
> ever end up in a position where I need to maintain your code.
>


FWIW, in my case I mostly stick with C90, but mostly it is because I
compile a lot of my code using MSVC.

though, they do at least support '//' and 'long long'.

Johannes Bauer

unread,
Apr 19, 2015, 5:05:11 PM4/19/15
to
On 19.04.2015 22:54, Keith Thompson wrote:

> Your preferences differ substantially from mine.
>
> (Johannes Bauer: See, this is how you disagree with someone without
> being insulting.)

Your opinion on what constitutes an insult differs substantially from mine.

Telling someone they sound like a horrible programmer is not an insult,
it is just the harsh truth. To me, David sounds like someone with an
incredible distortion in perceiving what constitutes good code. I would
expect people to believe the same of my programming skills if I'd
blatantly ignore tons of best practices that have crystallized over the
years.

And while it is your prerogative to sugarcoat the worst of programming
practices, I choose to not sugarcoat. There's a significant difference
to an insult, mind you.

Richard Bos

unread,
Apr 20, 2015, 6:26:03 AM4/20/15
to
David Kleinecke <dklei...@gmail.com> wrote:

> You answered your own question about enums. If you don't want to
> remember 0 means CLONE use an enum. I find it just as easy to put the
> code table in the comments.

What, every time? If you only use CLONE in one single line, yes, I'd
agree with you. But usually, you use such constants many times. In those
cases, I'd much rather use an enum (or #define, casu quo) with a
significant name, so I don't have to either remember what a meaningless
numerical constant stands for, _or_ add a comment for at least every
single source file.

Richard

David Kleinecke

unread,
Apr 20, 2015, 1:37:25 PM4/20/15
to
On Sunday, April 19, 2015 at 10:06:22 AM UTC-7, Stefan Ram wrote:
> David Kleinecke <dklei...@gmail.com> writes:
> >I didn't get my personal understanding from AK. Rather I deduced it from
> >the structure of SmallTalk. To me object-orientation means:
> >objects exchanging information by way of messages
> >with the implicit assumption that hat is all there is. It seems entirely
> >disjoint from question like binding.
>
> In this case, the binding can become part of the definition
> of »exchanging information by way of messages«.
>
> It is crucial that in OOP the programmer does NOT send a
> message to a specific object (that is known at compile time).
>
> Rather a message is sent »to an expression« first and foremost.
> The value of the expression than determines at runtime which
> object actually receives the message.
>
> This is not always made clear enough. For example, I am not
> aware of a means to illustrate this via UML, UML seems to
> assume or suggest that messages are sent directly to objects.

I see what you are saying as a possible point of view. Except I don't
think that is a generally accepted idea. As you note UML does not make
it. I would take the position you are over-specifying OOP.

In an event-oriented environment an object might simply emit events -
this may be what you are referring to. This is easy to implement with
my groups. All the object send events to an event-handler that passes
them on to objects. Object can subscribe to events or unsubscribe. Not
all processes are event-oriented.

David Kleinecke

unread,
Apr 20, 2015, 1:41:44 PM4/20/15
to
On Sunday, April 19, 2015 at 10:06:22 AM UTC-7, Stefan Ram wrote:

> You surely are aware that there already are several tutorials
> and libraries for »OOP in C«.

I developed my ideas because I was unsatisfied with the OOP model used
in GTK+ et al. None of the other models I have seen are IMO as simple as
the one I ended up with.

David Kleinecke

unread,
Apr 20, 2015, 1:50:47 PM4/20/15
to
In the long run innovation in computing will cease. We will all agree on
all the acceptable ways to do everything. Programming will be like Lego -
put together standard blocks. All the hardware will have the same
architecture. A handful of researchers will continue to scan the standard
codes and grub out the remaining bugs (I am not optimistic that we will
ever get our software bug-free).

I hope that by this time all the code I wrote for actual applications has
been retired and you will not be forced to maintain it. These days I code
for amusement and the grasp of general principles.


David Kleinecke

unread,
Apr 20, 2015, 1:53:48 PM4/20/15
to
You don't bother me. I've met lots of opinionated people.

David Kleinecke

unread,
Apr 20, 2015, 1:58:38 PM4/20/15
to
It's de minimus to me. I said optional - I mean optional. Do whatever feels
good. Aside to another commentator - there is no such thing as "best
practices".

A lot depends on how your programming environment is arranged.

Chris M. Thomasson

unread,
Apr 20, 2015, 3:50:20 PM4/20/15
to
> "David Kleinecke" wrote in message
> news:14aac167-b724-458d...@googlegroups.com...

> After all these years uncertainty still reigns about what
> object-oriented computing is all about. I have my own opinions
> and this groups seems like a good place to present my techniques
> and, perhaps, get some useful feedback.

[...]

Perhaps this just might be of interest to you:

https://groups.google.com/forum/#!msg/comp.lang.c/J0aNYQB_pXk/mwKkHAVX-GwJ

FWIW, I posted as SenderX a long time ago...

;^o

Johannes Bauer

unread,
Apr 20, 2015, 4:47:52 PM4/20/15
to
On 20.04.2015 19:53, David Kleinecke wrote:

> You don't bother me. I've met lots of opinionated people.

Yeah, I didn't really expect you to adress any issues that I raised.
People who think they have created a marvel by reinventing the wheel in
a crappy way usually aren't impressed by objections.

Happy coding,

Johannes Bauer

unread,
Apr 20, 2015, 4:49:29 PM4/20/15
to
On 20.04.2015 19:50, David Kleinecke wrote:

> I hope that by this time all the code I wrote for actual applications has
> been retired and you will not be forced to maintain it.

Eventually one thing that we both can strongly agree on.

Tim Rentsch

unread,
Apr 22, 2015, 2:45:06 PM4/22/15
to
r...@zedat.fu-berlin.de (Stefan Ram) writes:
> David Kleinecke <dklei...@gmail.com> writes:
>> After all these years uncertainty still reigns about what
>> object-oriented computing is all about.
>
> The term was coined by Alan Key in 1967.
>
> In 2003, I asked him what it means to him. He kindly
> replied:
>
> >>OOP to me means only messaging, local retention and
> protection and hiding of state-process, and extreme
> late-binding of all things.<<
>
> http://www.purl.org/stefan_ram/pub/doc_kay_oop_en
>
> AFAIK, this is the first and only time Alan Kay has
> defined this concept. He already gave explanations for
> the word >>object<< before, but not for >>object-oriented
> programming<<.
>
>> Some things people insist on - like polymorphism - seem to
>> me perhaps useful but easy to get along without.
>
> Alan Kay make polymorphism an essential part of the
> definition, run-time polymorphism, that is, >>extreme
> late-binding of all things<<, as he calls it.
>
> Inheritance is just a means to get polymorphism with
> static type-safety, but one can get polymorphism by other
> means like duck typing without inheritance. Alan Kay did
> not mention classes or inheritance in his definition.
>
> But one needs ADT objects as a foundation. So, to me,
> interpreting the words of Alan Kay, OOP = ADT + polymorphism.
> [snip]

If you can find a copy of the Sep 1982 issue of SIGPLAN
Notices, and read the article in that on object oriented
programming, I believe you will find a quote by Alan Kay
that bears directly on the relationship of OOP and ADT.

And, if you don't mind my saying so, I think the paper
also makes a good read otherwise.

Tim Rentsch

unread,
Apr 22, 2015, 2:50:48 PM4/22/15
to
David Kleinecke <dklei...@gmail.com> writes:

> On Saturday, April 18, 2015 at 11:04:31 AM UTC-7, Stefan Ram wrote:
>> David Kleinecke <dklei...@gmail.com> writes:
>>> After all these years uncertainty still reigns about what
>>> object-oriented computing is all about.
>>
>> The term was coined by Alan Key in 1967.
>>
>> In 2003, I asked him what it means to him. He kindly replied:
>>
>> >>OOP to me means only messaging, local retention and
>> protection and hiding of state-process, and extreme
>> late-binding of all things.<<
>>
>> http://www.purl.org/stefan_ram/pub/doc_kay_oop_en
>
> I didn't get my personal understanding from AK. Rather I deduced
> it from the structure of SmallTalk. To me object-orientation
> means:
>
> objects exchanging information by way of messages
>
> with the implicit assumption that hat is all there is. It seems
> entirely disjoint from question like binding. [snip]

Let me suggest that you have missed an important point.
Late binding is an essential characteristic - perhaps even
the /most/ essential characteristic - of both Smalltalk and
object-oriented programming.

David Kleinecke

unread,
Apr 23, 2015, 1:02:31 PM4/23/15
to
First I must admit I don't see much use for late binding. Perhaps you
could explain to me why people think it is important and what it does
that a framework like mine can't do.

But the real problem is that the term object-oriented programming is,
nowadays, not exactly what Key thought it was. Suppose I just start
with the framework I described and I want to give it a name. Suppose I
decide to call it "hot-shot programming" then everybody would laugh at
me ad say "You've only re-invented a version of OOP".

If I were to replace the method number argument in a class function
by a method name argument would that be late enough binding for you?

Chad

unread,
Apr 23, 2015, 2:13:06 PM4/23/15
to
I don't know about C, but thanks to late binding in Java, I can implicitly call to string() without explicitly typing it out.

Chad

unread,
Apr 23, 2015, 3:11:24 PM4/23/15
to
Er call *toString()*

Chris M. Thomasson

unread,
Apr 23, 2015, 7:14:12 PM4/23/15
to
> "Chris M. Thomasson" wrote in message
> news:mh3l9e$dau$1...@speranza.aioe.org...

> > "David Kleinecke" wrote in message
> > news:14aac167-b724-458d...@googlegroups.com...

> > After all these years uncertainty still reigns about what
> > object-oriented computing is all about. I have my own opinions
> > and this groups seems like a good place to present my techniques
> > and, perhaps, get some useful feedback.

> [...]

> Perhaps this just might be of interest to you:

[...]

Here is a decent method to get some object orientation in C:

http://pastebin.com/f52a443b1

;^D

David Kleinecke

unread,
Apr 24, 2015, 12:58:37 PM4/24/15
to
I am inclined to object that this makes things more complicated rather than
simpler. But without any context I cannot guess just exactly what you were
trying to do.

Chris M. Thomasson

unread,
Apr 24, 2015, 10:15:10 PM4/24/15
to
> "David Kleinecke" wrote in message
> news:8187843c-d4e6-47a8...@googlegroups.com...

> > On Thursday, April 23, 2015 at 4:14:12 PM UTC-7, Chris M. Thomasson
> > wrote:
[...]
> > Here is a decent method to get some object orientation in C:
> >
> > http://pastebin.com/f52a443b1

> I am inclined to object that this makes things more complicated rather
> than
> simpler.

Well, yes. It does make some things more complicated. Perhaps just one of
the
"side effect's" of trying to emulate OOP in C.


> But without any context I cannot guess just exactly what you were
> trying to do.

FWIW, I was trying to get a standard generic interface for testing different
multi-threaded synchronization algorithms. The example used in the PasteBin
code is in the context of a device driver, but the "OOP'ish" technique is
the
same. Take a look at the following functions from the pastebin link:
_______________________________________________________
void read_write(
struct device* const self
) {
char buf[100];

device_read(self, buf, 50);

device_write(self, buf, 5);
}


int main(void) {
struct device* a_device;

if (! usb_drive_create(&a_device)) {
read_write(a_device);

object_destroy(a_device);
}

return 0;
}
_______________________________________________________

The functions `device_read/write' operate on "device" objects and
`object_destroy'
works on the "base class interface" that every object inherits. This setup
can help
make the testing of a set of algorithms easier, by using a single unified
generic
interface.

Sound Kosher?

;^o

Tim Rentsch

unread,
Apr 25, 2015, 5:31:12 AM4/25/15
to
David Kleinecke <dklei...@gmail.com> writes:

> On Wednesday, April 22, 2015 at 11:50:48 AM UTC-7, Tim Rentsch wrote:
>> David Kleinecke <dklei...@gmail.com> writes:
>>
>>> On Saturday, April 18, 2015 at 11:04:31 AM UTC-7, Stefan Ram wrote:
>>>> David Kleinecke <dklei...@gmail.com> writes:
>>>>> After all these years uncertainty still reigns about what
>>>>> object-oriented computing is all about.
>>>>
>>>> The term was coined by Alan Key in 1967.
>>>>
>>>> In 2003, I asked him what it means to him. He kindly replied:
>>>>
>>>> >>OOP to me means only messaging, local retention and
>>>> protection and hiding of state-process, and extreme
>>>> late-binding of all things.<<
>>>>
>>>> http://www.purl.org/stefan_ram/pub/doc_kay_oop_en
>>>
>>> I didn't get my personal understanding from AK. Rather I deduced
>>> it from the structure of SmallTalk. To me object-orientation
>>> means:
>>>
>>> objects exchanging information by way of messages
>>>
>>> with the implicit assumption that hat is all there is. It seems
>>> entirely disjoint from question like binding. [snip]
>>
>> Let me suggest that you have missed an important point.
>> Late binding is an essential characteristic - perhaps even
>> the /most/ essential characteristic - of both Smalltalk and
>> object-oriented programming.

[quoted material below has been reordered]

> But the real problem is that the term object-oriented programming
> is, nowadays, not exactly what Key thought it was. [snip]

I don't want to get bogged down in a debate about terminology,
which would just distract us from getting to the key underlying
issues. Probably it won't come up, but in case it does I will
be using the term "object oriented programming" in the sense
that Alan Kay originally meant it.

> First I must admit I don't see much use for late binding. Perhaps
> you could explain to me why people think it is important and what
> it does that a framework like mine can't do.
>
> If I were to replace the method number argument in a class
> function by a method name argument would that be late enough
> binding for you?

Let me come at this from a rather different direction.
Suppose we have a more-or-less ordinary C module, by which I
mean a .h/.c pair of files, with the .h file giving the
interface, and the .c file giving the implementation.
Furthermore the module in question provides an abstract type

typedef struct foo_xyzzy_whatever Foo;

and a set of functions along the lines of the following

R1 *foo_x( Foo *, P1 * );

R2 *foo_y( Foo *, P2 * );

R3 *foo_z( Foo *, P3 * );

...

Then suppose we come along afterwards and define a function
f() along the lines you described in your first message,
implemented as follows:

void *
f( int n, void *o, void *a ){
switch( n ){
case 1: return foo_x( o, a );
case 2: return foo_y( o, a );
case 3: return foo_z( o, a );
...
default: ... whatever is appropriate here ...
}
}

To me this looks like a way of supplying a "class" as
you use the term in your first message. Is that wrong?
If so then what your idea is needs to be described
more clearly. But assuming that it is right, let
me proceed.

We now ask the question, What did we gain by writing
function f()? The answer is: not much. It looks like
we could simply leave out the function f() and everything
would be simpler and easier (ie, by calling functions
directly), not counting having to cast to (void *) here
and there to make things okay type-wise.

If the above is consistent with the scheme you are proposing,
then what advantage does it offer over just calling functions
directly? Conversely, if the above is not consistent with
what you're proposing, then you should try to explain your
idea in more detail so the distinctions are more apparent.

David Kleinecke

unread,
Apr 25, 2015, 1:19:56 PM4/25/15
to
Your understanding of the mechanics of the scheme I described is correct.

The advantage I obtain is that ALL the uses of objects of type Foo
are in one place. In a complex situation - I started with GTK+ - this
can be a valuable organizing feature.

It should also be valuable if I were to update the Foo class because I
can easily see what my changes would effect.

On a more utopian note I could use this approach in development by writing
each "class" specification and implementing each "class" separately.

In general I would allow other code to read Foo objects but not write to
them. A purist, I assume, would insist that all the gets must be methods -
but that seems pedantic to me.

luser droog

unread,
Apr 25, 2015, 4:37:27 PM4/25/15
to
The advantage would depend on how it were applied (I know you
know this). Rather than call it a class, it looks more to me
like an integer-encoding of the function selection, AKA an
opcode.

The advantages of a opcode representation of a function are
myriad in the areas of storage, marshaling, transmission, etc.

In the APL interpreter I'm currently hacking at, I do this
a lot. But I would go much further than the above code, to
make the encoding more explicit and a little more abstract.

#define FOOTAB(_) \
_(R1, foo_x, Foo*, P1*, foo_x(o,a))\
_(R2, foo_y, Foo*, P2*, foo_y(o,a))\
_(R3, foo_z, Foo*, P3*, foo_z(o,a))
#define FOO_PROT(r, f, p1, p2, b) \
r * f ( p1, p2 ) ;
FOOTAB(FOO_PROT)
#define FOO_OPS(r, f, p1, p2, b) \
OP_ ## f
enum opcodes { FOOTAB(FOO_OPS) };

#define FOO_CASE(r, f, p1, p2, b) \
case FOO_OPS(r, f, p1, p2, b): return b;

void *
f( int n, void *o, void *a ){
switch( n ){
FOOTAB(FOO_CASE) ...
default: ... whatever is appropriate here ...
}
}

This way everything that depends on the integer
encoding is packed into the same X-macro table.
And we can extract any pieces and generate whatever
code we want directly from the table members.

Calls to f can use the generated enum names instead
of the (zero-semantic) integers.

f(OP_foo_x, left, right);

Chris M. Thomasson

unread,
Apr 26, 2015, 1:14:10 AM4/26/15
to
"David Kleinecke" wrote in message
news:8187843c-d4e6-47a8...@googlegroups.com...

> > On Thursday, April 23, 2015 at 4:14:12 PM UTC-7, Chris M. Thomasson
> > wrote:
[...]
> > Here is a decent method to get some object orientation in C:
> >
> > http://pastebin.com/f52a443b1

> I am inclined to object that this makes things more complicated rather
> than
> simpler. But without any context I cannot guess just exactly what you were
> trying to do.

Here is something that is kind of closer to what you described in your OP:

http://codepad.org/SJvF3Y3v


Tim Rentsch

unread,
Apr 26, 2015, 10:13:30 AM4/26/15
to
> know this). [snip]

Yes, certainly there might be various advantages
considered in other situations. My question is what
is the advantage in this situation, ie, when f() is
called just in regular code, not as part of some
interpreter or some automatic code generation
component.

David Kleinecke

unread,
Apr 26, 2015, 11:07:35 AM4/26/15
to
There are several similar macro based ways of writing things. I have
explored schemes like this and decided they hide too much in the macros.
Once again I prefer the simple way.

David Kleinecke

unread,
Apr 26, 2015, 11:40:35 AM4/26/15
to
I wonder whether there is such a thing as "regular code". If it exists
at all I think it is within the methods of one of my classes. And within
a class I would write everything that way. For example, if I wanted to
reuse one of the external methods internally I would write the response
to the external method as a subroutine that could be called internally.

I assume the class source code is all in a single .c unit and there is
nothing else in that unit except any hidden parts of the object. The object
struct is in the .h header.

In a complex situation like GTK+ I find methods for what I imagine to be
object types scattered all over. The problems of following the logic get
very difficult. Perhaps these problems could be documented away but even
if they could I doubt that that documentation will ever be written. It
seems better, to me, to use better code structure instead.

Generally I would formulate most APIs as classes - usually one class often
with one object.

David Kleinecke

unread,
Apr 26, 2015, 11:41:00 AM4/26/15
to
As nearly as I can tell you are trying to do what I did in exactly the way
I am trying to avoid.

Perhaps you could try writing that application according to my scheme.

Tim Rentsch

unread,
Apr 26, 2015, 11:45:57 AM4/26/15
to
Okay. That being so I would say your scheme is more like a way
of providing abstract data types than it is of doing OOP.


> The advantage I obtain is that ALL the uses of objects of type Foo
> are in one place. In a complex situation - I started with GTK+ -
> this can be a valuable organizing feature.

Sure, but that is just as true if the foo_*() functions are used
directly under the abstract type scheme I described. I don't see
any additional benefit to having the f() function(s).

Oh, I should say what I mean by an abstract type. In C we get
abstract types by declaring a struct type, but not defining its
contents, in a header file, and using that type in all client
functions (more accurately, a pointer to the struct type):

typedef struct foo_contents_s Foo;

Foo *create_Foo( ... parameters ... );

Foo *clone_Foo( Foo * );

...

and then defining the contents in the corresponding .c file:

#include "whatever-the-header-was.h"
struct foo_contents {
...
};

Using this scheme there is no way for clients of the "Foo class"
to get at its representation at all. They have to go through
the declared function interface, which guarantees that all code
that deals with the internals is in the .c file.


> It should also be valuable if I were to update the Foo class
> because I can easily see what my changes would effect.

Ditto my last comment.


> On a more utopian note I could use this approach in development by
> writing each "class" specification and implementing each "class"
> separately.

I recommend doing just that, with the interface in a .h file,
and the implementation in the corresponding .c file.


> In general I would allow other code to read Foo objects but not
> write to them. A purist, I assume, would insist that all the gets
> must be methods - but that seems pedantic to me.

There is a very pragmatic reason (in C) to insist that all
accesses, including read access, go through the client function
interface. Namely, once the struct representation is known to
client code (ie, it is put in a .h file), there is no way to
enforce a "no write" policy. Conversely, if the struct is
only declared in the .h file, but defined in the .c file, then
there is no way to get at its members (either for writing or for
reading). (Of course the bytes can be stomped using memcpy()
or similar, but that is much less convenient so is much less
likely to be a problem.) There are principled reasons as
well, not just dogmatic (or "pedantic") ones, for restricting
all access to go through the client function interface, but
the most important one is the stronger guarantees obtained
by putting definitions of struct contents only in .c files,
not in .h files.

David Kleinecke

unread,
Apr 27, 2015, 11:38:10 AM4/27/15
to
On Sunday, April 26, 2015 at 8:45:57 AM UTC-7, Tim Rentsch wrote:
>
> There is a very pragmatic reason (in C) to insist that all
> accesses, including read access, go through the client function
> interface. Namely, once the struct representation is known to
> client code (ie, it is put in a .h file), there is no way to
> enforce a "no write" policy. Conversely, if the struct is
> only declared in the .h file, but defined in the .c file, then
> there is no way to get at its members (either for writing or for
> reading). (Of course the bytes can be stomped using memcpy()
> or similar, but that is much less convenient so is much less
> likely to be a problem.) There are principled reasons as
> well, not just dogmatic (or "pedantic") ones, for restricting
> all access to go through the client function interface, but
> the most important one is the stronger guarantees obtained
> by putting definitions of struct contents only in .c files,
> not in .h files.

There is actually a fundamental question at stake here. Just how much
policy should be enforced. I belong to the school (which I believe is
a minority position) where the language allows the programmer to do
most anything (including, as in the old joke, shoot himself in the
foot) and depends on the programmer's discipline to keep things correct.

I think the current notions of the language protecting the data are a
consequence of trying to manage large projects with multiple programmers.
This goes a long way in another direction - to the question of how to
integrate the work of many programmers - but I think questions like data
access are part of that problem and not part of the original C program.


Tim Rentsch

unread,
Apr 28, 2015, 11:23:33 AM4/28/15
to
What I meant is code written using an ordinary general
development process, not one tailored to a highly
specialized context.

Tim Rentsch

unread,
Apr 28, 2015, 11:27:36 AM4/28/15
to
Any question about what the language should or should not allow
is irrelevant to what I was saying. My comments are not about
language design decisions, but about program organization design
decisions.

David Kleinecke

unread,
Apr 28, 2015, 11:59:15 AM4/28/15
to
I don't know what your goals are, but mine are the ones Knuth stated
in his literate programming initiative. Knuth's goals - not his
implementation. A program should be easy to read and understand.

This goal seems to conflict with C in some ways. But I am reluctant
to invent a new language when such an attractive alternative already
exists. You may well be right that object-orientation (any version)
is not the answer. But some organizing principle is surely needed.

My OOP scheme is little more than what is already the usual practice -
An object class is a translation unit (.c file) and a header (.h) file
is its interface. I am just trying to tighten up some details.
the b

Tim Rentsch

unread,
Apr 30, 2015, 10:32:22 AM4/30/15
to
That's fine, but the concerns of literate programming are
orthogonal to the concerns of object-oriented programming.
They don't really have anything to do with each other.

> This goal seems to conflict with C in some ways. But I am reluctant
> to invent a new language when such an attractive alternative already
> exists. You may well be right that object-orientation (any version)
> is not the answer. But some organizing principle is surely needed.

Note that I haven't said anything about whether OOP will be
helpful for what you're trying to do. What I have done is tried
to explain (partly) what is essential to the notion of OOP, and
compare the scheme you outlined to OOP generally. I haven't
offered any opinion about whether your scheme, or OOP, is going
to be helpful to you or what you hope to accomplish.

> My OOP scheme is little more than what is already the usual practice
> - An object class is a translation unit (.c file) and a header (.h)
> file is its interface. I am just trying to tighten up some details.
> the b

Again I would say that what you have described appears to be
closer to abstract data types than it is to OOP. And that may be
fine for what you want to do. Given what you have said about the
approach you're taking, I would suggest you stick to normal C
programming practices, with struct types declared in .h files and
then have their contents defined in .c files, which I think will
provide enough organizational framework to do what you want to
do. (It may also be that a naming convention for functions in
the different modules is an important part of that.) But that's
just my impression, there may be other considerations I don't
know about that weigh in favor of a different course.

David Kleinecke

unread,
Apr 30, 2015, 12:19:48 PM4/30/15
to
Roughly I think the bottom line of what you have been saying is -
why bother?

That's where the goal of literate programming comes in - because I
want to make the program easy to understand.

I started from GTK+. Yesterday I was trying to read Cairo. Perhaps I
am naively optimistic but - there's got to be a better way.

I think my OOP scheme helps a little.

Tim Rentsch

unread,
May 1, 2015, 7:21:20 AM5/1/15
to
Not exactly. What I am saying is the scheme you describe clearly
has some associated costs, but AFAICT doesn't seem to provide any
benefits, and thus I don't understand what you think using this
scheme is buying you, relative to a more straightforward one.

> That's where the goal of literate programming comes in - because I
> want to make the program easy to understand.

I still don't see what (you think) one has to do with the other.

> I started from GTK+. Yesterday I was trying to read Cairo.
> Perhaps I am naively optimistic but - there's got to be a better
> way.

Hmmm. Maybe something related to these explains why you
have come up with the scheme that you have. How has the
structure of GTK+ influenced the scheme you have described?

> I think my OOP scheme helps a little.

I understand that you think it does, but I don't understand what
advantages it gives you compared to more typical C usage. What
can you do using your scheme that you think would be difficult
or (in practical terms) impossible under a usual C way of doing
something similar (eg, the approach I outlined upthread)?

David Kleinecke

unread,
May 1, 2015, 1:37:13 PM5/1/15
to
I am trying to make the code easy to understand. I am not attempting
to do anything new or solve any problems.

In GTK+ and related collections of code each pair of matched .c file
and .h file usually is focused on one type of object (generic - not
mine). It is easy imagine the pair as a class (in my sense) and the
type as an instance of the type. It is practically handed to you. At
the bottom line, all I am proposing is changing a collection of
functions
foo_method (Foo_object, .... ) {...}
into a single function
foo (Foo object, Foo_method_number m, ....) {...}

Some of this is optional. I use integer method numbers here because the
usual thing would be to name the methods and convert the names into
numbers by an enum. I could just as well use overt names (char*) but
recognition would be slower.

Much of this is my response to discovering situations where a type foo
object is some other code but the method used is not the foo.c file,
rather it is off in some third .c file and finding it is not obvious.
Even with the aid of an index it is off-putting.

So I think it is important to
1. Recognize the implicit class system
2. Collect all the methods of a type in one place.

PS: Cairo, in my opinion, is a worse mess than GTK+

Tim Rentsch

unread,
May 3, 2015, 1:54:56 PM5/3/15
to
Okay, I think I understand your motivation now. You think GTK+
is a bit of a mess, and you want to put an organizing layer on
top of it, to make it easier for you to understand and use. Is
that right?

Assuming it is, I still don't understand what you think using a
single foo() function buys you, relative to a more conventional
scheme where the organizing layer has different functions for
each represented entry point in GTK+. Let me be clear about
this, it may not be important for me to understand that, but
I thought you would be interested to hear the fact even if
you end up deciding to let the matter drop. That make sense?

Chris M. Thomasson

unread,
May 3, 2015, 3:19:53 PM5/3/15
to
> "Tim Rentsch" wrote in message
> news:kfn8ud5...@x-alumni2.alumni.caltech.edu...

> > David Kleinecke <dklei...@gmail.com> writes:
[...]
> Okay, I think I understand your motivation now. You think GTK+
> is a bit of a mess, and you want to put an organizing layer on
> top of it, to make it easier for you to understand and use. Is
> that right?

I tried to give a decent answer here:

https://groups.google.com/d/msg/comp.lang.C/JNl51a9nibs/6JpH6dKO6gUJ

Well, he basically said okay, but no cigar!

Epic Fail.

;^(


> Assuming it is, I still don't understand what you think using a
> single foo() function buys you, relative to a more conventional
> scheme where the organizing layer has different functions for
> each represented entry point in GTK+. Let me be clear about
> this, it may not be important for me to understand that, but
> I thought you would be interested to hear the fact even if
> you end up deciding to let the matter drop. That make sense?

FWIW, that paragraph makes sense to me.

Tim Rentsch

unread,
May 3, 2015, 4:45:14 PM5/3/15
to
"Chris M. Thomasson" <cri...@charter.net> writes:

>> "Tim Rentsch" wrote in message
>> news:kfn8ud5...@x-alumni2.alumni.caltech.edu...
>>
>>> David Kleinecke <dklei...@gmail.com> writes:
>
> [...]
>
>> Okay, I think I understand your motivation now. You think GTK+
>> is a bit of a mess, and you want to put an organizing layer on
>> top of it, to make it easier for you to understand and use. Is
>> that right?
>
> I tried to give a decent answer here:
>
> https://groups.google.com/d/msg/comp.lang.C/JNl51a9nibs/6JpH6dKO6gUJ
>
> [snip]

Yes, I saw that. I haven't yet been able to see the page
you referenced (ie, at codepad.org), so I don't know what
your suggested alternative is.

>> Assuming it is, I still don't understand what you think using a
>> single foo() function buys you, relative to a more conventional
>> scheme where the organizing layer has different functions for
>> each represented entry point in GTK+. Let me be clear about
>> this, it may not be important for me to understand that, but
>> I thought you would be interested to hear the fact even if
>> you end up deciding to let the matter drop. That make sense?
>
> FWIW, that paragraph makes sense to me.

Thank you for that, I was beginning to wonder if I was
going insane.

Chris M. Thomasson

unread,
May 3, 2015, 4:58:48 PM5/3/15
to
> "Tim Rentsch" wrote in message
> news:kfn6189...@x-alumni2.alumni.caltech.edu...
> > "Chris M. Thomasson" <cri...@charter.net> writes:

[...]
> > I tried to give a decent answer here:
> >
> > https://groups.google.com/d/msg/comp.lang.C/JNl51a9nibs/6JpH6dKO6gUJ

> Yes, I saw that. I haven't yet been able to see the page
> you referenced (ie, at codepad.org), so I don't know what
> your suggested alternative is.

FWIW, I actually made two tries that are unaccepatble to David (OP).

I will post one here, and another in a followup post...

Well, barring any formatting issues, here is try 1:
______________________________________________________________


/* Interfaces
____________________________________________________________________*/
#include <stddef.h>


struct object_prv_vtable {
int (*fp_destroy) (void* const);
};


struct device_prv_vtable {
int (*fp_read) (void* const, void*, size_t);
int (*fp_write) (void* const, void const*, size_t);
};


struct device_vtable {
struct object_prv_vtable const object;
struct device_prv_vtable const device;
};


struct device {
struct device_vtable const* vtable;
};


#define object_destroy(mp_self) ( \
(mp_self)->vtable->object.fp_destroy((mp_self)) \
)


#define device_read(mp_self, mp_buf, mp_size) ( \
(mp_self)->vtable->device.fp_read((mp_self), (mp_buf), (mp_size)) \
)


#define device_write(mp_self, mp_buf, mp_size) ( \
(mp_self)->vtable->device.fp_write((mp_self), (mp_buf), (mp_size)) \
)






/* Sample Header (usb_drive.h)
____________________________________________________________________*/
#if ! defined(USB_HEADER_H)
#define USB_HEADER_H


extern int usb_drive_create(struct device** const);


#endif







/* Sample Impl (usb_drive.c)
____________________________________________________________________*/
/* #include "usb_drive.c" */
#include <stdio.h>
#include <stdlib.h>


struct usb_drive {
struct device device;
/* whatever */
};


static int usb_drive_object_destroy(void* const);
static int usb_drive_device_read(void* const, void*, size_t);
static int usb_drive_device_write(void* const, void const*, size_t);


static struct device_vtable const g_table = {
{ /* object */
usb_drive_object_destroy
},

{ /* device */
usb_drive_device_read,
usb_drive_device_write
}
};


int usb_drive_create(
struct device** const pself
) {
struct usb_drive* const self = malloc(sizeof(*self));
if (self) {
self->device.vtable = &g_table;
*pself = &self->device;
return 0;
}
return -1;
}


int usb_drive_object_destroy(
void* const self_
) {
struct usb_drive* const self = self_;
printf("usb_drive_object_destroy(%p)\n", (void*)self);
free(self_);
return 0;
}


int usb_drive_device_read(
void* const self_,
void* buf,
size_t size
) {
struct usb_drive* const self = self_;
printf("usb_drive_device_read(%p, %p, %lu)\n",
(void*)self, buf, (unsigned long)size);
return 0;
}


int usb_drive_device_write(
void* const self_,
void const* buf,
size_t size
) {
struct usb_drive* const self = self_;
printf("usb_drive_device_write(%p, %p, %lu)\n",
(void*)self, buf, (unsigned long)size);
return 0;
}







/* Sample Application
____________________________________________________________________*/
void read_write(
struct device* const self
) {
char buf[100];

device_read(self, buf, 50);

device_write(self, buf, 5);
}


int main(void) {
struct device* a_device;

if (! usb_drive_create(&a_device)) {
read_write(a_device);

object_destroy(a_device);
}

return 0;
}


______________________________________________________________

Chris M. Thomasson

unread,
May 3, 2015, 5:02:24 PM5/3/15
to
> "Tim Rentsch" wrote in message
> news:kfn6189...@x-alumni2.alumni.caltech.edu...

> > "Chris M. Thomasson" <cri...@charter.net> writes:
[...]
> Yes, I saw that. I haven't yet been able to see the page
> you referenced (ie, at codepad.org), so I don't know what
> your suggested alternative is.

ARGH!!!!

The code I posted into codepad seems to be unavailable right now.

I am getting a `ERR_CONNECTION_RESET' error in Chrome.

DAMN!

Sorry about that non-sense Tim!

;^o

David Kleinecke

unread,
May 4, 2015, 3:21:18 PM5/4/15
to
There's nothing wrong with this way of doing business. I just think it
is unnecessarily complicated.

One must ask what one's goals are. It is important to me the get all the
methods together in one place.

GTK+ approaches this by, as I have said, equating a translation unit
with a what I called a class. But not making this identification
explicit they add confusion. I don't really care how the class idea
is implemented but I would prefer having it explicit.

Tim Rentsch

unread,
May 5, 2015, 8:56:54 AM5/5/15
to
"Chris M. Thomasson" <cri...@charter.net> writes:

>> "Tim Rentsch" wrote in message
>> news:kfn6189...@x-alumni2.alumni.caltech.edu...
>>> "Chris M. Thomasson" <cri...@charter.net> writes:
>
> [...]
>
>>> I tried to give a decent answer here:
>>>
>>> https://groups.google.com/d/msg/comp.lang.C/JNl51a9nibs/6JpH6dKO6gUJ
>>
>> Yes, I saw that. I haven't yet been able to see the page
>> you referenced (ie, at codepad.org), so I don't know what
>> your suggested alternative is.
>
> FWIW, I actually made two tries that are unaccepatble to David (OP).
>
> I will post one here, and another in a followup post...

Okay, thank you.

> Well, barring any formatting issues, here is try 1: [snip]

A fairly common approach to implementing OOP-in-C is how I might
describe this. So it's reasonable to ask (as I guess you did)
"how about this scheme instead?".

Chris M. Thomasson

unread,
May 6, 2015, 7:09:18 PM5/6/15
to
> "Tim Rentsch" wrote in message
> news:kfnd22f...@x-alumni2.alumni.caltech.edu...

> > "Chris M. Thomasson" <cri...@charter.net> writes:

[...]
> > FWIW, I actually made two tries that are unaccepatble to David (OP).
> >
> > I will post one here, and another in a followup post...

> Okay, thank you.

> > Well, barring any formatting issues, here is try 1:
[...]

> A fairly common approach to implementing OOP-in-C is how I might
> describe this.

It think it is a reasonable question as well Time.

> So it's reasonable to ask (as I guess you did) "how about this
> scheme instead?".

I thought that David perhaps did not approve because it lacked a way to call
a
function from a "unified place" using an index, almost like a syscall
interface
and op codes. Anyway, codepad seems to be back up and running, so here
is the my second try that also was deemed unacceptable for the OP's needs:


http://codepad.org/SJvF3Y3v
_________________________________________________________________
#include <stdio.h>
#include <stdlib.h>




/* "SysCall" Interface
___________________________________________________________*/
typedef int (*fp_syscall_type) (void*, void*);

#define syscall_explicit(mp_obj, mp_fidx, mp_ctx) ( \
(mp_obj)->vtable->fp[(mp_fidx)]((mp_obj), (mp_ctx)) \
)




/* Sample Device Object Interface
___________________________________________________________*/
enum device_vtable_fidx
{
DEVICE_FIDX_DESTROY = 0,
DEVICE_FIDX_READ = 1,
DEVICE_FIDX_WRITE = 2
};


struct device_vtable
{
fp_syscall_type fp[3];
};


struct device_ctx
{
void* buf;
size_t bufsz;
};


struct device
{
struct device_vtable const* vtable;
};


#define device_destroy(mp_obj) \
syscall_explicit(mp_obj, DEVICE_FIDX_DESTROY, NULL)

#define device_read(mp_obj, mp_ctx) \
syscall_explicit(mp_obj, DEVICE_FIDX_READ, mp_ctx)

#define device_write(mp_obj, mp_ctx) \
syscall_explicit(mp_obj, DEVICE_FIDX_WRITE, mp_ctx)






/* Sample USB Device Interface
___________________________________________________________*/
extern int usb_drive_create(struct device**);






/* Sample USB Drive Impl
___________________________________________________________*/
struct usb_drive_impl
{
struct device iface;
/* usb drive specific stuff */
};



static int usb_drive_syscall_destroy(void*, void*);
static int usb_drive_syscall_read(void*, void*);
static int usb_drive_syscall_write(void*, void*);


static struct device_vtable const g_device_vtable = {{
usb_drive_syscall_destroy,
usb_drive_syscall_read,
usb_drive_syscall_write
}};


int
usb_drive_create(
struct device** const self_iface
){
struct usb_drive_impl* self = malloc(sizeof(*self));

if (self)
{
self->iface.vtable = &g_device_vtable;
*self_iface = &self->iface;

printf("usb_drive_create(%p)\r\n", (void*)self);

return 0;
}

return 1;
}



int
usb_drive_syscall_destroy(
void* self_,
void* ctx_
){
struct usb_drive_impl* const self = self_;

free(self);

printf("usb_drive_syscall_destroy(%p, %p)\r\n", self_, ctx_);

return 0;
}


int
usb_drive_syscall_read(
void* self_,
void* ctx_
){
struct usb_drive_impl* const self = self_;
struct device_ctx* const ctx = ctx_;

printf("usb_drive_syscall_read(%p, %p)\r\n", self_, ctx_);

printf("read %lu bytes into %p\r\n",
(unsigned long)ctx->bufsz, ctx->buf);

return 0;
}


int
usb_drive_syscall_write(
void* self_,
void* ctx_
){
struct usb_drive_impl* const self = self_;
struct device_ctx* const ctx = ctx_;

printf("usb_drive_syscall_write(%p, %p)\r\n", self_, ctx_);

printf("write %lu bytes from %p\r\n",
(unsigned long)ctx->bufsz, ctx->buf);

return 0;
}





/* Sample Application
___________________________________________________________*/
void
read_write(
struct device* self
){
char buf[32] = { '\0' };
struct device_ctx dctx = { buf, 16 };

/* Use the device object interface */
device_read(self, &dctx);

dctx.bufsz = 8;
device_write(self, &dctx);


/* Use the device with explicit syscall's */
dctx.bufsz = 24;
syscall_explicit(self, DEVICE_FIDX_READ, &dctx);

dctx.bufsz = 13;
syscall_explicit(self, DEVICE_FIDX_WRITE, &dctx);
}



int
main()
{
struct device* self = NULL;

if (! usb_drive_create(&self))
{
read_write(self);

device_destroy(self);
}

return 0;
}
_________________________________________________________________


Yes it is highly contrived, but you can call functions using an index at
runtime...

lol ;^D

Chris M. Thomasson

unread,
May 6, 2015, 7:39:39 PM5/6/15
to
> "David Kleinecke" wrote in message
> news:df77ab5a-e73f-441c...@googlegroups.com...

> > On Sunday, May 3, 2015 at 2:02:24 PM UTC-7, Chris M. Thomasson wrote:
[...]
> > The code I posted into codepad seems to be unavailable right now.
[...]
> > Sorry about that non-sense Tim!

> There's nothing wrong with this way of doing business. I just think it
> is unnecessarily complicated.

Can you expand some more on why this simplistic method of OOP in C is
"unnecessarily" complicated? I mean, yes implementing some OOP features
in C can be awkward, but if you try to be a minimalist about it, then it can
sometimes be a workable solution to some problems...



> One must ask what one's goals are. It is important to me the get all the
> methods together in one place.

AFAICT, the methods for a "class" are "in one place" wrt the sample,
therefore
I simply must be misunderstanding you in a fundamental way here.

Humm...



> GTK+ approaches this by, as I have said, equating a translation unit
> with a what I called a class. But not making this identification
> explicit they add confusion. I don't really care how the class idea
> is implemented but I would prefer having it explicit.

Can you be more specific about what you mean by explicit?

FWIW, in the sample code I posted there is a "contract", so to speak. A
device
object has an interface and explicitly binds to it by implementing its
methods
within a translation unit. So, in a sense, this is directly equating a
translation
unit with an interface. Now, the minimalist OOP part comes in when multiple
objects bind to the same interface. This creates a many-to-one relationship
if
you will. This allows one to create a set of generic functions that operate
on
device objects, no matter what they are. This can come in handy.

For instance, many years ago I had to test a bunch of experimental
multi-threaded sync algorithms I created. IIRC, there was around 40'ish
different ways to implement a mutex, queue, semaphore, ect... So, I created
a single interface per "type" of algorithm and separate translation units to
implement each sync algo, much like in the sample code I posted. This
allowed me to easily test all of the sync algorithms using a single code
base that depended on, or had a contract with the interfaces.


Again, I am very sorry if I am completely misunderstanding you.

Sorry about that David!


Yikes! ;^(

Tim Rentsch

unread,
May 6, 2015, 10:30:55 PM5/6/15
to
"Chris M. Thomasson" <cri...@charter.net> writes:

> [..snip..] codepad seems to be back up and running, so here is the
> my second try that also was deemed unacceptable for the OP's
> needs:
>
> [.. long example ..]
>
> Yes it is highly contrived, but you can call functions using an
> index at runtime...

Okay, thank you. Seems straightforward enough, which I'm guessing
was your intent also in writing it.

David Kleinecke

unread,
May 7, 2015, 4:00:05 PM5/7/15
to
There is a trade off between what can be done and what is easy to
understand.

It is not easy to recognize an object class USBDrive in your code and I
think such a thing would be helpful. Not that your method is in any way
wrong. It just isn't very easy to follow. Bettre than GTK+ though.

I assume you are thinking of struct device as equivalent to USBDrive. I
confess to being too lazy to rewrite your example.
but there is a major difference here. In a vtable formulation like this
the point-of-view is that the object knows its methods. In my scheme
only the class knows the methods. This is a difference that probably
doesn't matter in most code but could. An object could, potentially,
overwrite a class method by manipulating its vtable. My object can't.

Your example in my scheme creates an empty object. That is not illegal
but suggests that all code is class code (object independent). I think
an empty object should not be allowed - there is nothing to persist.

So I see your example as a complicated way to write a rather simple
program.

Chris M. Thomasson

unread,
May 9, 2015, 3:42:45 PM5/9/15
to
> "David Kleinecke" wrote in message news:e75aaebf-348f-430e-a3f6-
> fb487f...@googlegroups.com...

> > On Wednesday, May 6, 2015 at 4:39:39 PM UTC-7, Chris M. Thomasson wrote:
[...]
> > FWIW, in the sample code I posted there is a "contract", so to speak. A
> > device object has an interface and explicitly binds to it by
> > implementing its
> > methods within a translation unit. So, in a sense, this is directly
> > equating a
> > translation unit with an interface. Now, the minimalist OOP part comes
> > in
> > when multiple objects bind to the same interface. This creates a
> > many-to-
> > one relationship if you will. This allows one to create a set of generic
> > functions
> > that operate on device objects, no matter what they are. This can come
> > in handy.


> There is a trade off between what can be done and what is easy to
> understand.

Indeed! Understanding the fine points of any trade off is a definite plus.


> It is not easy to recognize an object class USBDrive in your code and I
> think such a thing would be helpful.

Are you referring to the fact that a generic device object interface hides
what it actually represents?


> Not that your method is in any way wrong. It just isn't very easy to
> follow.
> Bettre than GTK+ though.
> I assume you are thinking of struct device as equivalent to USBDrive. I
> confess to being too lazy to rewrite your example.
> but there is a major difference here.

Well, I expect the USBDrive to honor its contract with the device object
interface. When I call functions on any device object, well, it damn well
better adhere to the interface contract wrt how its device functions are
documented to behave!


> In a vtable formulation like this
> the point-of-view is that the object knows its methods. In my scheme
> only the class knows the methods. This is a difference that probably
> doesn't matter in most code but could. An object could, potentially,
> overwrite a class method by manipulating its vtable.

In the minimalist scheme I provided, that would be a bug, wrt interface
objects manipulating their vtables.


> My object can't.
> Your example in my scheme creates an empty object.

Empty? Well, the example does dynamically create a real object with
a lifetime, interfaced through abstraction. Therefore, I must be
misunderstanding you again!

[...]

David Kleinecke

unread,
May 10, 2015, 2:15:32 PM5/10/15
to
On Saturday, May 9, 2015 at 12:42:45 PM UTC-7, Chris M. Thomasson wrote:
>
> Well, I expect the USBDrive to honor its contract with the device object
> interface. When I call functions on any device object, well, it damn well
> better adhere to the interface contract wrt how its device functions are
> documented to behave!

I think we are having trouble with the concept "interface". In my scheme
"interface" plays no part except perhaps as an alternative name for the
description (in an enum or comments) of an object's data and methods. In
particular "interface contract" has no meaning in my context.

These matters (interfaces and contracts and so on) are not part of C90 (I
admit to not checking newer versions) but could be implemented in most
any language (including, as you have shown C90).
0 new messages