Previously: Using glib-utility.h
What follows is a sample of how I presently use glib-utility.h
in my own code.
AnObject - The Root Class
For the purposes of my code, I define a class named AnObject
, which serves as the sole ancestor for all other classes. You will see more of AnObject
as we get into the code required to write a computer programming language.
Definition
Below is the definition of AnObject
, contained within a file named base/classwork.h
. You can see that I comment my code for documentation with Doxygen.
This file defines the base class, from which nearly all others are derived.
*/
#include "utility/glib-utility.h"
// AnObject ********************************************************************
/// The base class, from which nearly all others are derived
/** \struct AnObject
This class adds only a little functionality to what is already provided
by GObject.
\extends GObject
*/
g_class(AnObject, GObject, AN_OBJECT, an_object,
g_instance(
/* <private> */
/** \private Stores the display name of the class */
gpointer DisplayName;
),
/** \virtual Retrieve the name of the object */
def(char *)(*name)(AnObject *Self);
)
/// Type identity of AnObject
#define AN_OBJECT_TYPE (an_object_get_type())
/// Construct a new instance of AnObject or one of its descendants \memberof AnObject
/** Construct a new instance of AnObject or one of its descendants.
This method simply calls `g_object_new()` without any construction
properties. It is designed to be used on AnObject and its descendants, as
these classes use specialized constructors rather than construction
properties (or, indeed, ANY properties provided through GObject).
\memberof AnObject
*/
#define an_object_new(objectType) \
g_object_new(objectType, nil)
/// Free an instance of AnObject or one of its descendants \memberof AnObject
/** Free an instance of AnObject or one of its descendants.
This method simply calls `g_object_unref()` on the specified
\p Instance. It is provided for the sake of symmetry and clearness of code.
\memberof AnObject
*/
#define an_object_free(Instance) \
g_object_unref(Instance)
// Properties ------------------------------------------------------------------
/** \properties{AnObject} @{ */
/// Return the class name of the class or instance \memberof AnObject
def(const char *) an_object_class_name(AnObject *Self);
/// Construct a display name for the class or instance \memberof AnObject
def(char *) an_object_display_name(AnObject *Self);
/// \virtual Retrieve the name of the class or instance \memberof AnObject
/** Retrieve the name of the object.
This function is designed so that the name of any descendant of AnObject
can easily be retrieved. The value actually returned by this function will
vary depending on the class; in the base implementation of AnObject, the
value returned is simply the display name of the class.
\see an_object_display_name() for details.
\return The name of the object. The value of this string will vary
depending upon the class implementing it. The caller should NOT free the
string returned by this method.
\memberof AnObject
*/
#define an_object_name(Self) \
( IS_AN_OBJECT(Self)? AN_OBJECT_GET_CLASS(Self)->name(Self): nil )
/// @}
Implementation
Here is the source file that actually implements AnObject
: base/classwork.c
. You can see that AnObject
provides a method -- an_object_display_name()
-- that is useful for debugging output. It also provides a method to retrieve the class name, which does not require the GObject macros and so makes code easier to read, in my opinion. Finally, you can see how g_class_init()
, g_instance_init()
, and g_instance_dispose()
are used.
This file implements the base class, from which nearly all others are
derived.
*/
#include <glib/gprintf.h>
#include "base/classwork.h"
#include "string/cstrings.h"
// AnObject ********************************************************************
/** Return the name of the class or instance.
This method simply calls `G_OBJECT_CLASS_NAME()` to obtain the name
of the class as it was defined during registration with the GType system.
The calling routine should NOT attempt to free the string returned by this
function.
\memberof AnObject
*/
def(const char *) an_object_class_name(AnObject *Self)
{
return G_OBJECT_CLASS_NAME(AN_OBJECT_GET_CLASS(Self));
}
/** Construct a display name for the class.
The first time this method is called, it calculates the display name of the
class using the class name specified when the given class was registered
with the GType system and the following rules:
- A space is inserted wherever a transition between ASCII case occurs.
- All characters are converted to ASCII lower case.
- If #HideSingularArticles is defined and the class name begins with 'a'
or 'an', that word is removed.
If this method was called on a class named "ABalloonAnimal", it would
construct a display name that reads "a balloon animal".
The resulting display name is stored in a private field, and subsequent
calls to this method will return a reference to that string, since it is
assumed that the name of the class -- and therefore its display name -- will
never change. This method is designed specifically for debugging or
status output, and is used for that purpose by several classes.
\return A string containing the calculated display name of the class, as
described above. This string should NOT be freed by the caller, as it is
a private field belonging to the instance of AnObject and will be freed when
the instance itself is freed.
\memberof AnObject
*/
def(char *) an_object_display_name(AnObject *Self)
{
// Stores the length of the class name
gsize nameLength = 0;
// Refers to a character retrieved from the class name
gchar *character = nil;
// Stores the previous character retrieved from the string
gchar previousCharacter = 'A';
// A loop counter
guint32 i = 0;
g_if_fail(IS_AN_OBJECT(Self),
return nil;
)
if ( a_string_is_empty(Self->DisplayName) == false ) then
return a_string_text(Self->DisplayName);
if (Self->DisplayName == nil) then
Self->DisplayName = an_object_new(A_STRING_TYPE);
// Get the name of the class
character = an_object_class_name(Self);
nameLength = strlen(character);
// Loop through each character in the string
for (i = 0; i < nameLength; i++) {
// Insert spaces into the output where needed
if ( g_ascii_isupper(*character) ) then {
/* When making a transition between upper- and lower-case characters,
a space will be inserted before the upper-case character. This
allows classes with names such as 'AMySQLClass' to be rendered
correctly ('a mysql class' instead of 'a my sqlclass').
*/
if ( (i < nameLength) and
(g_ascii_isupper(*(character + sizeof(char))) == false) ) then
a_string_append_character(Self->DisplayName, ' ');
/* When making a transition between lower- and upper-case characters,
a space will be inserted before the upper-case character.
*/
else if ( g_ascii_isupper(previousCharacter) == false ) then
a_string_append_character(Self->DisplayName, ' ');
}
#ifdef HideSingularArticles
// Remove singular articles from the string
if ( (a_string_equals_constant(Self->DisplayName, " a")) or
(a_string_equals_constant(Self->DisplayName, "a ")) or
(a_string_equals_constant(Self->DisplayName, "n ")) ) then
a_string_empty(Self->DisplayName);
#else
if ( (a_string_equals_constant(Self->DisplayName, " a")) ) then
{
a_string_empty(Self->DisplayName);
a_string_set_text(Self->DisplayName, "a", sizeof(char));
}
#endif
a_string_append_character(Self->DisplayName, g_ascii_tolower(*character));
previousCharacter = *character;
character++;
}
return a_string_text(Self->DisplayName);
}
// Class implementation --------------------------------------------------------
g_class_implement(AnObject, G_TYPE_OBJECT, AN_OBJECT, an_object,
g_class_preamble(),
g_class_typedef(),
g_class_init(
// Establish virtual methods
Self->name = an_object_display_name;
),
g_instance_init(
// Establish private attributes
Self->DisplayName = nil;
),
g_instance_dispose(
// Free the display name
if ( Self->DisplayName != nil ) then {
g_object_unref(Self->DisplayName);
Self->DisplayName = nil;
}
),
g_instance_finalize()
)
Conclusion
This provides a general overview of the way in which I will be utilizing GLib and GObject to construct a parser for a programming language. More documentation will be forthcoming as I post examples and, eventually, the entire source code of the language parser; however, the source for glib-utility.h
is available now, and it is well-documented with comments, if you'd like to peruse those.
Thanks for reading!