/** * \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 © 2014 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 . */ #ifndef __TR_CLASS_CLASS_H__ #define __TR_CLASS_CLASS_H__ #include #include #include #include #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) \ extern struct TR_class c_##name; \ struct c_##name; \ typedef struct c_##name * name; \ extern struct TR_class * const _##name; \ struct c_##name #define TR_INSTANCE_INIT(name) \ struct c_##name##_object { \ void * TR_class; \ struct c_##name data; \ } #define TR_CLASSVARS_DECL(name) struct c_##name##_vars #define TR_CLASSVARS(name, class) ((struct c_##name##_vars *)(class)->vars) #define TR_CLASSVARS_BY_NAME(name) \ ((struct c_##name##_vars *)(TR_CLASS_BY_NAME(name))->vars) #define TR_CLASSVARS_STATIC(name) \ ((struct c_##name##_vars *)(TR_CLASS_BY_NAME_STATIC(name))->vars) #define TR_INHERIT_CLASSVARS(dest, src) \ memcpy(TR_CLASSVARS_BY_NAME(dest), \ TR_CLASSVARS_BY_NAME(src), \ sizeof(TR_CLASSVARS_DECL(src))) /** * 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)] #define TR_CV_EXTENDS(parent) struct c_##parent##_vars _ /** * 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,cvInit,...) \ struct c_##name##_vars _##name##_vars; \ void (* TR_initClassVars##name)(TR_class_ptr) = cvInit; \ static TR_class_ptr _classInit##name##_(void) { \ c_##name.init = NULL; \ c_##name.parent = _##_parent; \ if (TR_initClassVars##name) \ TR_initClassVars##name(_##name); \ return &c_##name; \ }; \ struct TR_class c_##name = { \ TR_CLASS_MAGIC, \ NULL, \ sizeof(struct c_##name), \ _classInit##name##_, \ &_##name##_vars, \ TR_INIT_IFACE_IMPL(__VA_ARGS__) \ }; \ struct TR_class * const _##name = &c_##name; \ struct c_##name##_vars _##name##_vars /** * 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__ } \ } #define TR_INSTANCE_CAST(class, name) ((class)&(_##name.data)) /** * 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*)))) #define TR_CLASS_BY_NAME(class_name) (TR_INIT_CLASS(& c_##class_name)) #define TR_CLASS_BY_NAME_STATIC(class_name) (& c_##class_name) /** * 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(class, object,_iface,method,...) \ do { \ struct i_##_iface * iface; \ TR_class_ptr pc_class = TR_CLASS_BY_NAME(class); \ 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(class, object,_iface,method,ret,...) \ do { \ struct i_##_iface * iface; \ TR_class_ptr pc_class = TR_CLASS_BY_NAME(class); \ 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; void * vars; struct TR_iface_impl impl; }; /** \endcond */ #endif // __TR_CLASS_CLASS_H__ // vim: set ts=4 sw=4: