It has been a while since I posted anything about E or EFL. I have been neglectful and it's time to change that. I want to do a quick intro on something really cool and new in EFL. It's part of a long-term move towards EFL 2.0. And that something is... Eo.
So the "G" world (GLib, GTK+, GNOME etc.) has . We couldn't let them have all the fun, so We've made our own. We call it Eo (EFL Objects). It gives you objects in plain-old-C. Not just plain objects, but a host of related features. What do you get?
And I'm sure I missed a few things. Why are we doing this? Why re-invent wheels? Why not just port to C++? Oh so many questions.
First - we like C. It just needs some extra filling out. Eo does just that. Changing our API to C++ would instantly shut out a lot of C developers and limit our audience, instead we are doing the inverse. We are adding a full and proper C++ API to EFL that is a direct 1:1 mapping to our Eo classes, properties and so on.
We can't feasibly use GObject without bringing in all of the GLib world with it, and GObject doesn't do many of the above things, so we're choosing a home-grown solution. In fact if you look at Eo a bit, you'll see it resembles JS, Python or Lua in terms of objects more than C++. In fact our object pointer indirection alone means we need to do it ourselves. But really - it's more fun to make your own object system in C.
But really it's a by-product of trying to clean up our libraries and their existing ad-hoc object model. This is basically what GObject did for GTK+ and we are now doing the same. We have a lot of interfaces that are duplicated in normal C functions because that becomes a necessity. Like:
There are 100's of such examples across APIs in EFL. We are moving to clean this up and have "one class to rule them all ... and in the darkness bind them":
Nice and simple now. So how do you make a new class? It's not hard. Make an Eo file like this:
Now to use the eolian code generators to fill in your class nuts and bolts. Let's assume you saved the above as tst.eo
This generates your class eo header (tst.eo.h) and the body of the code (tst.eo.c) and the actual class implementation in tst.c. You never have to touch tst.eo.c and tst.eo.h as these are fully generated and have no need for human maintenance. So let's look at tst.c:
This is basically an empty useless class that does nothing, BUT almost all of the boring boilerplate code is done for you. We have no code to USE our test class, so let's make some in main.c:
And we shall compile all of this into a small test binary with:
And voila. A tst" binary. Let's now go and fill in our class implementation and update tst.c to be like:
And now things run and do something when we run ./tst:
Of course the above eolian_gen commands would become part of your Makefiles or build setup. it will even "maintain" the implementation file for you (tst.c in the case above) and add methods as you add methods and properties in your class. It won't remove old ones - you'll have to do that, and if parameters change, you'll need to do that yourself, but a lot of the manual footwork has been removed by automated code generation.
Now what if i want a C++ binding to my new class? Well that's easy:
This will add the base Eo include dir to scan for Eo files and generate bindings for the class in tst.eo and out will pop tst.eo.hh. I won't paste the .hh file here as it is large and complex template-based C++, but you now could write some C++ code to use the same C class such as:
Just compile it like so:
This ensures you build the pure C object file to tst.o then link it in as part of compiling main.cc.
So Eo now makes doing objects in C a breeze. It removes a lot of the footwork in C that would make doing objects painful (and which often drives developers to C++ or some other language). It allows you to build simple C APIs that are usable not just from C in an OO way, but in C++ as if they are native C++ APIs, and in other languages too. We're fleshing out the Lua support at the moment, and other languages will appear as time goes on. This isn't just useful in EFL, but for anyone wanting to make simple OO code in C, be it in embedded devices, phones, desktops, laptops or servers. We happen to be doing it so we can offer far nicer APIs for EFL in future as well as clean up lots of internals, provide far more call safety at runtime, but I am sure this may sole some other problems people have out there.
We invite people to test this out and if you're interested, provide a bindings generator for your favorite language or runtime. This then allows such a language to be a "first class citizen" when using EFL, and any other APIs written using Eo. Such binding generation is then automatic with no human intervention. That is the only sane way to support bindings for languages as manual binding eventually fails.
Now what is object indirection? Well like GObject and older GTK+ before it, Qt and others, our objects before Eo were pointers to memory where the object data is stored. This allows programmers to "do bad things" (by accident) and pass in invalid pointers. We used to solve this by checking the pointer is NULL or not, and if not NULL, de-referencing it and checking for some magic numbers in the memory it points to to ensure the object is there and the right type. No more. Now what look like pointers are actually object IDs. They are not able to be de-referenced. They are broken up into separate fields (different bits in the pointer now have different meanings), and looked up in a table. The table is managed in memory away from normal heap allocations and managed by Eo core. This table in turn holds locations in memory of the objects. This means that an invalid pointer is always caught as it fails table range checks or the table entry is NULL. It even tries to detect false positives with generation count checks. This makes object addressing far more robust and basically almost impossible to get to crash. Eo brings this along with it to EFL, improving stability and correctness even when we don't find the bug and a user unfortunately has to encounter it.
This extra object reference checking leads us to the multi-call capability in Eo. This is where you do something like this:
You can call multiple methods on an object with a single object de-reference/check. This amortizes the cost of such checks over more calls, thus eventually negating them as we now do less checking that we did before on average per call.
Eo also does a check on every method call. If the object does not support that interface/class the call is turned into a no-op and safely returns. This is now guaranteed for every single method call at the C level, so making mistakes and calling the wrong method on the wrong object simply results in some complaints to stderr, and safely marching on.
So much is added and improved with the addition of Eo. Even the existing "legacy" C calls are auto-generated and wrapped on top of Eo calls, so in using EFL today, you are already using Eo, just with a very simple wrapper function on top for compatibility. Every object in EFL is now an Eo object, so you can even mix-and-match old style C calls and newer Eo ones. Eo is a huge step forward for EFL and hopefully is useful to others as well.