Nerd wannabe

lazy weblog

Why OOP failed on us

with 6 comments

[updated 21 Jan 2008: added more about vala]
[clarification: “us” = those hacking software in their spare time – except web software – and want to ship]

hack -> ship sketch

C++ was promising everything, 15 years ago. Over C, I mean.

Everybody started declaring the variables where it crossed their mind, overloading functions and giving default values, and from time to time they played with the class keyword. Oh, and operators, never forget!
When everybody started thinking in classes, they started to play with the template keyword.

Borland seems to have closed the Delphi business but freepascal and Lazarus are still alive nowadays.

In the other news, starting from ’95 there was Java, which was also OOP. The Smalltalk people were not that company-backed to be successful and nobody seemed to write in Objective-C than Steve Jobs for his NeXT computer. Simula.. I never heard of simula!

Then Web started kicking in, and php (3, then 4, then 5), perl, python all came along. Now ruby, and probably the next big interpreted language is right next door.

Then there’s C#. And J#, And F#, never mind VB (.net, not #). All running on virtual machines.

I will make a big step ahead and tell you:

All software on your machine is written in C.*

Or in C++/Delphi, but linked with C libraries.

* not all of it, but 99%

Nobody (that I know) ever downloads a program in python. Never mind Java, C#, or.. hell, desktop applications in PHP:) Even LISP lacks a great compiler – and nowbody downloads “runtime environments“.

This is OOP: no matter how much syntactic sugar you’re pouring into a language, it all boils down to interoperating with existing libraries, linking against them, calling them (and be called) using their calling conventions, registers, stack and even their byte order. OOP is none of these.

What OOP is is virtual tables, polymorphism, closures, multiple inheritance, reusability etcetera etcetera.

But nobody found a way to instantiate a class from a compiled library… It all boils down to processors, Turing/Von Neumann machines and assembler.

You can have this problem solved in two ways:

  • write an interpreter in C. This abstracts you from the environment soon enough. Re-invent all the basic functions of your environment in the interpreted language.
  • write your own abstraction layer as a runtime environment. You can do whatever you like in that environment, but not outside it. You end up re-writing 50% of the operating systems you’re running on .. in classes.
  • use an existing abstraction layer: use C to abstract away from system calls and use something different to abstract you away from C.

C++ did that (the last). The first C++ compiler was a C preprocessor. Because of the operator overloading, function overloading, classes and template crap, it had to do some name mangling, but it did it. Then along came true C++ compilers, then exceptions etc. Nothing was standard anymore, except the extern “C” { directive. It miserably failed to inter-operate with compiled libraries.

Java, C# did the second: they wrote their runtime environment just to cope with portability. Interoperatibility was penalized ( JNI? COM/Interop? ha.. ha.. ). Although calling native libraries was possible, the download for the runtime environment was a show-stopper: nobody wanted your kilobits of C# because of the hundreds megabytes of your supporting virtual machine..
Therefore they are used server-side.

PHP, perl, python, ruby chose the first: interpret for the masses! Basic did that, and it had the advantage of only two datatypes: string or number. But the costs.. Yeah, interpreted languages have their cost, like.. no threading machine – these (multicore) days.. Nobody wants to download perl.exe for their favorite money calculator..
Therefore they are used server-side.

So, what’s left? is winamp made in C? is Total Commander made in C? What about Firefox??

Tell you what: winamp is indeed C. TC is Delphi. and Firefox is XUL+JavaScript+XPCOM, where XPCOM is made in C.

What does C and delphi have in common? No name mangling. Delphi was proprietary, that’s why.. (VB6.0 didn’t have this problem either)

So how do you do to still program OOP and do it in C ? I challange you: http://www.google.ro/search?q=C+oop

All your OOP code could be done in assembler (in C, however, it’s portable)

In the same way. Given that your runtime environment or your interpreter still runs on a Turing machine, the same thing can be done in C. Manually.Well, why would you do that??

Well, you may be Mozilla Foundation and you may be building browsers for all platforms. XPCOM is OO, of course.
Or you may be Jack the coder, and ‘you know jack’ about MacOS X, but would still want to compile there. Oh, and there’s an AI engine you’re doing that needs performance..

Why, why am I defending C? it’s 2008.. and processors aren’t speeding up anymore (acutally they are lagging up)

And there’s a library, yes right, that exports some symbols (C _cdecl symbols), and using it with a .h file can enable you to write OOP in C. It’s called GObject.

You can write shared libraries that export interfaces, abstract classes and classes for other people to use.

That is, C libraries, but without the burden of manually exporting “C” functions (this is the case with C++) or manually writing a wrapper library that initializes the .NET runtime environment or the JVM.

That means that you can build on existing software and software can build upon you, something other OOP solutions only do “entre eux“, in-house, in their own environment or even only with sources.

Also, GObject solves the name mangling problem, being in C, there is no name mangling. It’s just you that decides the names of the functions.

But how would you do that??

You could start to write boilerplate code like this header (scroll down over it, it’s just an example):

#ifndef __EXAMPLE_H__
#define __EXAMPLE_H__

#include
#include
#include
#include 

G_BEGIN_DECLS

#define TYPE_EXAMPLE (example_get_type ())
#define EXAMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_EXAMPLE, Example))
#define EXAMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_EXAMPLE, ExampleClass))
#define IS_EXAMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_EXAMPLE))
#define IS_EXAMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_EXAMPLE))
#define EXAMPLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_EXAMPLE, ExampleClass))

typedef struct _Example Example;
typedef struct _ExampleClass ExampleClass;
typedef struct _ExamplePrivate ExamplePrivate;

struct _Example {
	GObject parent_instance;
	ExamplePrivate * priv;
	char* myfield;
};
struct _ExampleClass {
	GObjectClass parent_class;
	void (*myfunction) (Example* self);
};

void example_myfunction (Example* self);
Example* example_new (void);
char* example_get_myproperty (Example* self);
void example_set_myproperty (Example* self, const char* value);
GType example_get_type (void);

G_END_DECLS

#endif

and this .c file:


#include "Example.h"
#include 

struct _ExamplePrivate {
	char* _myproperty;
};
#define EXAMPLE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_EXAMPLE, ExamplePrivate))
enum  {
	EXAMPLE_DUMMY_PROPERTY,
	EXAMPLE_MYPROPERTY
};
static void example_real_myfunction (Example* self);
static gpointer example_parent_class = NULL;
static void example_dispose (GObject * obj);

static void example_real_myfunction (Example* self) {
	g_return_if_fail (IS_EXAMPLE (self));
	fprintf (stdout, "myfunction got called\n");
}

void example_myfunction (Example* self) {
	EXAMPLE_GET_CLASS (self)->myfunction (self);
}

Example* example_new (void) {
	Example * self;
	self = g_object_newv (TYPE_EXAMPLE, 0, NULL);
	return self;
}

char* example_get_myproperty (Example* self) {
	g_return_val_if_fail (IS_EXAMPLE (self), NULL);
	return self->priv->_myproperty;
}

void example_set_myproperty (Example* self, const char* value) {
	char* _tmp2;
	const char* _tmp1;
	g_return_if_fail (IS_EXAMPLE (self));
	_tmp2 = NULL;
	_tmp1 = NULL;
	self->priv->_myproperty = (_tmp2 = (_tmp1 = value, (_tmp1 == NULL ? NULL : g_strdup (_tmp1))), (self->priv->_myproperty = (g_free (self->priv->_myproperty), NULL)), _tmp2);
}

static void example_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) {
	Example * self;
	self = EXAMPLE (object);
	switch (property_id) {
		case EXAMPLE_MYPROPERTY:
		g_value_set_string (value, example_get_myproperty (self));
		break;
		default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
		break;
	}
}

static void example_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) {
	Example * self;
	self = EXAMPLE (object);
	switch (property_id) {
		case EXAMPLE_MYPROPERTY:
		example_set_myproperty (self, g_value_get_string (value));
		break;
		default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
		break;
	}
}

static void example_class_init (ExampleClass * klass) {
	example_parent_class = g_type_class_peek_parent (klass);
	g_type_class_add_private (klass, sizeof (ExamplePrivate));
	G_OBJECT_CLASS (klass)->get_property = example_get_property;
	G_OBJECT_CLASS (klass)->set_property = example_set_property;
	G_OBJECT_CLASS (klass)->dispose = example_dispose;
	EXAMPLE_CLASS (klass)->myfunction = example_real_myfunction;
	g_object_class_install_property (G_OBJECT_CLASS (klass), EXAMPLE_MYPROPERTY, g_param_spec_string ("myproperty", "myproperty", "myproperty", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
}

static void example_init (Example * self) {
	self->priv = EXAMPLE_GET_PRIVATE (self);
}

static void example_dispose (GObject * obj) {
	Example * self;
	self = EXAMPLE (obj);
	(self->myfield = (g_free (self->myfield), NULL));
	(self->priv->_myproperty = (g_free (self->priv->_myproperty), NULL));
	G_OBJECT_CLASS (example_parent_class)->dispose (obj);
}

GType example_get_type (void) {
	static GType example_type_id = 0;
	if (G_UNLIKELY (example_type_id == 0)) {
		static const GTypeInfo g_define_type_info = { sizeof (ExampleClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) example_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (Example), 0, (GInstanceInitFunc) example_init };
		example_type_id = g_type_register_static (G_TYPE_OBJECT, "Example", &g_define_type_info, 0);
	}
	return example_type_id;
}

But you may find it better to write the same boilerplate in Vala:

using GLib;
public class Example: GLib.Object {
public string myfield;
public string myproperty { get; set;}
public virtual void myfunction(){ stdout.printf(“myfunction got called\n”); }
}

So this is it: Vala is yet another C preprocessor (just like C++ was).Vala has a C#-like syntax and it spills out GObject code. That means that you can:

  • export clasess, interfaces from a shared library (so/dll) or (re)use existing classes/interfaces from another library
  • use your existing (optimized) compiler that’s been around for 15 years and it’s tested
  • forget about allocation/deallocation – Vala .ref ()’s and .unref ()’s for you
  • use lambda expressions
  • use existing GObject signals and properties painlessly
  • use a string class that can be passed/received to/from existing API functions unchanged (no ->c_str () needed)
  • fine tune your Vala object methods with [Import]s from manually crafted C sources
  • define a VAPI file for existing libraries so that you can use them directly as objects from Vala (they don’t need to be GObject libraries)
  • oh, and you can make some Generic programming. Just to make the collections work;)

Vala – the best thing since sliced bread? You decide: check out the tutorial:)

Written by vlad

January 20, 2008 at 10:57 pm

Posted in morons

6 Responses

Subscribe to comments with RSS.

  1. GObject is the best thing since sliced bread. Vala is just an easier way of slicing the bread ;)

    KiyuKo

    April 15, 2008 at 10:38 am

  2. One of the most interesting and educational posts I have ever read.

    My only wish is to have some examples and a bit more information and some examples about the 3 bullet points, that explain the abstracting of the system.

    You also may consider adding ActionScript and AIR to the case study.

    Anyway, great post!

    Mihail Najdenov

    August 11, 2008 at 7:39 am

  3. Nice concept but filled with lots of bad/incorrect information.

    NB User

    February 13, 2009 at 7:13 pm

    • Examples or it didn’t happen.

      Seriously, unless you can demonstrate that you actually know what you’re talking about — people will just assume you’re a posturing prick — which you clearly are.

      Trevor Roll

      September 23, 2011 at 7:24 pm

  4. Borland didn’t close the Delphi business – they sold it to Embarcadero, and the product still exists.

    Barry Kelly

    October 7, 2010 at 11:54 am

  5. I’ve come across the same learning when writing bindings for Haskell. In C++ land the world on windows machines is split in the Microsoft world and the gcc world and now interop! In C land you can link vice versa. And Haskell is linking with something similar to a gcc C toolchain. Also, please try to do a C++ binding yourself, it is incredible how rich the C++ API can be. I even have found libraries doing C marcros in the C++ header files in the class line declaration, so that “class” is not showing up! Wow! I also think GObject is great. Unfortunately, when you want to use it you need a language that uses it, like Vala, you don’t want to write GObject code manually. So in my case, I would need: a tool to capture C++ interfaces in GObject wrappers (not existing) and a tool to make GObject programming a snap in Haskell (not existing). mmmh.

    ursfromthebackwoods

    March 16, 2013 at 3:47 pm


Leave a reply to Trevor Roll Cancel reply