class.h 10 KB
/**
 * \file
 * My own class implementation for C. It combines a data structure with
 * a set of interfaces. Each interface is another structure containing
 * one or more function pointers to concrete implementations of this
 * interface for the defined class.
 *
 * To each interface a set of caller functions exist, that take an instance
 * of an object and then in turn call the implementation for the class of
 * this object. If there is none within the class it looks into its
 * parent class and so forth.
 *
 * This is somewhat similar to late binding in real OOP languages, but
 * by far not so elaborated. This is not a real object oriented language
 * and will surely never ever provide all features these have.
 *
 * That said it has proven very usefull for me to orgnize code and prevent
 * code duplication.
 *
 * \author	Georg Hopp
 *
 * \copyright
 * Copyright © 2012-2013  Georg Hopp
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __TR_CLASS_CLASS_H__
#define __TR_CLASS_CLASS_H__

#include <stdarg.h>
#include <sys/types.h>
#include <string.h>
#include <assert.h>

#include "tr/interface.h"

/** \cond PRIVATE */
#ifndef _ISOC99_SOURCE
#define _ISOC99_SOURCE
#endif
/** \endcond */

/**
 * A magic number that identifies instances of a TR_CLASS class.
 */
#define TR_CLASS_MAGIC    0xFEFE

/**
 * Class declaration macro. This one declares all structures
 * that are needed to create a new class.
 */
#define TR_CLASS(name)                      \
	struct c_##name;                        \
	typedef struct c_##name * name;         \
	extern struct TR_class * const _##name; \
	struct c_##name

/**
 * Make the new class a child of an existing class.
 * This is used within the class declaration and can
 * only be used once. If you do it twice the behaviour
 * is undefined, but most likely the resulting code won't
 * even compile.
 */
#define TR_EXTENDS(parent) \
	const char _[sizeof(struct c_##parent)]

/**
 * Some macros might translate a give NULL to _NULL,
 * this should in turn be a real NULL again.
 */
#define _NULL   NULL

/**
 * This will create a new class which previously has
 * to be defined by TR_CLASS.
 * This makro will create static variables and functions
 * used to manage and controll the new class (and its
 * instances.)
 * Especially it creates and initializes tha class
 * structure for this class. This structure contains all
 * the meta informations of the new class. These are
 * it's members as well as its interface implementations.
 * Each class must at least provide an implementation
 * for the ctor interface else no instances can be
 * created.
 */
#define TR_CREATE_CLASS(name,_parent,...)           \
	static struct TR_class c_##name;                \
	static TR_class_ptr _classInit##name##_(void) { \
		c_##name.parent = _##_parent;               \
		c_##name.init   = NULL;                     \
		return &c_##name;                           \
	};                                              \
	static struct TR_class c_##name = {             \
		TR_CLASS_MAGIC,                             \
		NULL,                                       \
		sizeof(struct c_##name),                    \
		_classInit##name##_,                        \
		TR_INIT_IFACE_IMPL(__VA_ARGS__)             \
	};                                              \
	struct TR_class * const _##name = &c_##name;	\
	struct c_##name##_object {                      \
		void            * TR_class;                 \
		struct c_##name   data;                     \
	}


/**
 * Create a static instance of a class.
 * This is new and currently it is only possible to create
 * instances for class that does not need a constructor.
 *
 * This does not call any constructor with any value...this 
 * has to be fixed. // When this macro is used within a global
 * (static) context this can't be fixed. No function can be 
 * called from there. Instead I add a TR_objectInit function which
 * takes an object name (created on either the stack or the
 * heap) and a variable argument list and calls its constructor
 * with the arguments.
 */
#define TR_INSTANCE(class, name, ...)      \
	struct c_##class##_object _##name;     \
	class name = &(_##name.data);          \
	struct c_##class##_object _##name = {  \
		&c_##class,                        \
		{  ##__VA_ARGS__ }                 \
	}

/**
 * I initialize _ (the class's base or parent class)
 * with the identifier of that class.
 * This identifier is not static and thus can't be used
 * for a static initialization. As a workaround, until I
 * find a better solution I create an initialization function
 * with each class, that will be called once as soon as
 * an interface implementation will be called. In any case
 * this is a call to the constructor of this class.
 * Well, not in any...if I remember correct the static
 * instance does not call anything at all...
 *
 * \cond PRIVATE
 */
#define TR_INIT_CLASS(class) \
		((class)->init? (class)->init() : (class))
/** \endcond */

/**
 * Returns the pointer to the class structure of the given object.
 * The class structure is the structure that contains all the
 * metainformations about our class.
 *
 * \see TR_CREATE_CLASS
 */
#define TR_GET_CLASS(object) \
		(TR_INIT_CLASS(*(TR_class_ptr *)((void*)(object) - sizeof(void*))))

/**
 * Returns this class's implementations of the interface
 * identified by iface.
 */
#define TR_IFACE_GET(class,iface) \
		(TR_interfaceGet(&((class)->impl),(iface)))

/**
 * Check if the given class is a child of any base class.
 * That is it has an TR_EXTENDS expression whithin its declaration.
 *
 * \see TR_EXTENDS
 */
#define TR_HAS_PARENT(class) \
		(NULL != ((class)->parent) && TR_INIT_CLASS((class)->parent))

/**
 * Check if obj really points to a class instance.
 *
 * /see TR_CLASS_MAGIC
 */
#define TR_IS_OBJECT(obj) \
		((TR_GET_CLASS((obj)))->magic == TR_CLASS_MAGIC)

/**
 * Check of obj points to an instance of class.
 */
#define TR_INSTANCE_OF(class,obj) \
		((TR_GET_CLASS((obj))) == _##class)

/**
 * Call the class's implementation for the given method of the
 * given interface _iface.
 * For this the makro searches through all interfaces defined within
 * a class for the correct one...from a performance aspect this might
 * not be ideal but on the other hand...we don't expect more than a
 * hand full of interfaces per class.
 *
 * \cond PRIVATE
 */
#define _TR_CALL(_class,_iface,method)                                     \
	do {                                                                   \
		TR_class_ptr class = _class;                                       \
		iface = (struct i_##_iface *)TR_IFACE_GET(class, &i_##_iface);     \
		while ((NULL == iface ||                                           \
					NULL == iface->method) && TR_HAS_PARENT(class)) {      \
			class = class->parent;                                         \
			iface = (struct i_##_iface *)TR_IFACE_GET(class, &i_##_iface); \
		}                                                                  \
		assert(NULL != iface->method);                                     \
	} while(0)
/** \endcond */

/**
 * Call the class's implementation for the given method of the
 * given interface _iface. This one does not handle a return
 * value of the called implementation and as such is only suitable
 * if there is no return value or you are not interested in it.
 *
 * \todo actually i use gcc feature ## for variadoc... think about
 * a way to make this standard.
 */
#define TR_CALL(object,_iface,method,...)               \
	do {                                                \
		struct i_##_iface * iface;                      \
		_TR_CALL(TR_GET_CLASS(object), _iface, method); \
		iface->method(object, ##__VA_ARGS__);           \
	} while(0)

/**
 * Call the class's implementation for the given method of the
 * given interface _iface. The return value of the function is assigned
 * to ret.
 *
 * \see TR_CALL
 */
#define TR_RETCALL(object,_iface,method,ret,...)        \
	do {                                                \
		struct i_##_iface * iface;                      \
		_TR_CALL(TR_GET_CLASS(object), _iface, method); \
		ret = iface->method(object, ##__VA_ARGS__);     \
	} while(0)

/**
 * Like call but this calls the implementation of the direct parent
 * class of this object.
 *
 * \see TR_CALL
 */
#define TR_PARENTCALL(object,_iface,method,...)         \
	do {                                                \
		struct i_##_iface * iface;                      \
		TR_class_ptr pc_class = TR_GET_CLASS((object)); \
		assert(TR_HAS_PARENT(pc_class));                \
		_TR_CALL(pc_class->parent, _iface, method);     \
		iface->method(object, ##__VA_ARGS__);           \
	} while(0)

/*
 * Like retcall but this calls the implementation of the direct parent
 * class of this object.
 *
 * \see TR_RETCALL
 */
#define TR_PARENTRETCALL(object,_iface,method,ret,...)  \
	do {                                                \
		struct i_##_iface * iface;                      \
		TR_class_ptr pc_class = TR_GET_CLASS((object)); \
		assert(TR_HAS_PARENT(pc_class));                \
		_TR_CALL(pc_class->parent, _iface, method);     \
		ret = iface->method(object, ##__VA_ARGS__);     \
	} while(0)


/**
 * Definition of the metadata structures and symbols used to
 * manage classe.
 *
 * \cond PRIVATE
 */
struct TR_class;
typedef struct TR_class * TR_class_ptr;
typedef TR_class_ptr (* TR_fptr_classInit)(void);
struct TR_class {
	const int         magic;
	TR_class_ptr      parent;
	size_t            object_size;
	TR_fptr_classInit init;
	struct TR_iface_impl impl;
};
/** \endcond */

#endif // __TR_CLASS_CLASS_H__

// vim: set ts=4 sw=4: