Hi Schubido,

about your first question, yes, one does not have to deal with the internals of a class. In many cases its even dangerous because chances are that a classes internal layout changes in the future. Eg. the string class (LFString.c and LFString.h) will be in the future changed to support UTF-8 and some other encodings, depending on the layout of the class will definitely break code in the future. However, just working with the methods won't break as they are maintained by the same person who also maintains the struct around the object.

Your second example is a bit more complex to answer as Lite Foundation gives you three ways to do this particular example;
First of all there is some boilerplate code to create the two classes:
Code:
struct __samevalue
{
    LFRuntimeBase base; // The Lite Foundation object used to store infos about the object
}

struct __incrementvalue
{
    struct __samevalue base; // This time, base is the superclass
}

typedef struct __samevalue* samevalue;
typedef struct __samevalue* incrementvalue;


// In some function that is meant to be invoked by someone outside
LFTypeID __samevalueID = LFRuntimeRegisterClass((const LFRuntimeClass *)&__samevalueClass); // __samevalueClass is assumed to be a filled out LFRuntimeClass object
LFTypeID __incrementvalueID = LFRuntimeRegisterClass((const LFRuntimeClass *)&__incrementvalueClass); // __incrementvalueClass is assumed to be a filled out LFRuntimeClass object



So, now about how to implement the functionality, the first option is to use Lite-C's function overloading and breaking the code on all C compilers.
The second option is to use one function that accepts the polymorphic LFTypeRef and then does its work depending on what object it works with:
Code:
int calculate(LFTypeRef ref, int v)
{
    if(LFIsTypeOf(ref, __samevalueID)
        return v;
        
    if(LFIsTypeOf(ref, __incrementvalueID)
        return v + 1;
        
    return 0;
}



Not the best way either, but, you can still fallback to protocols. A protocol is just a definition of a function that must be implemented by classes that implement the protocol. The calculate function would then become a wrapper to a runtime call that then dispatches the classes protocol information and picks the right function which is then called:
Code:
// In some function that is meant to be invoked by someone outside
LFProtocolBody *pbody = LFRuntimeCreateProtocolBody(sharedProtocolID;
LFRuntimeProtocolBodySetIMP(pbody, __calculateSelector, LFProtocolMethod21(__samevlueCalcImp)); // Say the runtime that its a function with 1 argument and a return value

LFTypeID __samevalueID = LFRuntimeRegisterClass((const LFRuntimeClass *)&__samevalueClass);

// ----------

LFProtocolBody *pbody = LFRuntimeCreateProtocolBody(sharedProtocolID);
LFRuntimeProtocolBodySetIMP(pbody, __calculateSelector, LFProtocolMethod21(__incrementvalueCalcImp));

LFTypeID __incrementvalueID = LFRuntimeRegisterClass((const LFRuntimeClass *)&__incrementvalueClass); // __incrementvalueClass is assumed to be a filled out LFRuntimeClass object

// ----------

int __samevlueCalcImp(LFTypeRef ref, int v)
{
    return v;
}

int __incrementvalueCalcImp(LFTypeRef ref, int v)
{
    return __samevlueCalcImp(ref) + 1;
}

// --------

int calculate(LFTypeRef ref, int v)
{
    return (int)LFRuntimeRInvoke1(ref, __calculateSelector, (LFTypeRef)v);
}




There is a little more code needed to create the actual protocol, but that should give you an idea on how to do it. This is for sure not as elegant as a C++ or Java class but mostly because of limitations in the language which I sadly just can't fix.


Quote:
My first impression is that this is more a container (how I would call it) concept then OOP.
I think it would help to better get the purpose of this library if you could give some easy to understand examples which show for which problems this is an efficient solution.

Well, first of all; You can do real OOP with Lite Foundation!
I agree with the example problem, but I can't think of any easy to understand examples, for example I could write a example that shows how a class can implement faulting to help keeping the memory footprint low, however, this would assume that everyone understands the concept of faulting.
The same goes with eg. a example for class clusters, they are meant to float as fragments in the code, so it becomes more complicated to explain which part is what and how it works with the rest.

I wrote examples for the standard library that also ships with Lite Foundation for people who don't want to use the runtime directly, this is where the purpose of the library changes completely again. And as more classes get added to the default library, there will come more examples on how to use them (beside that the naming convention should make this really clear).

The runtime guide covers this part (how to extend it, how to work with protocols etc), but it contains not so much example code that can be directly executed.


Shitlord by trade and passion. Graphics programmer at Laminar Research.
I write blog posts at feresignum.com