tags:

views:

372

answers:

7

Having learned Java and C++, I've learned the OO-way. I want to embark on a fairly ambitious project but I want to do it in C. I know how to break problems down into classes and how to turn them into class hierarchies. I know how to abstract functionality into abstract classes and interfaces. I'm even somewhat proficient at using polymorphism in an effective way.

The problem is that when I'm presented with a problem, I only way I know how to do it is in an Object-Oriented way. I've become too dependent on Object-Oriented design philosophies and methodologies.

I want to learn how to think in a strictly procedural way. How do I do things in a world that lacks classes, interfaces, polymorphism, function overloading, constructors, etc.

How do you represent complex concepts using only non-object-oriented structs? How do you get around a lack of function overloading? What are some tip and tricks for thinking in a procedural way?

+1  A: 

I think you have a good plan. Doing things the completely OO way in C, while quite possible, is enough of a pain that you would soon drop it anyway. (Don't fight the language.)

If you want a philosophical statement on mapping the OO way to the C way, in part it happens by pushing object creation up one level. A module can still implement its object as a black box, and you can still use reasonable programming style, but basically its too much of a pain to really hide the object, so the caller allocates it and passes it down, rather than the module allocating it and returning it back up. You usually punt on getters and setters, or implement them as macros.

Consider also that all of those abstractions you mentioned are a relatively thin layer on top of ordinary structs, so you aren't really very far away from what you want to do. It just isn't packaged quite as nicely.

DigitalRoss
A: 

The standard way to do polymorphic behavior in C is to use function pointers. You'll find a lot of C APIs (such as the standard qsort(3) and bsearch(3)) take function pointers as parameters; some non-standard ones such as qsort_r take a function pointer and a context pointer (thunk in this case) which serves no purpose other than to be passed back to the callback function. The context pointer functions exactly like the this pointer in object-oriented languages, when dealing with function objects (e.g. functors).

See also:

Adam Rosenfield
+1  A: 

The C toolkit consists of functions, function pointers and macros. Function pointers can be used to emulate polymorphism.

StackedCrooked
+1  A: 

You are taking the reverse trip old C programmers did for learning OO.

Even before c++ was a standart OO techniquis were used in C.

They included defining structs with a pointer to srtuct (usually called this...) Then defining pointer functions in the struct, and during runtime initialize those pointers to the relevant functions. All those functions received as first paremeter the struct pointer this.

Am
"this" has no sense as a member of the structure, but as a parameter in the function calling convention (usually the first).
fortran
u can also call it me, self, or anything you like. but naming it this might make more sense if you are familiar with OO.
Am
Naming it `this` has the disadvantage that you can never compile the code with a C++ compiler (which considers `this` a keyword). And there are OO languages out there which call it `self` and (IIRC, anyway) `me`. If you have a _general_ OO background (as opposed to a C-derived OO language background), `this` doesn't necessarily make more sense than other names.
sbi
good point, i agree
Am
@sbi: Naming it 'this' has the *advantage* that you cannot compile the code with a C++ compiler. Code written as C should be compiled with a C compiler. If it needs to be used in a C++ project, it should be compiled with a C compiler and linked in. C and C++ are link compatible, and there is (almost) never any valid reason to compile C code with a C++ compiler. Structuring the code so that doing so is impossible may therefore be a good thing.
William Pursell
@William: You do have a point there, too, but I can't decide which side I lean to. (And I don't need to, since I usually don't write C myself.)
sbi
+4  A: 

The procedural way is to, on one side, have your data structures, and, on the other, your algorithms. Then you take your data structures and pass them to your algorithms. Without encapsulation, it takes a somewhat higher amount of discipline to do this and if you increase the abstraction level to make it easier to do it right, you're doing a considerable part of OO in C.

sbi
A: 

Don't think you have to shelve your knowledge of object-oriented work - you can "program into the language".

I had to work in C after being primarily experienced in object-oriented work. C allows for some level of object concepts to pull through. At the job, I had to implement a red-black tree in C, for use in a sweep-line algorithm to find the intersection points in a set of segments. Since the algorithm used different comparison functions, I ended up using function pointers to achieve the same effect as lambdas in Scheme or delegates in C#. It worked well, and also allowed the balanced tree to be reusable.

The other feature of the balanced tree was using void pointers to store arbitrary data. Again, void and function pointers in C are a pain (if you don't know their ins and outs), but they can be used to approximate creating a generic data structure.

One final note: use the right tool for the job. If you want to use C simply to master procedural technique, then choose a problem that is well-suited to a procedural approach. I didn't have a choice in the matter (legacy application written in C, and people demand the world and refuse to enter the 21st century), so I had to be creative. C is great for low/medium abstractions from the machine, say if you wanted to write a command-line packet inspection program.

emptyset
+1  A: 

Don't think C in the complete OOP way. If you have to use C, you should learn procedural programming. Doing this would not take more time than learning how to realize all the OOP features in C. Furthermore, basic encapsulation is probably fine, but a lot of other OOP features come with overhead on performance when you mimic them (not when the language is designed to support OOP). The overhead may be huge if you strictly follow the C++ design methodology to represent every small things as objects. Programming languages have specific purposes in design. When you break the boundary, you always have to pay something as the cost.