some thoughts on object oriented design in C

193 views
Skip to first unread message

Ondrej Certik

unread,
Dec 26, 2007, 11:58:43 AM12/26/07
to sy...@googlegroups.com
Hi,

I was thinking a little bit how to code SymPy in C. I want to use OOP,
since that's what I am used to in Python. So C++ would be a natural
choice.
But then one notices that almost all robust programs are done in C,
like Python, or the linux kernel. In our case, we want to extend
Python,
and all other good Python libraries use C (like numpy, or Mercurial,
etc.). So there are probably good reasons for that.

So I was looking how to do OOP in C. I found this paper from 1993:

http://www.cs.rit.edu/~ats/books/ooc.pdf

it explains how to code OOP in ansi C, I read through it, but it's too complex.
Then I found this really interesting page:

http://ldeniau.web.cern.ch/ldeniau/html/oopc.html

Look at the C Object System (COS), which is a set of macros in C, that
allows you to program like in C++. There is also a draft paper how it works,
but it's slower than g++. It's nice, but it's just too academic and noone use it
(yet)...

Then I decided to look at some good, real life programs. I found this:

http://barracudaserver.com/WP/DeviceControl/OOIntro.html

and that's what I like. It's simple and it does the job and it solves
a real problem,
not some academic one. Here is an example of the same C and C++ code
side by side:

http://barracudaserver.com/WP/DeviceControl/C_and_CPP_source.html

That shows, it's really the same thing.

Here is another example of C and C++ code side by side:

http://www.eventhelix.com/RealtimeMantra/basics/object_oriented_programming_in_c.htm

So the only thing, that seems more complicated in C compared to C++ is
virtual functions invocations:

http://www.eventhelix.com/RealtimeMantra/basics/ComparingCPPAndCPerformance2.htm

where one needs to handle VTable manually, but as shown, it's not
really difficult or messy. The only thing I don't like is that one
needs to use numbers (e.g. pVTable[1]) to access the correct method:

C++: pShape->Draw();
C: (pShape->pVTable[0].pFunc)(pShape);

C++: pShape->MoveTo(100, 100);
C: (pShape->pVTable[1].pFunc)(pShape, 100, 100);

Which is error prone. But one can make macros, so that one can write

Shape_MoveTo_virtual(pShape, 100, 100)

and it will get translated to

(pShape->pVTable[1].pFunc)(pShape, 100, 100);

But still I find it complex. Let's make it more simple. So I digged a
little more and I found this:

http://flinflon.brandonu.ca/dueck/1997/62285/virtualc.html

And that's finally what I was looking for. It's simple, explicit, pure
C and fully object oriented with virtual functions and VMT.

Do you know about some opensource pure C program, that uses this
design, so that we can learn from it?

Ondrej

Ondrej Certik

unread,
Dec 27, 2007, 3:20:53 PM12/27/07
to sy...@googlegroups.com

So Kirill suggested it's the gobject in glib (used by gtk, gnome, gimp, etc..):

http://library.gnome.org/devel/gobject/unstable/

So there are a lot of programs to study.

And when one doesn't neet the whole machinery (it's probably an
overkill for our purposes),
one can just use structs with function pointers, as described in my
previous email.

Ondrej

Pearu Peterson

unread,
Dec 27, 2007, 4:17:53 PM12/27/07
to sympy
On Dec 27, 8:20 pm, "Ondrej Certik" <ond...@certik.cz> wrote:
>
> And when one doesn't neet the whole machinery (it's probably an
> overkill for our purposes),
> one can just use structs with function pointers, as described in my
> previous email.

I think that even writing sympy in C/C++ is an overkill. Usually
one needs to do some profiling to find out which parts of a program
are computationally expensive and then write optimized extension
modules only for those parts.

Pearu

Kirill Smelkov

unread,
Jan 1, 2008, 6:10:00 AM1/1/08
to sy...@googlegroups.com

First of all, sorry for not replying for so long...

Ondrej, I think I agree with Pearu here -- we do not need full OOP and
we only need to write only small parts in C.

Also, look at any python module, or Python itself:

Consider for example Python list.

- it inherits from object, so it's struct starts with macro

PyObject_VAR_HEAD

Think of it as of "list : object" in C++

- Then we have PyList_Type which describes how to allocate/deallocate
list objects, method table, etc...

- Then we have functions like PyList_New, PyList_GetItem, etc, to
construct objects, acces their elements, etc...

- 'virtual' methods are declared in list_methods (which is referenced
from PyList_Type.tp_methods)


So, why not to stick to Python approach?

BTW: Cython also allows to define new Python types which is cool!


--

Also, if we need pure C virtual functions, something like following will
do:

typedef struct {
struct BasicMeth *vtable;

/* blah-blah-blah */
} Basic;

typedef struct {
Basic_VAR_HEAD /* this will include common data members from basic
*/

PyObject* args;
} Add;


/* later, e.g. */
Basic *expr = Add_New(...);

expr->vtable->expand(expr);

--
Всего хорошего, Кирилл.
http://landau.phys.spbu.ru/~kirr/aiv/

Ondrej Certik

unread,
Aug 7, 2008, 7:00:43 AM8/7/08
to sy...@googlegroups.com

But this only allows exactly one subclass of PyObject. Otherwise the
subclass would have to also provide this kind of macro.

>
> Think of it as of "list : object" in C++
>
> - Then we have PyList_Type which describes how to allocate/deallocate
> list objects, method table, etc...
>
> - Then we have functions like PyList_New, PyList_GetItem, etc, to
> construct objects, acces their elements, etc...
>
> - 'virtual' methods are declared in list_methods (which is referenced
> from PyList_Type.tp_methods)
>
>
> So, why not to stick to Python approach?

You can handle all methods in the vtable, ok.

But then you still need to pass all variables in the struct as macros,
so that derived objects can declare them as the first members in the
struct.
Without using macros, you'd have to copy the same change in the Basic
object to all derived objects by hand. And with using macros, I don't
find it very convenient.

>
> BTW: Cython also allows to define new Python types which is cool!
>
>
> --
>
> Also, if we need pure C virtual functions, something like following will
> do:
>
> typedef struct {
> struct BasicMeth *vtable;
>
> /* blah-blah-blah */
> } Basic;
>
> typedef struct {
> Basic_VAR_HEAD /* this will include common data members from basic

The problem with Basic_VAR_HEAD is that it would have to contain the
whole basic struct, but as a macro. Maybe without the vtable.
I don't find it very convenient to use C macros for the definition of
the whole class.

> */
>
> PyObject* args;
> } Add;
>
>
> /* later, e.g. */
> Basic *expr = Add_New(...);
>
> expr->vtable->expand(expr);

I put my code here:

http://wiki.sympy.org/wiki/Playing_with_SymPy_design_in_C

The disadvantage of it is that if you add some private variable to the
basic struct, you would have to add it by hand to all the derived
classes. Which is not the way. Or use macros, which is imho not a way
either. Or just use C++.

Ondrej

Reply all
Reply to author
Forward
0 new messages