views:

1037

answers:

10

When I am presented with programming problems, I naturally start breaking them up into logical objects in my head. Who has what responsibility, who owns what, who derives from what, etc.

I am struggling with C. I just don't get how to do things in a Procedural Language.

Can an experienced C programmer help explain how I should think about my programs during design time?

For example, I want to write my own Semaphore class. I would naturally need a Queue data structure for my program, which I would like to write myself as well. If I needed to do this in Java or C#, I could simply whip up a quick Queue class and create a new instance of it in my Semaphore class.

But in C, there aren't objects. So do I have to inline all the behavior of my Queue data structure?

Can someone help me "get it"?

Related: what is the best way to plan and organize development of an application in c

+14  A: 

You can still think object oriented with C.

You just need to create a struct and a set of functions that take a pointer to an instance of that struct as its first parameter.

As for polymorphism, you can pass the size of the struct as the first member of the struct, so you know how to cast it.

There is a great pdf of object oriented programming with ANSI-C here.

Brian R. Bondy
+26  A: 

But in C, there aren't objects. So do I have to inline all the behavior of my Queue data structure?

No.

Do this.

  1. Define your class however you feel comfortable doing OO design.

  2. Write the attributes of your class as a C-language struct.

  3. Put that struct in a header file, along with all of the functions that operate on that struct. Make sure a MyStruct * self is the first argument to all of these "method functions".

  4. Write a C module with all of the bodies of the method functions.

Poor-person's OO in C. It works well. Just be disciplined about putting everything into the struct that you need -- public and private instance variables -- everything.

Generally, avoid trying to have private variables in the first place. You don't have the full power of an OO compiler, so don't bother with low-value features like "private" or "protected".

S.Lott
leading underscores are reserved and should not be used.
Evan Teran
Depending on how your api is defined you can also use variations on PIMPL to define private variables, or casting to/from "char reserved[SOME_SIZE]" so the user can't ever see what the private variables are.
Greg Rogers
@evan names with leading underscores are only reserved at global scope, not for struct members
anon
Good point. When doing per-person's OO things like private and protected are more trouble than they're worth.
S.Lott
I think private/public could be emulated using #define and #undef around interface declarations.
Brian R. Bondy
@Brian R. Bondy: perhaps -- but it's unlikely to be worth the extra complexity.
S.Lott
@S.Lott: I agree
Brian R. Bondy
You can also fake some parts of inheritance using composition.
Ryan Graham
@Ryan Graham: essentially, all you have is composition. You can try some clever #define techniques to give you inheritance-like features. I think composition is easier to deal with.
S.Lott
I have done similar to this before I learned Java; but I used opaque pointers and made all variables private. Methods were used to expose public data - IMHO that's less error-prone and superior to exposing the variables in the header.
Software Monkey
@Software Monkey. I'm not sure that it's worth the effort to assure that the pointers are truly opaque. There's enough fooling around required just to be sure that you have `struct *self` everywhere.
S.Lott
+10  A: 

I would amend S. Lott's answer to use an opaque pointer to perform data hiding of the members of the struct:

  1. Define your class however you want using normal OO design.
  2. Member variables of your class go into a C language struct.
  3. In the header file, you do not want to expose the member variables of your object (since these would be "private" in an OO language). Instead, use an opaque pointer, i.e.
    typedef struct mystruct_s *mystruct_t; // first argument to all your methods
  4. For all the methods you want to be "public", put their signatures in your .h file. Method bodies should go into the .c file, and "private" methods should be only defined in the .c file and also declared static so their symbols do not collide with symbols defined in other files.

Clever naming conventions like underscores are unnecessary using this method, but it means that all your member variables will be private. Functions can be public or private, although public functions they are part of a global namespace so you might want to qualify their names with a "package" name like mystruct_push(), mystruct_pop(), etc.

You also need to make it clear if the caller or the library is responsible for calling malloc() and free(). Most likely you will have mystruct_t *create() and void destroy(mystruct_t *target) methods.

sk
I agree; I had voted up S. Lott's answer, but moved my vote to this because hiding the structure's contents is the best way to go - I have done exactly this many times and it works *really* well.
Software Monkey
A: 

I second the suggestions for doing "Poor man's OO in C." I also think you might benefit from taking some time to see how Perl's OO works. Basically OO is accomplished in Perl by having the interpreter supply every method with the instance as an implicit first parameter. You'll want to do the same in C, explicitly, and use really, really good code organization since the compiler will not enforce good encapsulation for you.

By the way, you can enforce keeping the members of your structs private by using opaque pointers. I seem to recall that the GNU programming standards and recommendations included a technique for doing this by basically casting everything to void* when it was passed around, then using typedefs to name each specific type of opaque pointer that was supposed to be passed. (i.e., each "class")

skiphoppy
+2  A: 

Check out the Lua C api. It has been my guiding light as far as C interface design goes. Every function takes a Lua state as a leading argument which becomes your "this". Inheritance is a little trickier but Chipmunk manages to do a pretty good job exposing functions that take a generic shape structs and work out the details of which function is actually called via a "klass". You can often exploit void* to have functions take different types (structs) the way you would overload in OO. It can feel a little hackish at times but works well.

Nick
+2  A: 

I had a bear of a time moving from procedural to OO thinking, so I feel your pain.

I found that in order to learn how to create objects, it was best to think about how they would look to the caller. The same approach might help you, going the other way. Consider what the API to your component would look like. One good way is to study existing C APIs (I used the standard Java APIs as a set of examples of an OO API).

You're used to using a queue component something like this:

import some.package.Queue;

Queue q = new Queue(); 
q.add(item);

In a typical C api, you'd expect something more like:

#include <queue.h> // provides queue, make_queue(), queue_add(), others

queue q = make_queue(); // queue is probably a struct, or a struct*
queue_add(q,item);

Whenever you find yourself thinking in objects, make a similar transform.

You can use pointers to functions and the like, to make object-like structures in C - but many thousands of C programmers have managed without.

Good luck!

slim
+1  A: 

Originally C++ was just a compiler that wrote C code from the C++ sources; those were then compiled by the native C compiler, linked, etc.

Hence, all OOP methods are available in C -- it's just that the compiler won't help you, and does not provide all the compile-time capabilities such as templates, operator overrides, data hiding, etc.

  1. The C++ "struct" was (probably still is) equivalent to a "class" with all members "public".
  2. Member functions can be implemented as function pointers in a structure; this can provide encapsulation and polymorphism. Constructors exist in the global scope unless you're using factories. Destructors can be member functions.
  3. Using accessor member functions, like getColor(), setColor() can alleviate internal differences and provide some data hiding. If you really want, you could use Brian Bondy's suggestion of hiding with macros.
  4. There are actually a lot of FOSS libraries that provide standard containers -- hash tables, dynamic arrays, linked lists, etc.
NVRAM
+1  A: 

You can do derived classes also with C:

http://stackoverflow.com/questions/660083/derived-classes-in-c-what-is-your-favorite-method

Trevor Boyd Smith
A: 

use glib, c library with oop http://library.gnome.org/devel/glib/2.20/

plan9assembler