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

Adding the ability to add functions into structures?

2 views
Skip to first unread message

Albert

unread,
Dec 30, 2005, 8:12:33 PM12/30/05
to
So structures are useful to group variables, so you can to refer to a
collection as a single entity. Wouldn't it be useful to also have the
ability to collect variable and functions?

Ask K&R say, C programs consist of variables to store the input and
functions to manipulate them.

This would make C object-oriented - how cool would that be?

Are there any problems with adding the ability to have functions
encapsulated in structures?

Walter Roberson

unread,
Dec 30, 2005, 9:27:06 PM12/30/05
to
In article <1135991553.8...@z14g2000cwz.googlegroups.com>,

Albert <albert.xt...@gmail.com> wrote:
>So structures are useful to group variables, so you can to refer to a
>collection as a single entity. Wouldn't it be useful to also have the
>ability to collect variable and functions?

>Are there any problems with adding the ability to have functions
>encapsulated in structures?

structures are data, functions are code. Having the ability to store
functions (instead of function -pointers-) into structures would
require mechanisms by which data become executable. Some
architectures (e.g., the Harvard architecture) cannot do that.

In architectures that do allow it, you would allow -all- of
your data to be executable (in which case you have all of the
classic stack overflow problems) or else you need a mechanism
to -dynamically- indicate that a particular block of memory is
executable; the existance of such a dynamic method is not at
all certain on any particular system.
--
Is there any thing whereof it may be said, See, this is new? It hath
been already of old time, which was before us. -- Ecclesiastes

Chuck F.

unread,
Dec 30, 2005, 9:42:44 PM12/30/05
to
Walter Roberson wrote:
> Albert <albert.xt...@gmail.com> wrote:
>
>> So structures are useful to group variables, so you can to
>> refer to a collection as a single entity. Wouldn't it be
>> useful to also have the ability to collect variable and
>> functions?
>
>> Are there any problems with adding the ability to have
>> functions encapsulated in structures?
>
> structures are data, functions are code. Having the ability to
> store functions (instead of function -pointers-) into structures
> would require mechanisms by which data become executable. Some
> architectures (e.g., the Harvard architecture) cannot do that.
>
> In architectures that do allow it, you would allow -all- of your
> data to be executable (in which case you have all of the classic
> stack overflow problems) or else you need a mechanism to
> -dynamically- indicate that a particular block of memory is
> executable; the existance of such a dynamic method is not at all
> certain on any particular system.

Methinks the OP wants lisp.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>

Jack Klein

unread,
Dec 31, 2005, 12:07:41 AM12/31/05
to
On 30 Dec 2005 17:12:33 -0800, "Albert"
<albert.xt...@gmail.com> wrote in comp.lang.c:

It's already been done. They discuss it in comp.lang.c++, down the
hall. And in comp.lang.java, and I'm sure also in whatever group it
is on Microsoft's server that is devoted to C#.

I know I'll get flamed for this, but with the exception of inheritance
this is really nothing but syntactical sugar. You can write object
oriented programs in C right now.

A perfect example is the FILE data type, declared an <stdio.h>. It
has a creator, fopen(), a destructor, fclose(), and all sorts of
methods you can invoke on it via its pointer, such as fprintf(),
fscanf(), fread(), fwrite(), between its successful creation and its
destruction.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html

Artie Gold

unread,
Dec 31, 2005, 12:52:35 AM12/31/05
to
Chuck F. wrote:
> Walter Roberson wrote:
>
>> Albert <albert.xt...@gmail.com> wrote:
>>
>>> So structures are useful to group variables, so you can to
>>> refer to a collection as a single entity. Wouldn't it be
>>> useful to also have the ability to collect variable and
>>> functions?
>>
>>
>>> Are there any problems with adding the ability to have
>>> functions encapsulated in structures?
>>
>>
>> structures are data, functions are code. Having the ability to
>> store functions (instead of function -pointers-) into structures
>> would require mechanisms by which data become executable. Some
>> architectures (e.g., the Harvard architecture) cannot do that.
>>
>> In architectures that do allow it, you would allow -all- of your
>> data to be executable (in which case you have all of the classic
>> stack overflow problems) or else you need a mechanism to
>> -dynamically- indicate that a particular block of memory is
>> executable; the existance of such a dynamic method is not at all
>> certain on any particular system.
>
>
> Methinks the OP wants lisp.
>
Methinks too much credit you offer to the OP.

--ag

--
Artie Gold -- Austin, Texas
http://goldsays.blogspot.com (new post 8/5)
http://www.cafepress.com/goldsays
"If you have nothing to hide, you're not trying!"

Richard Heathfield

unread,
Dec 31, 2005, 12:54:27 AM12/31/05
to
Jack Klein said:

> I know I'll get flamed for this,

FLAME ON!!!

> but with the exception of inheritance
> this is really nothing but syntactical sugar. You can write object
> oriented programs in C right now.

Oh. Was that it? Sheesh. Flame off again. (sigh)

> A perfect example is the FILE data type, declared an <stdio.h>. It
> has a creator, fopen(), a destructor, fclose(), and all sorts of
> methods you can invoke on it via its pointer, such as fprintf(),
> fscanf(), fread(), fwrite(), between its successful creation and its
> destruction.

This is pretty much how I do it, yes. Just a minor nit - FILE isn't (quite)
a perfect example, in at least two ways:

1) FILE should, in my opinion, be a truly opaque type, with the structure
definition hidden internally. typedef struct _iobuf FILE; is already more
than we need to know. The whole schmeer is far, far more than we need to
know.

2) I wish I wish I wish that fclose took FILE ** rather than FILE *, don't
you?

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)

Chuck F.

unread,
Dec 31, 2005, 3:10:24 AM12/31/05
to
Jack Klein wrote:
>
... snip ...

>
> I know I'll get flamed for this, but with the exception of
> inheritance this is really nothing but syntactical sugar. You
> can write object oriented programs in C right now.
>
> A perfect example is the FILE data type, declared an <stdio.h>.
> It has a creator, fopen(), a destructor, fclose(), and all sorts
> of methods you can invoke on it via its pointer, such as
> fprintf(), fscanf(), fread(), fwrite(), between its successful
> creation and its destruction.

I see no reason for any fires. The only problem with your example
is that you can't write (in general) that type in C. As a further
example I suggest my hashlib, which is written in standard C, is
also very much object oriented, offers constructors, destructors,
and methods.

It offers the opaque incomplete object "hshtbl", whose details are
completely hidden. This is unlike FILE*, which has to be
incompletely hidden because of some standards constraints. Of
course hashlib has the advantage of being designed some 30 plus
years later than the FILE system.

See: <http://cbfalconer.home.att.net/download/hashlib.zip>

Richard Heathfield

unread,
Dec 31, 2005, 5:44:13 AM12/31/05
to
Chuck F. said:

> Jack Klein wrote:
>>
> ... snip ...
>>
>> I know I'll get flamed for this, but with the exception of
>> inheritance this is really nothing but syntactical sugar. You
>> can write object oriented programs in C right now.
>>
>> A perfect example is the FILE data type, declared an <stdio.h>.
>> It has a creator, fopen(), a destructor, fclose(), and all sorts
>> of methods you can invoke on it via its pointer, such as
>> fprintf(), fscanf(), fread(), fwrite(), between its successful
>> creation and its destruction.
>
> I see no reason for any fires. The only problem with your example
> is that you can't write (in general) that type in C.

I must have misunderstood what you mean, because I see no reason whatsoever
why one could not write (in general) that type in C.

Malcolm

unread,
Dec 31, 2005, 7:23:31 AM12/31/05
to

"Jack Klein" <jack...@spamcop.net> wrote

> I know I'll get flamed for this, but with the exception of inheritance
^^^^^^^^^^^^^^^^^^^^^^^^

> this is really nothing but syntactical sugar. You can write object
> oriented programs in C right now.
>
> A perfect example is the FILE data type, declared an <stdio.h>. It
> has a creator, fopen(), a destructor, fclose(), and all sorts of
> methods you can invoke on it via its pointer, such as fprintf(),
> fscanf(), fread(), fwrite(), between its successful creation and its
> destruction.
>
Inheritance is crucial.
An object is any set of data items that are "part of the same thing". C
structures are therefore objects. (The C standard further specifies that an
object must be stored contigously in memory. This is a language issue and a
fairly obvious thing to do, but not strictly necessary).

A program becomes "object-oriented" not when it uses objects, but when the
objects enter into relationships with each other. In C++ like most lanauges
that support obect-orientation, this is achieved via inheirtance. However
there are other ways, for example Microsoft Windows objects all respond to
the same message system, Java interfaces specify common methods, text
adventure objects have verb handlers.


jacob navia

unread,
Dec 31, 2005, 9:02:03 AM12/31/05
to
Albert a écrit :
You can do this by defining an interface. I do this by using the
following schema:

The object has a pointer to the interface, called "lpVtbl", and other
fields.

The interface is a set of function pointers that defines the methods
available for the object.

For instance:

1) I define a forward declaration to the object
typedef struct _ArrayList ArrayList;

2) I define the interface functions:
typedef struct {
// Returns the number of elements stored
int (*GetCount)(ArrayList &AL);
// Is this collection read only?
int (*IsReadOnly)(ArrayList &AL);
// Sets this collection read-only or unsets the read-only flag
int (*SetReadOnly)(ArrayList &AL,int flag);
///SNIP /// SNIP /// SNIP
// Pushes a string, using the collection as a stack
int (*Push)(ArrayList &AL,void *str);
} ArrayListInterface;

3) I define the object
struct _ArrayList {
ArrayListInterface *lpVtbl; // The table of functions
size_t count; /* number of elements in the array */
void **contents; /* The contents of the collection */
size_t capacity; /* allocated space in the contents vector */
unsigned int flags; // Read-only or other flags
size_t ElementSize; // Size of the elements stored in this array
};

The usage is now very simple: For instance to get the count of elements
in an array list I do:

ArrayList al;
/// snip initialization, etc

int count = al.lpVtbl->GetCount(&al);

The lcc-win32 compiler uses this feature extensiveley to define
arraylists and other data structures in C.

jacob

Chuck F.

unread,
Dec 31, 2005, 8:48:00 AM12/31/05
to
Richard Heathfield wrote:
> Chuck F. said:
>> Jack Klein wrote:
>>>
>> ... snip ...
>>>
>>> I know I'll get flamed for this, but with the exception of
>>> inheritance this is really nothing but syntactical sugar.
>>> You can write object oriented programs in C right now.
>>>
>>> A perfect example is the FILE data type, declared an
>>> <stdio.h>. It has a creator, fopen(), a destructor,
>>> fclose(), and all sorts of methods you can invoke on it via
>>> its pointer, such as fprintf(), fscanf(), fread(), fwrite(),
>>> between its successful creation and its destruction.
>>
>> I see no reason for any fires. The only problem with your
>> example is that you can't write (in general) that type in C.
>
> I must have misunderstood what you mean, because I see no reason
> whatsoever why one could not write (in general) that type in C.

I meant that to implement FILE you have to access such things as
ports, disks, etc. that take you out of pure C.

Eric Sosman

unread,
Dec 31, 2005, 9:24:30 AM12/31/05
to
jacob navia wrote:
> [...]

> For instance:
>
> 1) I define a forward declaration to the object
> typedef struct _ArrayList ArrayList;

Have you forgotten that the identifier `_ArrayList'
is off-limits?

"All identifiers that begin with an underscore and
either an uppercase letter or another underscore
are always reserved for any use."
-- ISO/IEC 9899:1999 section 7.1.3 paragraph 1

--
Eric Sosman
eso...@acm-dot-org.invalid

Richard Heathfield

unread,
Dec 31, 2005, 9:26:33 AM12/31/05
to
Chuck F. said:

> Richard Heathfield wrote:
>> Chuck F. said:
>>> The only problem with your
>>> example is that you can't write (in general) that type in C.
>>
>> I must have misunderstood what you mean, because I see no reason
>> whatsoever why one could not write (in general) that type in C.
>
> I meant that to implement FILE you have to access such things as
> ports, disks, etc. that take you out of pure C.

Oh. I thought you meant opaque types were impossible, which they clearly
aren't.

Eric Sosman

unread,
Dec 31, 2005, 9:27:47 AM12/31/05
to
jacob navia wrote:
> [...]

> 2) I define the interface functions:
> typedef struct {
> // Returns the number of elements stored
> int (*GetCount)(ArrayList &AL);

Whatever this language is, I doubt it's topical on
comp.lang.c.

--
Eric Sosman
eso...@acm-dot-org.invalid

jacob navia

unread,
Dec 31, 2005, 9:50:09 AM12/31/05
to
Eric Sosman a écrit :

> jacob navia wrote:
>
>> [...]
>> 2) I define the interface functions:
>> typedef struct {
>> // Returns the number of elements stored
>> int (*GetCount)(ArrayList &AL);
>
>
> Whatever this language is, I doubt it's topical on
> comp.lang.c.
>
Yes, replace the "&" by "*".
lcc-win32 supports "&" as in C++, but it can be
replaced by "*". References are just pointers that
can never be NULL. A pointer will do too.

jacob navia

unread,
Dec 31, 2005, 9:51:04 AM12/31/05
to
Eric Sosman a écrit :
Yes, you are right. Just put
typedef struct tagArrayList

Sometimes I use _ because is shorter

Chuck F.

unread,
Dec 31, 2005, 9:48:41 AM12/31/05
to

In this case I think you have forgotten that Jacob is the
implementor, and thus correctly avoiding the users namespace.

Clark S. Cox III

unread,
Dec 31, 2005, 10:54:59 AM12/31/05
to

Why do you keep posting code that, while almost C, will only work on a
single compiler, on a single platform?

--
Clark S. Cox, III
clar...@gmail.com

Keith Thompson

unread,
Dec 31, 2005, 2:04:18 PM12/31/05
to

Why not just use

struct ArrayList {
...
};

and then consistently refer to the type as "struct ArrayList"? What
benefit does the typedef give you? (If you think not having to type
the word "struct" is enough of a benefit, I disagree but I won't argue
about it.)

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.

jacob navia

unread,
Dec 31, 2005, 2:46:29 PM12/31/05
to
Clark S. Cox III a écrit :

Well, I forgot that. It is a single compiler yes, and I do not have
the means of porting it. But what's your point?

Changing "&" by "*" will make it work in any compiler in any platform.

Jack Klein

unread,
Dec 31, 2005, 4:03:07 PM12/31/05
to
On Sat, 31 Dec 2005 05:54:27 +0000 (UTC), Richard Heathfield
<inv...@invalid.invalid> wrote in comp.lang.c:

> Jack Klein said:
>
> > I know I'll get flamed for this,
>
> FLAME ON!!!
>
> > but with the exception of inheritance
> > this is really nothing but syntactical sugar. You can write object
> > oriented programs in C right now.
>
> Oh. Was that it? Sheesh. Flame off again. (sigh)
>
> > A perfect example is the FILE data type, declared an <stdio.h>. It
> > has a creator, fopen(), a destructor, fclose(), and all sorts of
> > methods you can invoke on it via its pointer, such as fprintf(),
> > fscanf(), fread(), fwrite(), between its successful creation and its
> > destruction.
>
> This is pretty much how I do it, yes. Just a minor nit - FILE isn't (quite)
> a perfect example, in at least two ways:
>
> 1) FILE should, in my opinion, be a truly opaque type, with the structure
> definition hidden internally. typedef struct _iobuf FILE; is already more
> than we need to know. The whole schmeer is far, far more than we need to
> know.

I agree that FILE is not a perfectly opaque type, although I
deliberately decided not to complicate the point I was making to the
OP by bringing that up.

FILE cannot truly be an opaque type as far standard C is concerned for
the simple reason that the standard requires some functions to be
implemented as macros, and so some of the details must be exposed by
<stdio.h>.

Nevertheless, it is opaque enough for the point of the article, in the
sense that none of its internal details or workings are documented.
There is nothing that a strictly conforming program can do with a FILE
* other than through the documented functions.

> 2) I wish I wish I wish that fclose took FILE ** rather than FILE *, don't
> you?

The one I consider much more important than that would be defining
fclose(NULL) as a no-op, just like free(NULL) is. It would greatly
simplify cleaning up resources. Right now you have to write code like
this:

WARNING: pseudo code not compiled, tested, or sanity checked!

return_type some_func( /*...*/)
{
FILE *fin = NULL;
FILE *fout = NULL;
data_type *data_ptr = NULL;
return_type something = DEFAULT_VALUE;

/* ... */

fin = fopen(...);
fout = fopen(...);
data_ptr = malloc(...);

/* ... */

if (fin) fclose(fin);
if (fout) fclose(fout);
free(data_type);

return something;
}

The additional checking for null FILE pointers before calling fclose()
is not a major effort, but it is just annoying enough to earn a place
on the pet peeve list after writing it INT_MAX times.

BTW, what's wrong with:

void rh_fclose(FILE **fp)
{
if (*fp)
{
fclose(*fp);
*fp = NULL;
}
}

Of course, I could just as easily write:

void jk_fclose(FILE *fp)
{
if (fp) fclose(fp);
}

Maybe time for both of us to make a new year's resolution to stop
carping about the easily fixable stuff?

Nah, that would take all the fun out of it!

Jack Klein

unread,
Dec 31, 2005, 4:17:15 PM12/31/05
to
On Sat, 31 Dec 2005 12:23:31 +0000 (UTC), "Malcolm"
<regn...@btinternet.com> wrote in comp.lang.c:

>
> "Jack Klein" <jack...@spamcop.net> wrote
> > I know I'll get flamed for this, but with the exception of inheritance
> ^^^^^^^^^^^^^^^^^^^^^^^^
> > this is really nothing but syntactical sugar. You can write object
> > oriented programs in C right now.
> >
> > A perfect example is the FILE data type, declared an <stdio.h>. It
> > has a creator, fopen(), a destructor, fclose(), and all sorts of
> > methods you can invoke on it via its pointer, such as fprintf(),
> > fscanf(), fread(), fwrite(), between its successful creation and its
> > destruction.
> >
> Inheritance is crucial.

Says who? The first hit on Google for the phrase "object oriented"
including quotations is
http://java.sun.com/docs/books/tutorial/java/concepts/, which has the
page title "Object-Oriented Programming Concepts". The first question
and answer on this page are:

What Is an Object?

An object is a software bundle of related variables and methods.
Software objects are often used to model real-world objects you find
in everyday life.

Further down the page comes the question "What is Inheritance".

Inheritance is a heavily-used feature that is provided by most object
oriented languages and systems, but it is indeed an extra feature. The
true definition of object orientation is pretty well captured in the
first question and answer. Object orientation means that data is
encapsulated and not manipulated directly, only via specifically
defined functions, often called "methods." And the C FILE type meets
this definition.

> An object is any set of data items that are "part of the same thing". C
> structures are therefore objects. (The C standard further specifies that an
> object must be stored contigously in memory. This is a language issue and a
> fairly obvious thing to do, but not strictly necessary).

That's quite correct. In actual fact, the definition of "object" in
the C++ language standard is exactly the same as it is in C, and has
nothing at all to do with classes or object oriented programming. In
C++, as in C, an int is an object.

> A program becomes "object-oriented" not when it uses objects, but when the

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is an interesting point of view, and one perhaps subscribed to by
many programmers who use object oriented paradigms, perhaps in
languages that have more built-in support for it than C does. Still,
I would be mildly surprised if you could find an authoritative
definition of "object orientation" that states that inheritance is
required.

> objects enter into relationships with each other. In C++ like most lanauges
> that support obect-orientation, this is achieved via inheirtance. However
> there are other ways, for example Microsoft Windows objects all respond to
> the same message system, Java interfaces specify common methods, text
> adventure objects have verb handlers.

Actually, inheritance can be done in C as well, but the result tends
to be rather messy. Most people who attempt it are trying to write
C++ in C and, in my experience, the result is neither good C++ nor
good C.

Eric Sosman

unread,
Dec 31, 2005, 4:29:02 PM12/31/05
to
Jack Klein wrote:
> [...]

> FILE cannot truly be an opaque type as far standard C is concerned for
> the simple reason that the standard requires some functions to be
> implemented as macros, and so some of the details must be exposed by
> <stdio.h>.

What functions that deal with FILE or FILE* are required
to be macros?

--
Eric Sosman
eso...@acm-dot-org.invalid

Mark McIntyre

unread,
Dec 31, 2005, 7:20:43 PM12/31/05
to
On Sat, 31 Dec 2005 16:29:02 -0500, in comp.lang.c , Eric Sosman
<eso...@acm-dot-org.invalid> wrote:

>Jack Klein wrote:
>> [...]
>> FILE cannot truly be an opaque type as far standard C is concerned for
>> the simple reason that the standard requires some functions to be
>> implemented as macros, and so some of the details must be exposed by
>> <stdio.h>.
>
> What functions that deal with FILE or FILE* are required
>to be macros?

getchar() and getc() may be implemented as a macro, as may the put...
equivalents.
.


Mark McIntyre
--

----== 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 =----

Keith Thompson

unread,
Dec 31, 2005, 10:56:13 PM12/31/05
to
Mark McIntyre <markmc...@spamcop.net> writes:
> On Sat, 31 Dec 2005 16:29:02 -0500, in comp.lang.c , Eric Sosman
> <eso...@acm-dot-org.invalid> wrote:
>>Jack Klein wrote:
>>> [...]
>>> FILE cannot truly be an opaque type as far standard C is concerned for
>>> the simple reason that the standard requires some functions to be
>>> implemented as macros, and so some of the details must be exposed by
>>> <stdio.h>.
>>
>> What functions that deal with FILE or FILE* are required
>>to be macros?
>
> getchar() and getc() may be implemented as a macro, as may the put...
> equivalents.

Any library function may be implemented as a macro. I think assert()
is the only one that's required to be a macro.

getc() and putc() are equivalent to fgetc() and fputc(), respectively,
except that *if* they're implemented as macros they can evaluate their
stream arguments more than once.

A conforming implementation could make getc() and putc() regular
functions without macro equivalents, and make FILE as opaque as it
likes (it still has to be an object type), but making them macros is
probably important for performance.

Then again, FILE could be just an array of N unsigned chars (or a
struct containing such an array), with the code that uses it
converting FILE* to REAL_FILE* to access the internals.

Richard Heathfield

unread,
Jan 1, 2006, 12:52:24 AM1/1/06
to
Jack Klein said:

> FILE cannot truly be an opaque type as far standard C is concerned for
> the simple reason that the standard requires some functions to be
> implemented as macros, and so some of the details must be exposed by
> <stdio.h>.

Absolutely true, but a nuisance nonetheless.

>> 2) I wish I wish I wish that fclose took FILE ** rather than FILE *,
>> don't you?
>
> The one I consider much more important than that would be defining
> fclose(NULL) as a no-op, just like free(NULL) is.

That, too.


> The additional checking for null FILE pointers before calling fclose()
> is not a major effort, but it is just annoying enough to earn a place
> on the pet peeve list after writing it INT_MAX times.

For sufficiently low values of INT_MAX, anyway.

> BTW, what's wrong with:
>
> void rh_fclose(FILE **fp)
> {
> if (*fp)
> {
> fclose(*fp);
> *fp = NULL;
> }
> }

Nothing. That's how most of my opaque type destructors look. But (and this
will perhaps raise a chuckle) whilst I freely and consciously borrowed the
FILE * model for my opaque types - with the above modification - it had not
actually occurred to me to wrap C's I/O streams in this model! Thanks for
the suggestion. :-)

Eric Sosman

unread,
Jan 1, 2006, 8:26:19 AM1/1/06
to
Keith Thompson wrote:
> Mark McIntyre <markmc...@spamcop.net> writes:
>
>>On Sat, 31 Dec 2005 16:29:02 -0500, in comp.lang.c , Eric Sosman
>><eso...@acm-dot-org.invalid> wrote:
>>
>>>Jack Klein wrote:
>>>
>>>>[...]
>>>>FILE cannot truly be an opaque type as far standard C is concerned for
>>>>the simple reason that the standard requires some functions to be
>>>>implemented as macros, and so some of the details must be exposed by
>>>><stdio.h>.
>>>
>>> What functions that deal with FILE or FILE* are required
>>>to be macros?
>>
>>getchar() and getc() may be implemented as a macro, as may the put...
>>equivalents.
>
>
> Any library function may be implemented as a macro. I think assert()
> is the only one that's required to be a macro.

There's also setjmp().

However, that wasn't what I asked. Jack Klein wrote that
the Standard *requires* some functions to be implemented as
macros, and this is true. However, I was unable to think of
any FILE-using functions that are *required* to be macros. Can
you suggest any such?

I'm well aware that the Standard *permits* an implementation
to provide macros for any library functions it chooses, so long
as it provides the actual function as well. I'm also aware that
the Standard makes special provision for getc() and putc() (but
not for getchar(), by the way) that make macro implementations
easier. But does the Standard *require* a macro implementation
for any FILE or FILE* function?

... and what this all comes down to, of course, is whether
FILE can be a fully opaque type. I believe it can be, but both
Jack Klein and Richard Heathfield (two not-to-be-ignored people)
take the opposite view. I'm trying to learn why.

--
Eric Sosman
eso...@acm-dot-org.invalid

Richard Heathfield

unread,
Jan 1, 2006, 9:11:01 AM1/1/06
to
Eric Sosman said:

> I'm well aware that the Standard *permits* an implementation
> to provide macros for any library functions it chooses, so long
> as it provides the actual function as well. I'm also aware that
> the Standard makes special provision for getc() and putc() (but
> not for getchar(), by the way) that make macro implementations
> easier. But does the Standard *require* a macro implementation
> for any FILE or FILE* function?
>
> ... and what this all comes down to, of course, is whether
> FILE can be a fully opaque type. I believe it can be, but both
> Jack Klein and Richard Heathfield (two not-to-be-ignored people)
> take the opposite view. I'm trying to learn why.

Well, to be perfectly honest with you I was just nodding "wisely" at Jack's
assertion rather than think it through myself. (So much for
not-to-be-ignored!)

But now that I'm actually thinking about it (albeit not terribly brightly,
since dinner is moderately imminent), I don't know of anything that would
stop the following program from being strictly conforming:

#include <stdio.h>

int main(void)
{
FILE foo;
return 0;
}

and of course that wouldn't be legal if FILE were truly opaque.

Also, I was half-thinking (er, yes, I think that's what I mean) of the
possibility that, since getc et al *can* be implemented as macros, it is
necessary to make FILE visible in order to allow implementors the freedom
to implement it as a macro if they so choose. This is probably what Jack
meant, although even now I will freely admit that I haven't thought this
through thoroughly, or at least I don't think I have.

Chuck F.

unread,
Jan 1, 2006, 4:21:17 AM1/1/06
to
Jack Klein wrote:
>
... snip wishes from RH about fclose(FILE**) ...

>
> The additional checking for null FILE pointers before calling
> fclose() is not a major effort, but it is just annoying enough
> to earn a place on the pet peeve list after writing it INT_MAX
> times.> BTW, what's wrong with:
>
> void rh_fclose(FILE **fp)
> {
> if (*fp)
> {
> fclose(*fp);
> *fp = NULL;
> }
> }

If this is called from a function that can be passed a generic
FILE*, that may actually be stdin or stdout. I don't believe the
standard insists on those being modifiable pointers.

Emmanuel Delahaye

unread,
Jan 1, 2006, 12:07:10 PM1/1/06
to
Albert a écrit :
> So structures are useful to group variables, so you can to refer to a
> collection as a single entity. Wouldn't it be useful to also have the
> ability to collect variable and functions?
>
> Ask K&R say, C programs consist of variables to store the input and
> functions to manipulate them.
>
> This would make C object-oriented - how cool would that be?
>
> Are there any problems with adding the ability to have functions
> encapsulated in structures?

There is no good reason to do that (multipliying function instances ?
Why in the world ?).

The idea behind OOP is that the code is organized around objects and
methods (functions) to manipulate the objects. The objects are designed
to be instanciable, but not the functions. The idea is to have a langage
trick that 'connects' the object and its method:

obj.method()

as in C++, but behind the scene, the method stays unique. What you can
do in C is to store the address of the method (function) in the
structure using a pointer to a function, but it brings nothing really
interesting.

The C-way is rather to define function names like:

type_function()

and to pass the address of the object as the first parameter of these
functions

type_function(type *self)

It's also common to have explicit constructors and destructors:

type * type_create()
type_delete(type *self)

Note that if you are only using pointers to the objects, the type can be
completely opaque:

typedef struct type type;

A good standard-C-example is the FILE function family.

Constructors / destructors:
fopen() / fclose()

Methods:
fgetc() / fputc() / ferror() etc.

--
A+

Emmanuel Delahaye

jacob navia

unread,
Jan 1, 2006, 1:13:55 PM1/1/06
to
Emmanuel Delahaye a écrit :

I disagree.

I proposed (in another message in this thread) to program using
interfaces, i.e. arrays of function pointers that can be used
(as it is used routinely in COM programming) to encapsulate
the functions that act in a given object.

This approach has several advantages:
1) You can use common names like Add, Delete, etc, without
polluting the global namespace. The problem with the approach
you propose is that you *have* to prefix the function with
some prefix since you can't have two functions called "Add"
in the same program.

2) You can change or extend the interface without recompiling
client code. For instance, you can add new methods at the
end of the interface ("inheritance") without any recompilation.

3) You can "subclass" a functionality of the object by saving the stored
pointer, then replacing it by your own one, without any need
for recompilation.

For instance:

The procedure in the interface "Add" is lacking some functionality.
When you create the object, you save the pointer to the "ADD" function
then you assign a pointer to a new function "Add1", and store it in the
same place:
Object->InterfaceTable->Add = add1;

All the code that calls that function pointer will continue to
work unmodified using your new "Add1" function. If you save the
pointer to the function you can use it to call the old functionality
in case you do not want to totally replace it but just extend it.

This is much more flexible than C++.

jacob

Jordan Abel

unread,
Jan 1, 2006, 2:13:33 PM1/1/06
to
On 2006-01-01, Richard Heathfield <inv...@invalid.invalid> wrote:
>> The additional checking for null FILE pointers before calling fclose()
>> is not a major effort, but it is just annoying enough to earn a place
>> on the pet peeve list after writing it INT_MAX times.
>
> For sufficiently low values of INT_MAX, anyway.

INT_MAX is required to be at least 32767, of course. Though if you do
not include <limits.h>, you may define it yourself to some other value.
This, of course, should never be done.

Jordan Abel

unread,
Jan 1, 2006, 2:21:59 PM1/1/06
to

getchar does not have any arguments, therefore it wouldn't make sense to
give it permission to evaluate its arguments more than once. I believe
it is implicitly permitted to evaluate the expression (stdin) more than
once, since (stdin) doesn't have side effects.

In other words, #define getchar() getc(stdin) is legal. Particularly
since "The getchar function is equivalent to getc with the argument
stdin ."

> that make macro implementations
> easier. But does the Standard *require* a macro implementation
> for any FILE or FILE* function?
>
> ... and what this all comes down to, of course, is whether
> FILE can be a fully opaque type. I believe it can be, but both
> Jack Klein and Richard Heathfield (two not-to-be-ignored people)
> take the opposite view. I'm trying to learn why.

Even if the standard required a macro, that wouldn't mean such a macro
would be required to expose the innards of the file structure.

#define getc(f) getc(f) is legal, and glibc appears to in fact do this,
for some unknown reason.

What did they say that indicated a view that FILE can't be an opaque
type? It's not even really required to be a structure - An
implementation could make FILE an integer type if it wants - or void.

Jordan Abel

unread,
Jan 1, 2006, 2:32:31 PM1/1/06
to
On 2006-01-01, Richard Heathfield <inv...@invalid.invalid> wrote:
> Eric Sosman said:
>
>> I'm well aware that the Standard *permits* an implementation
>> to provide macros for any library functions it chooses, so long
>> as it provides the actual function as well. I'm also aware that
>> the Standard makes special provision for getc() and putc() (but
>> not for getchar(), by the way) that make macro implementations
>> easier. But does the Standard *require* a macro implementation
>> for any FILE or FILE* function?
>>
>> ... and what this all comes down to, of course, is whether
>> FILE can be a fully opaque type. I believe it can be, but both
>> Jack Klein and Richard Heathfield (two not-to-be-ignored people)
>> take the opposite view. I'm trying to learn why.
>
> Well, to be perfectly honest with you I was just nodding "wisely" at Jack's
> assertion rather than think it through myself. (So much for
> not-to-be-ignored!)
>
> But now that I'm actually thinking about it (albeit not terribly brightly,
> since dinner is moderately imminent), I don't know of anything that would
> stop the following program from being strictly conforming:
>
> #include <stdio.h>
>
> int main(void)
> {
> FILE foo;
> return 0;
>}
>
> and of course that wouldn't be legal if FILE were truly opaque.

Suppose that FILE is another pointer, though - or an integer. That would
be opaque enough to make no difference.

Or FILE is void and the implementation does not fail to compile on
encountering a field or variable declared void. (There's nothing
forbidding an implementation from emitting a diagnostic on encountering
a declared FILE, or on encountering anything else.)

Besides, there are multiple levels of "opaque". There are types which
can only be declared by the program and passed around by reference.
fpos_t is one.

Whether the definition can be modified while retaining the ability to
execute older binaries is really not the standard's business, since it
doesn't specify compatibility between different implementations.

> Also, I was half-thinking (er, yes, I think that's what I mean) of the
> possibility that, since getc et al *can* be implemented as macros, it is
> necessary to make FILE visible in order to allow implementors the freedom
> to implement it as a macro if they so choose. This is probably what Jack
> meant, although even now I will freely admit that I haven't thought this
> through thoroughly, or at least I don't think I have.

But arguably the implementors can choose not to make it visible if they
do not wish to implement these as macros. Both are under the
implementors' control.

Jordan Abel

unread,
Jan 1, 2006, 2:38:07 PM1/1/06
to
On 2006-01-01, Emmanuel Delahaye <em...@YOURBRAnoos.fr> wrote:
> A good standard-C-example is the FILE function family.
>
> Constructors / destructors:
> fopen() / fclose()
>
> Methods:
> fgetc() / fputc() / ferror() etc.

Speaking of which, I think it would have been better for them to take
the FILE* argument first, to match fprintf. However, the reasons for
this inconsistency lie forgotten in the depths of history. (It strikes
me as especially odd given that UNIX functions that take a file
descriptor number do take it first. Anyone here who was at Bell Labs
around that time have any comment on this? I forget, does DMR read this
newsgroup?)

Chuck F.

unread,
Jan 1, 2006, 4:02:35 PM1/1/06
to

If you think of the assembly language historically needed to
implement those functions, all becomes clear (especially if you
have done such implementations). fprintf needs the FILE first to
allow the variadic mechanisms to functions. For the others,
remember that the usual stack passing mechanism is last argument
pushed first. Thus, for fputc(c, f) the stack at function entry
will look like:

stack-mark
f
c
<top of stack>

This allows the routine to examine c and decide on such things as
'does this get printed', or 'should I convert into a tab sequence'
before worrying about the actual destination. This preliminary
work can be done without disturbing the stack-mark or f.

In this case the gain is fairly small. However when the parameters
include such things as fieldsize or precision, the gains can be
significant.

Keith Thompson

unread,
Jan 1, 2006, 4:26:13 PM1/1/06
to
Jordan Abel <rand...@gmail.com> writes:
> On 2006-01-01, Richard Heathfield <inv...@invalid.invalid> wrote:
[...]

>> But now that I'm actually thinking about it (albeit not terribly brightly,
>> since dinner is moderately imminent), I don't know of anything that would
>> stop the following program from being strictly conforming:
>>
>> #include <stdio.h>
>>
>> int main(void)
>> {
>> FILE foo;
>> return 0;
>>}
>>
>> and of course that wouldn't be legal if FILE were truly opaque.

I think we need to define just what we mean by "opaque".

FILE is IMHO sufficiently opaque as far as the standard is concerned;
it's not impossible to peek into its innards, but any program that
does so is non-portable -- not just in the pedantic "the standard
doesn't guarantee anything" sense, but in the sense that it will break
when ported to a different platform.

(I'm assuming here that the definition of type FILE really does differ
across implementations; I've never actually checked it.)

> Suppose that FILE is another pointer, though - or an integer. That would
> be opaque enough to make no difference.
>
> Or FILE is void and the implementation does not fail to compile on
> encountering a field or variable declared void. (There's nothing
> forbidding an implementation from emitting a diagnostic on encountering
> a declared FILE, or on encountering anything else.)

The standard specifically requires FILE to be an object type. I
suppose an implementation could act as if void is an object type, but
IMHO that goes beyond what "conforming" means.

Keith Thompson

unread,
Jan 1, 2006, 4:37:40 PM1/1/06
to
"Chuck F. " <cbfal...@yahoo.com> writes:
> Jordan Abel wrote:
[...]

>> Speaking of which, I think it would have been better for them to
>> take the FILE* argument first, to match fprintf. However, the
>> reasons for this inconsistency lie forgotten in the depths of
>> history. (It strikes me as especially odd given that UNIX
>> functions that take a file descriptor number do take it first.
>> Anyone here who was at Bell Labs around that time have any
>> comment on this? I forget, does DMR read this newsgroup?)
>
> If you think of the assembly language historically needed to implement
> those functions, all becomes clear (especially if you have done such
> implementations). fprintf needs the FILE first to allow the variadic
> mechanisms to functions. For the others, remember that the usual
> stack passing mechanism is last argument pushed first. Thus, for
> fputc(c, f) the stack at function entry will look like:
>
> stack-mark
> f
> c
> <top of stack>
>
> This allows the routine to examine c and decide on such things as
> 'does this get printed', or 'should I convert into a tab sequence'
> before worrying about the actual destination. This preliminary work
> can be done without disturbing the stack-mark or f.
>
> In this case the gain is fairly small. However when the parameters
> include such things as fieldsize or precision, the gains can be
> significant.

You seem to be assuming that the generated code can't easily access
items lower on the stack before accessing items higher on the stack.
In all the assembly languages I'm familiar with, it's easy to access
any item on the stack (within reason) by using a
stack-pointer-plus-offset addressing mode. All parameters are
available simultaneously and equally easily. (Some CPUs might limit
the offset to some small value, but plenty for a couple of
parameters.)

Historically, I know this is true for the PDP-11. What about the
PDP-7 (isn't that the first system where C was implemented)?

Did some CPUs actually require upper stack items to be popped before
lower stack items could be accessed?

Eric Sosman

unread,
Jan 1, 2006, 5:37:01 PM1/1/06
to
Keith Thompson wrote:
> [...]

> The standard specifically requires FILE to be an object type. I
> suppose an implementation could act as if void is an object type, but
> IMHO that goes beyond what "conforming" means.

So it does; I think that clears up my confusion. Jack
Klein was right when he said FILE cannot be an opaque type,
but it's not because of "the simple reason that the standard
requires some functions to be implemented as macros." Rather,
it's because 7.19.1/2 requires that FILE be "an object type"
and 6.2.5/1 says that object types "fully describe" objects.
There's no formal definition of "opaque," but it appears
antithetical to "full description."

Thanks to Keith, and to Richard Heathfield for making
the point in a slightly different way.

--
Eric Sosman
eso...@acm-dot-org.invalid

Keith Thompson

unread,
Jan 1, 2006, 5:52:08 PM1/1/06
to

But 7.19.1 says that FILE is

an object type capable of recording all the information needed to
control a stream, including [a bunch of stuff]

It doesn't necessarily have to record the information *directly*. I
can imagine FILE being a typedef for void* (making FILE* void**);
internally the void* can be converted to a pointer to a structure
containing the actual information.

Or if you want the information stored directly in the FILE, make it an
appropriately sized array of unsigned char.

It's probably not worth the effort, though. Even if FILE is a typedef
for a struct containing the required information explicitly, the fact
that the standard says nothing about the content makes it sufficiently
opaque for most purposes. Any scheme to hide the information can be
circumvented by a programmer sufficiently determined to shoot himself
in the foot.

Chuck F.

unread,
Jan 1, 2006, 8:00:13 PM1/1/06
to

In effect, yes. Take the 8080 as an example. I had routines to
address things offset from TOS, but they were relatively complex
and slow. However manipulating TOS, and possibly NOS, was fairly
easy. Using these methods it was possible to write pure code.

jacob navia

unread,
Jan 2, 2006, 6:40:12 AM1/2/06
to
Jordan Abel a écrit :

Ahhh living in the past.

Those never ending historical trivia that seem to have so
much success here.

I tried to start a discussion based in the interface concept
but it went completely down the drain.

The 8086, the PDP7, that is MUCH more fun. The only answers
I got pointed to some triviality. The discussion here never
gets to any substantive discussion about modern software construction.

Eric Sosman

unread,
Jan 2, 2006, 8:55:50 AM1/2/06
to
Keith Thompson wrote:
> Eric Sosman <eso...@acm-dot-org.invalid> writes:
>
>>Keith Thompson wrote:
>>
>>>[...]
>>>The standard specifically requires FILE to be an object type. I
>>>suppose an implementation could act as if void is an object type, but
>>>IMHO that goes beyond what "conforming" means.
>>
>> So it does; I think that clears up my confusion. Jack
>>Klein was right when he said FILE cannot be an opaque type,
>>but it's not because of "the simple reason that the standard
>>requires some functions to be implemented as macros." Rather,
>>it's because 7.19.1/2 requires that FILE be "an object type"
>>and 6.2.5/1 says that object types "fully describe" objects.
>>There's no formal definition of "opaque," but it appears
>>antithetical to "full description."
>>
>> Thanks to Keith, and to Richard Heathfield for making
>>the point in a slightly different way.
>
>
> But 7.19.1 says that FILE is
>
> an object type capable of recording all the information needed to
> control a stream, including [a bunch of stuff]
>
> It doesn't necessarily have to record the information *directly*. I
> can imagine FILE being a typedef for void* (making FILE* void**);
> internally the void* can be converted to a pointer to a structure
> containing the actual information.

Yes, of course: Even though <stdio.h> must reveal the
nature of a FILE, it does not follow that FILE is easily
decoded. Someone else suggested that FILE could be an int
that indexes arrays of stuff hidden away in the innards of
the library, out of the program's view, and there is no
shortage of other possibilities more and less obscure.

Nonetheless: Whatever a FILE may be, <stdio.h> must
"fully describe" it. That was the crux of my question,
even though the "full description" is useless in practice.

--
Eric Sosman
eso...@acm-dot-org.invalid


Richard Bos

unread,
Jan 2, 2006, 10:18:15 AM1/2/06
to
Jack Klein <jack...@spamcop.net> wrote:

> On 30 Dec 2005 17:12:33 -0800, "Albert"
> <albert.xt...@gmail.com> wrote in comp.lang.c:


>
> > So structures are useful to group variables, so you can to refer to a
> > collection as a single entity. Wouldn't it be useful to also have the
> > ability to collect variable and functions?
> >
> > Ask K&R say, C programs consist of variables to store the input and
> > functions to manipulate them.
> >
> > This would make C object-oriented - how cool would that be?

> It's already been done. They discuss it in comp.lang.c++, down the
> hall. And in comp.lang.java, and I'm sure also in whatever group it
> is on Microsoft's server that is devoted to C#.


>
> I know I'll get flamed for this, but with the exception of inheritance

> this is really nothing but syntactical sugar. You can write object
> oriented programs in C right now.

You'd get flamed for that if you posted it in comp.lang.c++, I have no
doubt. Here, though... I've been agreeing with that for years. Ninety
percent of object orientation is merely what good programmers were doing
decades ago, long before I myself started programming.

Richard

Chris Torek

unread,
Jan 2, 2006, 1:01:56 PM1/2/06
to
In article <lnu0cnh...@nuthaus.mib.org>
Keith Thompson <ks...@mib.org> wrote:
>... 7.19.1 says that FILE is

>
> an object type capable of recording all the information needed to
> control a stream, including [a bunch of stuff]
>
>It doesn't necessarily have to record the information *directly*.

And indeed, the Standard permits the (actual, historical)
System V implementation, in which FILE was a typedef-name
(or perhaps #define) for a structure that did in fact not
contain all of the data.

Specifically, there was a separate, private array with a name
something like "bufend" or "bufsize". The stdio FILE structure
included fields like "pointer", "file number", "flags" or "mode",
and so on, but was missing at least one data item: the size or
end-pointer for each stdio buffer associated with each open file.
Some stdio functions then did something essentially like this:

int some_stdio_function(FILE *fp) {
size_t n = bufend[fp - &__stdio_files[0]] - fp->_base;
...
}

That is, the actual value of the pointer "fp" was important:
fp had to point to one of the 20 or so (FOPEN_MAX) array entries,
and a separate, parallel array (bufend, or whatever its name was)
contained additional data.

This meant that code of the form:

void somefunc(FILE *fp) {
FILE fcopy = *fp;
...
result = fprintf(&fcopy, fmt, arg1, arg2, ...);
...
}

would (sometimes) break badly on some System V Unix systems, despite
working on almost every other Standard C system known to man.

The C Standard permits this breakage, by rendering undefined the
behavior of using "fcopy" as in somefunc() above.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.

Keith Thompson

unread,
Jan 2, 2006, 4:17:19 PM1/2/06
to
jacob navia <ja...@jacob.remcomp.fr> writes:
> Jordan Abel a écrit :
>> On 2006-01-01, Emmanuel Delahaye <em...@YOURBRAnoos.fr> wrote:
>>>A good standard-C-example is the FILE function family.
>>>
>>>Constructors / destructors:
>>>fopen() / fclose()
>>>
>>>Methods:
>>>fgetc() / fputc() / ferror() etc.
>> Speaking of which, I think it would have been better for them to take
>> the FILE* argument first, to match fprintf. However, the reasons for
>> this inconsistency lie forgotten in the depths of history. (It strikes
>> me as especially odd given that UNIX functions that take a file
>> descriptor number do take it first. Anyone here who was at Bell Labs
>> around that time have any comment on this? I forget, does DMR read this
>> newsgroup?)
>
> Ahhh living in the past.
>
> Those never ending historical trivia that seem to have so
> much success here.

Some of us find historical trivia interesting. Apart from being
interesting for their own sake, they can be helpful to understand why
the C language is the way it is, warts and all.

> I tried to start a discussion based in the interface concept
> but it went completely down the drain.

Yes, well, that happens sometimes. We're not obligated to discuss
whatever you find interesting, any more than you're obligated to
discuss what we find interesting.

Having said that, your idea for interfaces
<http://groups.google.com/group/comp.lang.c/msg/857ea8e70333c6c5?hl=en&fwc=1>
does look at least moderately interesting. To summarize:

struct ArrayList;

struct ArrayListInterface {
int (*GetCount)(struct ArrayList *AL);
int (*IsReadOnly)(struct ArrayList *AL);
int (*SetReadOnly)(struct ArrayList *AL, int flag);
int (*Push)(struct ArrayList *AL, void *str);
};

struct ArrayList {
struct ArrayListInterface *lpVtbl;
size_t count;
void **contents;
size_t capacity;
unsigned int flags;
size_t ElementSize;
};

(This isn't identical to the code you posted; I've fixed some syntax
errors and dropped the typedefs in favor of using struct tags
directly.)

One difficulty with this approach, as compared to the equivalent in a
language like C++ that support OO directly, is that you have to
manually build and initialize the function table yourself; the
compiler won't do it for you. If you forget to do this, Bad Things
Happen; for example:

struct ArrayList obj;
int count = obj.lpVtbl->GetCount();

invokes undefined behavior. But that's not a fatal flaw; it's easy
enough to declare an object that provides a default initial value:

const struct ArrayListInterface ArrayListInterfaceDefault = {
...
};

struct ArrayList ArrayListDefault = {
&ArrayListInterfaceDefault,
0,
...
}

and then:

struct ArrayList obj = ArrayListDefault;
int count = obj.lpVtbl->GetCount();

will consistently give you 0.

I think this kind of thing is often overkill if you don't actually
need more than one implementation of, say, the GetCount() function for
your type. Plain old type and function declarations, with no attempt
to encapsulate the functions in the types, is very often perfectly
adequate. But when it isn't, this seems like a reasonable approach.

(Please note that I'm discussing your idea because I find the idea
itself interesting, not because I'm influenced by your complaints
about other things being trivial.)



> The 8086, the PDP7, that is MUCH more fun. The only answers
> I got pointed to some triviality. The discussion here never
> gets to any substantive discussion about modern software construction.

Maybe you should try comp.programming.

jacob navia

unread,
Jan 2, 2006, 4:46:16 PM1/2/06
to
Keith Thompson a écrit :

>
> One difficulty with this approach, as compared to the equivalent in a
> language like C++ that support OO directly, is that you have to
> manually build and initialize the function table yourself; the
> compiler won't do it for you. If you forget to do this, Bad Things
> Happen; for example:
>
> struct ArrayList obj;
> int count = obj.lpVtbl->GetCount();
>
> invokes undefined behavior. But that's not a fatal flaw; it's easy
> enough to declare an object that provides a default initial value:
>
> const struct ArrayListInterface ArrayListInterfaceDefault = {
> ...
> };
>
> struct ArrayList ArrayListDefault = {
> &ArrayListInterfaceDefault,
> 0,
> ...
> }
>
> and then:
>
> struct ArrayList obj = ArrayListDefault;
> int count = obj.lpVtbl->GetCount();
>
> will consistently give you 0.

Normally you would call a constructor function that returns a pointer
to an allocated structure.

The advantage of this is the flexibility it gives you. You can change
the behavior of a function at run time, by assigning the function
pointer to another function. That function can call the old function
pointer and then do something special, or can reimplement the
old function completely. And this is achieved without having to change
a single line in user code.

The array list object is patterned like its name-sake in C#: a flexible
array that can hold items and grows automatically if needed.

Another plus point is that the name-space of the interface is private
to the containing structure, so you can use simple names like "Add"
or "Delete" without fear that it will clash with the user name space.

jacob

Dave Thompson

unread,
Jan 11, 2006, 1:01:26 AM1/11/06
to
On Sun, 01 Jan 2006 08:26:19 -0500, Eric Sosman
<eso...@acm-dot-org.invalid> wrote:
<snip>

> > Any library function may be implemented as a macro. I think assert()
> > is the only one that's required to be a macro.
>
> There's also setjmp().
>
setjmp() isn't formally required; on some platforms (particularly
calling conventions) there's no good way to make it a true function,
but it could still be a compiler special-case rather than a macro.

va_start and va_arg are required to be macros (and by their nature
cannot be implemented as true functions). va_end and C99 va_copy are
unspecified. In C99 tgmath.h and the new-in-math.h generic
fpclassify/is{finite,inf,normal,nan}/signbit and isgreater etc. are --
even though their functionality doesn't really make sense as macros
and will probably end up being compiler special-cases anyway.

> However, that wasn't what I asked. Jack Klein wrote that
> the Standard *requires* some functions to be implemented as
> macros, and this is true. However, I was unable to think of

> any FILE-using functions that are *required* to be macros. <snip>


> I'm well aware that the Standard *permits* an implementation
> to provide macros for any library functions it chooses, so long
> as it provides the actual function as well. I'm also aware that
> the Standard makes special provision for getc() and putc() (but
> not for getchar(), by the way) that make macro implementations
> easier. But does the Standard *require* a macro implementation
> for any FILE or FILE* function?
>

Concur.

> ... and what this all comes down to, of course, is whether
> FILE can be a fully opaque type. I believe it can be, but both
> Jack Klein and Richard Heathfield (two not-to-be-ignored people)
> take the opposite view. I'm trying to learn why.

Except that it must be an object (=complete) type and thus must have a
definite size. It wasn't really necessary and perhaps wasn't wise to
standardize even that much, but thatt's water far out to sea now.

- David.Thompson1 at worldnet.att.net

0 new messages