GOPHERSPACE.DE - P H O X Y
gophering on hngopher.com
HN Gopher Feed (2017-10-20) - page 1 of 10
 
___________________________________________________________________
Object oriented programming with ANSI-C (1993) [pdf]
78 points by geospeck
https://www.cs.rit.edu/~ats/books/ooc.pdf
___________________________________________________________________
 
kuwze - 2 hours ago
Reminded me of the  C Object System by Laurent Deniau[0].[0]:
http://ldeniau.web.cern.ch/ldeniau/cos.html
 
wybiral - 1 hours ago
One thing that was really educational for me was reading how
CPython implemented their object system. https://github.com/python/
cpython/blob/master/Include/object...Basically objects are structs
that all share the same header which contains a pointer to the
object type (ob_type) and the reference count (ob_refcnt) for
memory management.That way you can always cast back to a pointer of
that header struct and access ob_type and ob_refcnt no matter what
the actual implemented struct contains afterwards.ob_type contains
the name of the type and function pointers for common methods used
by objects (getattr, setattr, hash, arithmetic, etc).
 
yawaramin - 3 hours ago
The first chapter, on abstraction and information hiding, is
probably the most important. Every programmer should know how to do
it. It's one of C's main strengths that it allows abstract data
types so easily. ADTs in fact allow better binary compatibility
since you only expose pointers as your public API, not structures
of different shapes and sizes.I'm continually surprised by how
often people insist on putting struct definitions right in their
header files, defeating the whole idea of type abstraction.
 
  megous - 2 hours ago
  > I'm continually surprised by how often people insist on putting
  struct definitions right in their header files, defeating the
  whole idea of type abstraction.It also defeats the idea of easy
  struct embedding as is done for example in the Linux kernel.
  Different codebases, different needs.
 
  rmind - 59 minutes ago
  > I'm continually surprised by how often people insist on putting
  struct definitions right in their header files, defeating the
  whole idea of type abstraction.In general, I totally agree with
  you that it is best to keep the structure definition in the
  translation unit (the .c file) and opaque structure declaration
  in the header.  However, there are some cases when it is not very
  practical, e.g. when you you want to embed the structure into
  another (for performance reasons or cases like on-disk
  structures, etc) or when you want to separate out some logic into
  another .c file (think of separation of concerns), instead of
  having one massive .c file.When there is such need, I generally
  create two headers files, e.g. foo.h (for the public API) and
  foo_impl.h (structure definition and related stuff), with
  something like:    #if !defined(__FOO_PRIVATE)     #error
  "foo_impl.h should only be included by the internal foo modules"
  #endif
 
  sparkie - 2 hours ago
  You need to define a struct in a header to pass by value, which
  is usually cheaper than having to dereference a pointer (often
  resulting in a cache miss). The style presented in the paper has
  several pointer dereferences, for example each type has another
  pointer to a class header which contains its constructor and some
  other function pointers.I personally do not like the style in
  this paper as it makes APIs difficult to read - you need to
  resort to documentation to understand anything. In the first set
  example:    #ifndef SET_H     #define SET_H     extern const void
  * Set;     void * add (void * set, const void * element);
  void * find (const void * set, const void * element);     void *
  drop (void * set, const void * element);     int contains (const
  void * set, const void * element);     #endif  What type is add
  expecting and what is it going to return? We have no clue, other
  than to continue reading the docs.A "better" approach to an
  opaque pointer type is to simply declare a struct in the header,
  but only define it in the implementation file. The above API
  becomes this, where it's pretty obvious from the arguments and
  return types (it also avoids having to do an explicit manual cast
  in each of the methods):    #ifndef SET_H     #define SET_H
  #include "Object.h"     typedef struct set_t Set;     Object*
  set_add (Set*, const Object* element);     Object* set_find
  (const Set*, const Object* element);     Object* set_drop (Set*,
  const Object* element);     int set_contains (const Set*, const
  Object* element);     Set* set_alloc(void);     void
  set_free(Set*);     #endif  Obviously, this doesn't play well
  with the full OOP approach this paper takes, but IMO, it's better
  to just compose structs using the style above, and leave memory
  management up to the type rather than trying to have a fancy
  global "new" and pointers to constructors.
 
    yawaramin - 2 hours ago
    Yeah, when you can't afford the cache misses I understand that
    you would pass by value. But often, these things are done as
    premature optimisations. We can always expose a struct's
    definition later; but we can't hide it once it's exposed.I
    agree with your API redesign, in fact if we forget about trying
    to do OOP we would get IMO even nicer design:    typedef struct
    set_t* set;     typedef void* set_elem;      set set_new(void);
    void set_free(set);     set_elem set_add(set, set_elem);
    set_elem set_find(set, set_elem);     set_elem set_drop(set,
    set_elem);     int set_contains(set, set_elem);  Edit: hiding
    the pointer in the typdef because if we later decide to expose
    the set struct, we minimise the required changes to the API.
 
  Koshkin - 3 hours ago
  > It's one of C's main strengths that it allows abstract data
  types so easily.On the contrary, data type abstraction in C is a
  pain. This was one of the reasons for creation of "C with
  classes" (a.k.a. C++).
 
    dangerbird2 - 2 hours ago
    The only things that are particularly painful about opaque
    types in C is that you can't easily use stack-based memory to
    allocate opaque objects. This of course is true for C++ opaque
    types, but people tend to rely on private fields of concrete
    types instead of "PIMPL" struct declarations, even though the
    latter is arguably better for binary compatability and
    information hiding.
 
    ASalazarMX - 2 hours ago
    I prefer to call it "C Gone Wild".
 
    catnaroek - 3 hours ago
    Not that C++ is particularly good at data abstraction either.
 
  hawski - 2 hours ago
  I was thinking lately that C is more data-oriented than many.
  However I'm limited in knowledge mostly to imperative
  languages.What I mean is that defining arrays of structs has very
  light syntax. For example in Python AFAIK you can't easily do
  this. You have to use dictionaries (which is cumbersome if you
  have many rows) or some kind of class/module that will make it
  even less clear. What Suckless [1] guys are doing where you can
  use C as configuration language is example of it. Like
  keybindings configuration [2] in dwm [3]:  static Key keys[] = {
  /* modifier                     key        function
  argument */         { MODKEY,                       XK_p,
  spawn,          {.v = dmenucmd } },         { MODKEY|ShiftMask,
  XK_Return, spawn,          {.v = termcmd } },         { MODKEY,
  XK_b,      togglebar,      {0} },         { MODKEY,
  XK_j,      focusstack,     {.i = +1 } },         { MODKEY,
  XK_k,      focusstack,     {.i = -1 } },         /* ... */   };
  When I do something in Python I miss this. C++ most likely will
  require doing some playing around with constructors.Also with C99
  it's only sweeter. When you need you can use designated
  initializers [4] - putting names of fields or indices of array in
  initialization list. You can use compound literals [5] to obviate
  some need for constructors.[1] https://suckless.org/[2]
  https://git.suckless.org/dwm/tree/config.def.h[3]
  https://dwm.suckless.org/[4] https://gcc.gnu.org/onlinedocs/gcc
  /Designated-Inits.html[5] https://gcc.gnu.org/onlinedocs/gcc
  /Compound-Literals.html
 
    geofft - 1 hours ago
    Take a look at https://github.com/python-attrs/attrs which will
    let you do something like    import attr      @attr.s     class
    Key(object):          modifier = attr.ib()          key =
    attr.ib()          function = attr.ib()          argument =
    attr.ib()      keys = [         Key(MODKEY,           XK_p,
    spawn, {'v': dmenucmd}),         Key(MODKEY|ShiftMask,
    XK_Return, spawn, {'v': termcmd}),         # ...     ]  which
    gets you the concise and readable syntax of C structs, while
    keeping these things as actual attributes as if they were
    classes (e.g., [key.modifier for key in keys] will work).And
    you get designated initializers via Python's usual keyword-
    argument syntax, the ability to specify attr.ib(default=...),
    and a few other things.
 
  catnaroek - 3 hours ago
  Not that object-oriented languages are particularly good at it,
  but defining and using abstract data types in C is a pain:(0)
  Forward declarations are the only way not to explicitly reveal
  the representation of types, but you have to pay an indirection
  tax for it, whether you actually need indirection or don't.(1)
  Without heavily abusing the preprocessor, there is no way to
  express the fact that two abstract data types expose the same
  interface.(2) Again, without heavily abusing the preprocessor,
  there is no way to make common algorithms work on abstract data
  types that have a common interface.
 
    yawaramin - 1 hours ago
    (0) True, but the default assumption really should be that the
    indirection tax is fine. There are plenty of non-realtime C
    apps. If you're really seeing a slowdown, then by all means
    trade off some of that abstraction power.(1) & (2) You could
    use conversion functions, no? E.g.:    typedef struct
    ordering_t* ordering;     typedef struct person_t* person;
    typedef struct business_t* business;      ordering
    ordering_of_person(person);     ordering
    ordering_of_business(business);     void* sort(void*,
    ordering);
 
sesteel - 4 hours ago
This is one of my favorite reads.  About 10 years ago I was
developing a toy programming language that compiled to ANSI-C and
this was a great resource to me at the time.
 
Koshkin - 4 hours ago
  > void * find (const void * _set, const void * _element)  Such an
eye sore, and I think it goes strongly against the tradition of
coding in C when someone uses the asterisk in the pointer context
in such a way that makes it look like an infix operator, i.e. as if
it was multiplication.As to achieving a reasonably looking OO in C,
this is why C++ was born...
 
kasajian - 41 minutes ago
Another article related to this:
https://www.codeproject.com/Articles/22139/Simply-Object-Ori...I
wrote it a while back just to see what I would come up with if I
had to solve that problem for myself using only macros.