C is much more widely supported than C++. There is a subset of C++ that is common to all C++ compilers but it can be emulated in C with minimal loss in efficiency or readability. This subset includes
While the C++ in C approach is not perfect, it does allow much greater portability.
C++ provides language primitives for memory allocation (new
)
and freeing (free
). These functions call the
constructor
and destructor
for an object. The libwww
implements this in C with object_new
and
object_delete
. For example:
PUBLIC HTRequest * HTRequest_new (void) { HTRequest * me; if ((me = (HTRequest *) HT_CALLOC(1, sizeof(HTRequest))) == NULL) HT_OUTOFMEM("HTRequest_new()"); /* Force Reload */ me->reload = HT_ANY_VERSION; . . . me->ContentNegotiation = NO; /* Do this by default */ #ifdef WWW_WIN_ASYNC HTEvent_winHandle(me); #endif return me; }
Many classes are defined in C++ to be private, meaning that all manipulation
of the object is done through member functions (methods). Any attempt at
meddling with the contents by an outside function will result in a compiler
error. Object data is protected in libwww by declaring a structure in a header
file, but not defining it. That is, the object is opaque to the
application - exactly like the ANSI C FILE
object.
typedef struct _MyObject MyObject; int MyObject_saveTheWorld(MyObject * me);
The method (saveTheWorld
) is accessible to everyone including
the header, but the contents of a MyObject
are not. Only the
module containing MyObject
's methods has a definition of the
object,
struct _MyObject { int someInt; };
and therefore, access to the contents (someInt
);
Most C++ objects come with a set of methods (member functions) which are
permitted to affect the object. Any operation
performed on an object (myObject->saveTheWorld
) will be performed
by an method in that object's class.
class MyObject { private: // data int someInt; public: // methods int saveTheWorld(void); } main () { MyObject * myObject = new MyObject; myObject->saveTheWorld(); }
What is actually happening is that the compiler is generating a name from
the class name and the method name, e.g..
_@MyObject@@saveTheWord@132
. This is done explicitly in libwww,
e.g. MyObject_saveTheWorld.
While more verbose, this does, at
least, eliminate the awkward hunt to figure out what instance of a method
the compiler used.
The this pointer is simply an understood first parameter to any method. libwww includes all parameters explicitly.
C++:
myObject->saveTheWorld();
is equivalent to
libwww:
MyObject_saveTheWorld(myObject);
In C++, classes may be derived from other classes.
class MyOtherObject: public MyObject { private: // data int anotherInt; public: // methods int saveTheWales(void); };
As far as memory is concerned, this is identical to
struct _MyOtherObject { MyObject base; int anotherInt; };
but one problem remains. In C++, all the instances of
MyOtherObject
may call
myOtherObject->saveTheWales()
as well as
myOtherObject->saveTheWorld()
. The compiler knows that a
pointer to a MyOtherObject
also points to a
MyObject
, so saveTheWorld
may be called with the
same pointer as saveTheWales
. This is much trickier than the
others as it requires more logic on the part of the compiler.
In libwww, this is mostly handled through explicit pointer casting. As
an example, an HTStream class always
includes an HTStreamClass
as the first element (normally called
isa
). This way, the local definition of the
HTStream may vary from module to module,
depending on what context is needed to process the stream, but the library
can use the standard HTStreamClass
functions to supply it with
data.