LCOM: lightweight component object model

[Abstract] component object model (COM) is the first programming model introduced by Microsoft. It divides the software into two independent parts: interface and implementation. The abstract functions are defined by the interface. An implementation module may implement multiple interfaces to provide actual functions. In this way, in a large-scale software, the coupling between modules can be greatly reduced and the difficulty of module management can be reduced. Some embedded operating systems also introduce com, such as vxWorks. However, in order to take into account the distributed (DCOM) and realize the exchange of binary object models, com is more complex and has a large amount of basic code. This paper attempts to introduce a lightweight component object model, which is completely implemented in c. the core part is very small. It can even run on the computing platform of 51 single chip microcomputer, which is conducive to code sharing under various platforms. This model is called Light Component Object Model (LCOM).

1. General

The so-called interface can be understood as a group of related function pointers, which are used to describe a group of functions. Using a group of functions to express a group of functions is often used in programming practice. For example, Mesa's OpenGL Dispatch API in Linux is a group of more complex function pointers. The related functions of DirectX under Windows, including block device drivers under Linux, are defined group by group function pointers.
Like COM, LCOM uses a 128 bit ID number to identify a group of function pointers, that is, an interface. Once the interface is released, the number of functions, declaration order, function name, parameter table, return value, etc. in the interface will not be modified, so that the application can be effectively compatible with the implementation of different versions. When implementing different versions, the application can be used directly as long as the version upgrade library is provided, and the application does not even need to be reconnected.
In fact, the stability of the interface is very important. At present, a problem in the open source community is compatibility. The versions of a software module are often incompatible, even neither forward nor backward. Each version has changed something mysteriously, which brings great confusion to users. The interface under Windows is much more stable. Since the Win32 programming model started in the 1980s, it can basically be used now. It is a 64 bit system, also known as Win32. This reduces the cost of maintenance and training for many software personnel for the software team providing services under Windows.
LCOM based applications can query whether the object implements the specified interface through a public interface that all objects specify to be implemented, and manage the life cycle of the object (actually implemented by reference counting), generation, destruction and validity of the object (internal consistency) through this interface, And some additional, such as the version number of the object implementation, are implemented by the object unified interface.
For example, for OpenGL system, you can define OpenGL 1.1 interface, OpenGL 1.3 interface, OpenGL 1.5 interface, OpenGL 2.1 interface, OpenGL 3.2 interface, OpenGL 4.0 interface, etc. such a driver actually implements an object. What interface it supports can be queried through a public interface. A special test system can be designed to test whether a driver object supports various versions of API.
Although LCOM is only a set of function pointers, we also need to support direct access to objects from the interface, so as to reduce the burden of applications and do not need to maintain data and function groups respectively. As long as an application maintains an interface, it can access all the functions of the object. Through any interface, it can query other interfaces supported by the object and access all the data of the object.
LCOM also provides a set of common functions and macros to realize object management and free applications from complex object life cycle management.
LCOM provides a unified method in the creation of objects, so that the description of objects can be put in a file such as xml. With a simple parsing software, all the object modules required by the application system can be created to realize the flexible management of software modules. This processing also brings convenience to the storage and transmission of objects. As long as the object class ID, instantiation parameters and objects are stored, the objects can be stored in the memory and transmitted through the communication system, and then re generated remotely with a unified object generation interface.
LCOM defines the interface in a formal way. The readability, unambiguity, completeness and stability of the defined interface are guaranteed to facilitate the communication between teams, so that the design, implementation, testing, verification, debugging and evaluation of software can be completed by different professional teams. Especially for some common interfaces, various professional teams can release their professional service software.
For example, define an I2C interface, so that different hardware environments use different objects to implement the same I2C interface, but its testing, verification and evaluation (performance evaluation?) It is maintained by another professional team, and the upper application based on I2C stably depends on this interface. This reduces the communication cost between teams, makes the trust between teams easier, and may even lead to the efficient cooperation of teams who don't know each other on the network.
If there is a centralized component trading organization, it can provide requirements release, design, implementation, testing and other teams based on network components or interfaces, provide component level transactions, and solve the integration workload of application software.
LCOM is programmed with ANSI C in order to be used in some small systems (such as 51 single chip microcomputer, whose compiler may not support c + +), and its minimum memory occupied by the system can be cut to a very small size to reduce the system overhead as much as possible.

2.GUID

Like COM, LCOM uses GUID to identify the internal interface (IID), object (CLSID) and parameter (PARAMID) of LCOM. Generally speaking, once the target of a GUID identification is published, its meaning will not be modified in the future, which will help the compatibility of applications and protect the advance investment of users. You can not support an interface. As long as you claim to support it, you should meet all the functions and logical relationships of the interface when it is released. If you want to support new features, you can only express them with another interface. DirectX series modules provided under Windows, such as DIrectPlay, have DIrectPlay2, DirectPlay3 and other interfaces.

GUID is defined as follows:

#undef DEFINE_GUID
#ifdef IMPLEMENT_GUID
#if _BYTE_ORDER == _BIG_ENDIAN
#define DEFINE_GUID(name, L, s1, s2, b1, b2, b3, b4, b5, b6, b7, b8) \
const uint32_t name[4] = {L, \
  ((s2) << 0) | (s1 << 16),  \
  ((b1) << 0) | ((b2) << 8) | ((b3) << 16) | ((b4) << 24), \
  ((b5) << 0) | ((b6) << 8) | ((b7) << 16) | ((b8) << 24), \
};
#else
#define DEFINE_GUID(name, L, s1, s2, b1, b2, b3, b4, b5, b6, b7, b8) \
const uint32_t name[4] = {L, \
  ((s1) << 0) | (s2 << 16),  \
  ((b4) << 0) | ((b3) << 8) | ((b2) << 16) | ((b1) << 24), \
  ((b8) << 0) | ((b7) << 8) | ((b6) << 16) | ((b5) << 24), \
};
#endif /*_BYTE_ORDER == _BIG_ENDIAN*/							
#else
#define DEFINE_GUID(name, L, s1, s2, b1, b2, b3, b4, b5, b6, b7, b8) \
extern const uint32_t name[4];
#endif

You can see that the GUID consists of 128 bits and is placed in four 32-bit unsigned integers.
There are many tools for generating guids. It is recommended to use guidgen, a Microsoft GUID generation tool, under windows Exe, which can generate a 128 bit number according to the running computer hardware status and running time. It is said that it can ensure that the ID numbers generated in any two times (including generated by different computers at the same time) are different. In this way, you can safely generate a unique ID number to name your interface or object.
After the tool is generated, you can select the following format:

// {290C21DB-06A6-4752-A90C-75DF92A5CF48}
DEFINE_GUID(<<name>>, 
0x290c21db, 0x6a6, 0x4752, 0xa9, 0xc, 0x75, 0xdf, 0x92, 0xa5, 0xcf, 0x48);

Replace the name with the name you want to define. For example, it is generally defined in the c header file as follows:

DEFINE_GUID(IID_XXXX, 0x290c21db, 0x6a6, 0x4752, 0xa9, 0xc, 0x75, 0xdf, 0x92, 0xa5, 0xcf, 0x48);

DEFINE_ The macro definition of guid actually consists of two implementations. One is the implementation that actually stores the 128 value of guid in a global variable and defines implement externally_ GUID is implemented. One is only declaring an external variable for reference by other modules. You can directly include the c header file in the reference module, and define implement in the only file related to the object implementation or interface_ Guid, then contain the header file that declares the guid, and then cancel implement immediately_ Guid definition, which ensures that each guid can be implemented by a unique module.
In the first mock exam, we use a pointer to GUID, so we define a pointer type. In fact, in many cases, we store or transmit pointer to GUID. When the same module is compared with GUID, whether the GUID is equal, we can directly compare the pointer to the same value, and of course, the pointer to the external input. You cannot assume a direct pointer reference, so define a macro to determine whether two guids are equal:

typedef const uint32_t* IIDTYPE;
#define isGUIDEqual(n1, n2) (((n1)[0] == (n2)[0]) \
                          && ((n1)[1] == (n2)[1]) \
                          && ((n1)[2] == (n2)[2]) \
                          && ((n1)[3] == (n2)[3]))

3. Basic public interface

3.1 definition of public interface

LCOM defines the public interfaces that all objects must implement as follows:

typedef struct sIObject {
	int __thisoffset;
/*
	Function: whether the object supports the specified interface
	Parameters:
	    object -- Object data pointer
	    iid -- Interface number
	    pInterface -- Store the returned interface pointer
	Return value:
	    EIID_OK: success
	    EIID_INVALIDOBJECT: object Is an invalid object
	    EIID_INVALIDPARAM: Parameter error
	    EIID_UNSUPPORTEDINTERFACE : Object does not support this interface
*/	
	int (*QueryInterface)(HOBJECT object, IIDTYPE iid, const void **pInterface);
/*
	Function: increase the reference count of objects
	Parameters:
	    object -- Object data pointer
	Return value:
	    >=0: Increased reference count 
	    EIID_INVALIDOBJECT: object Is an invalid object
*/		
	int (*AddRef)(HOBJECT object); 
/*
	Function: reduce the reference count of the object, and delete the object when the reference count is 0
	Parameters:
	    object -- Object data pointer
	Return value:
	    >= 0: Reduced count of references
	    EIID_INVALIDOBJECT: object Is an invalid object
*/		
	int (*Release)(HOBJECT object);
/*
	Function: judge whether the object is a valid object
	Parameters:
	    object -- Object data pointer
	Return value:
	    0 -- The object is invalid
	    1 -- Object is valid
*/	
	int (*IsValid)(HOBJECT object);
}IObject;

It also stipulates that the 0 offset of each object must be a pointer to IObject, so the pointer to the object is actually a pointer to the interface. In other words, the so-called object in LCOM is actually a pointer to the interface pointer, which is stored in the implementation data structure of the object. definition

typedef void * HOBJECT;

Represents an interface. The pointer points to the pointer of an interface. Since the place where the object offset value is 0 stores a pointer to IObject, the interface pointer with offset value of 0 is also the pointer of the object. The offset value from the interface object to the interface pointer is recorded in each interface implementation, so the object pointer can be obtained from the interface.

#define objectThis(_obj) (((uint8_t *)(_obj))-((*(IObject **)(_obj))->__thisoffset))

In order to realize the life cycle management of the object, there should also be a reference count variable, and the object should also save a GUID of the object type, so that the application can confirm whether the object is the object type it wants.
The first few members of each interface should be exactly the same as IObject, so that each interface can be used as IObject to ensure that the public interface function of the object can be called through each interface.
When an object implements an interface, the pointer of the interface must be stored in the object. Note that the first member of the interface is__ this_offset, this member records the offset value from the pointer of the interface to the beginning of the object. In this way, if we have the pointer of this pointer member in the object, we can get the first address of the object according to the offset value, so as to access the object itself.

3.2 implementation example of public interface

In this way, if you want to implement an object xxxx, you only need to define the CLSID of the object in the header file. The definition structure in the implementation c file is as follows:

typedef struct _sXXXX {
	const IObject * __IObject_ptr;
	int __object_refcount;
	IIDTYPE __object_clsid;
}sXXXX;

static int xxxxQueryInterface(HOBJECT object, IIDTYPE iid, const void **pInterface);
static int xxxxAddRef(HOBJECT object);
static int xxxxRelease(HOBJECT object);
static int xxxxIsValid(HOBJECT object);
static void xxxxDestroy(HOBJECT object); 
static int xxxxCreate(const PARAMITEM * pParams, int paramcount, HOBJECT* pObject); 
static int xxxxValid(HOBJECT object);

static const IObject xxxx_object_interface = { 
		0, /* IObject The pointer to must be at the 0 offset of the object */
		xxxxQueryInterface, 
		xxxxAddRef, 
		xxxxRelease, 
		xxxxIsValid 
	}; 
static const char * xxxxModuleInfo()
{
	return "1.0.0 20210423.2118 xxxx module by RAOXIANHONG";
}
static int xxxxRegister() 
{
	return objectCreateRegister(CLSID_XXXX, xxxxCreate, xxxxModuleInfo);
}

FUNCPTR A_u_t_o_registor_xxxx = (FUNCPTR)xxxxRegister;

static int xxxxQueryInterface(HOBJECT object, IIDTYPE iid, const void **pInterface)
{
	if (!objectIsClass(object, CLSID_XXXX)) 
		return EIID_INVALIDOBJECT; 
  if (!objectIsValid(object))
		return EIID_INVALIDOBJECT;
	if (pInterface == 0)
		return EIID_INVALIDPARAM;
	*pInterface = 0;
	if (isGUIDEqual(iid ,IID_OBJECT)) { 
		*pInterface = objectThis(object); 
		objectAddRef(object); 
		return EIID_OK; 
	}
	return EIID_UNSUPPORTEDINTERFACE;
}
static int xxxxAddRef(HOBJECT object) 
{ 
  sXXXX * pD; 
  if (!objectIsValid(object)) 
    return EIID_INVALIDOBJECT; 
  pD = (sXXXX *)objectThis(object); 
  pD->__object_refcount++; 
  return pD->__object_refcount; 
} 

static int xxxRelease(HOBJECT object) 
{ 
  sXXXX * pD;
  int ret;
  if (!objectIsValid(object))
    return EIID_INVALIDOBJECT; 
  pD = (sXXXX *)objectThis(object);
  pD->__object_refcount--;
  ret = pD->__object_refcount; 
  if (pD->__object_refcount <= 0) {
    pD->__object_refcount = 1; 
    /*In order to ensure that recursive calls do not occur in the Destroy process, the reference count is set to 1 here*/
	xxxxDestroy(object);
  }
  return ret;
}

static int xxxxIsValid(HOBJECT object)
{ 
  sXXXX * pD; 
  if (object == 0) 
    return 0; 
  pD = (sXXXX *)objectThis(object); 
  if (pD->__object_clsid != CLSID_XXXX)
    return 0;
  if (pD->__object_refcount < 1)
    return 0; 
  return xxxxValid(object);
}

Some macros and functions will be explained in detail in later chapters.

4. Object management

LCOM provides management functions for object classes, including class registration, class instance generation, class module information output, etc. It can provide a unified class object instance generation function. As long as the application holds the CLSID of the class and the corresponding instantiation parameters, it can generate object instances through objectCreate or objectCreateEx functions.
The class module information output function can print out the module information of the class registered in the system, which helps to easily obtain the version information of each module in the process of system debugging and maintenance. Version mismatch is also a nightmare for software maintenance engineers. In fact, the version information is often the first thing that foreground maintenance engineers and background development engineers need to confirm, Ensure that the two teams work on one version.
In this way, when each object is implemented, in addition to the public interface of LCOM, several functions required for object management are also implemented:

static void xxxxDestroy(HOBJECT object); 
static int xxxxCreate(const PARAMITEM * pParams, int paramcount, HOBJECT* pObject); 
static const char * xxxxModuleInfo();
static int xxxxValid(HOBJECT object);

A pointer and a function:

FUNCPTR A_u_t_o_registor_xxxx = (FUNCPTR)xxxxRegister;

This pointer supports that under some operating systems that can access the symbol table (such as vxWorks), all a can be run directly while calling in the module_ u_ t_ o_ registor_ Start function to complete the registration of all classes contained in the module.
Xxdestroy implements the deletion of objects. Instead of directly calling this function to delete objects in the application, it increases the reference count through AddRef (QueryInterface also increases the reference count) and reduces the reference count through Release. When the reference count is 0, call this function to Release the resources occupied by the object.
Xxxvalid returns whether a legal object is formed inside the object, which can generally be judged by whether some data are consistent. Before calling this function, it has been ensured that the object is indeed the class implemented in the module, and the pointer is legal.
Xxxcreate and xxxmoduleinfo and object class ID CLSID_XXXX is transferred to the objectCreateRegister function, which records the object generation function of each class and the version information function of the implementation module in a table.
The object management module provides the following functions:

/*The following is the consistency implementation support of object generation*/
typedef struct _sParamItem {
	IIDTYPE				name; /*Parameter number, the meaning of each number shall be specified by the object implementation, and the details of the object generated parameter table shall be described in the object implementation document*/
	uint32_t     		i32value; /*32 Bit general parameters*/
	double				fvalue; /*Floating point parameter*/
	void *				pvalue; 
}PARAMITEM;

/*
	Object generation function
	Function: the object implementation module generates a new object
	Parameter: pparames -- parameter table generated by the object. When paramcount > 0 is yes, pparames must not be NULL
	      paramcount -- When the number of parameters in the table cannot be less than zero, the number of parameters in the table must not be less than zero
	      pObject -- The pointer that holds the returned object handle cannot be NULL
  Return value:
     EIID_OK -- success
     EIID_INVALIDPARAM -- Invalid parameter
     EIID_MEMORYNOTENOUGH -- insufficient memory
     EIID_GENERALERROR -- Other errors
*/
typedef int (*FObjectCreate)(const PARAMITEM * pParams, int paramcount, HOBJECT * pObject);

/*
	Object information function
	Function: return the version information of the object implementation module
	Parameter: None
  Return value:
     Version information of object implementation module
*/
typedef const char * (*FModuleVersionInfo)();


/*
	Object class management module initialization
	Function: initialize internal resources. This function is called only once during initialization
	Parameters:
	Return value:
	   EIID_OK -- success
*/

int objectRegisterInit();

/*
	Object class registration
	Function: register an object class into the object generation support module
	Object number: CLD
	      objectCreator -- Object generation function
	      moduleinfo -- Object version information function
	Return value:
	   EIID_OK -- success
	   EIID_INVALIDPARAM -- invalid parameter
	   EIID_MEMORYNOTENOUGH -- insufficient memory	  
	   EIID_GENERALERROR -- The object class is already registered
*/
int objectCreateRegister(IIDTYPE clsid, FObjectCreate objectCreator, FModuleVersionInfo moduleinfo);

/*
	Function: print the version information of all registered object implementation modules
	Parameter: data -- the first parameter passed to funcoutput function,
	      funcoutput -- String output function. If it is 0, call printf to output to standard output.
	Return value:
*/
int objectPrintInfo(PTR data, OFUNCPTR funcoutput);

/*
	Object class logoff
	Function: unregister all registered classes. This routine should be called at the end of the application to release resources
	Parameters:
	Return value:
	   EIID_OK -- success
*/
int objectCreateUnRegisterAll(void);

/*
	Object generation
	Function: query the object generation function of the registered object class to generate objects
	Parameter: clsid -- object class number
	      pParams -- Object generation parameter table. If this class has been registered, it will be transferred to the registered object generation function
	      paramcount -- Number of object generated parameters
	      pObject -- Pointer that holds the returned object handle
  Return value:
     EIID_OK -- success
     EIID_INVALIDPARAM -- invalid parameter
     EIID_MEMORYNOTENOUGH -- insufficient memory
     EIID_GENERALERROR -- Object generation error
     EIID_INVALIDCLSID -- This class is not registered
*/
int objectCreate(IIDTYPE clsid, const PARAMITEM * pParams, int paramcount, HOBJECT * pObject);
int objectCreateEx(IIDTYPE clsid, const PARAMITEM * pParams, int paramcount, IIDTYPE iid, const void **pInterface);

/*
  Query object generation function
  Function: returns the object generation function of the specified class number
  Parameter: clsid -- object class number
        pOBjectCreator -- Store the pointer of the object generation function
  Return value:
      EIID_OK -- success
      EIID_INVALIDPARAM -- invalid parameter
      EIID_INVALIDCLSID -- This class is not registered
*/
int objectQueryCreator(IIDTYPE clsid, FObjectCreate * pObjectCreator);

In this way, the application can use a unified interface to generate objects, and even store the CLSID and parameters of the generated objects in a software composition description file, and realize the generation of each object in the whole software through a simple parsing function.

5. Interface definition and Implementation

LCOM ultimately needs to realize the user's functions. First, it must be able to define the user interface, and then implement the specified interface in the object.

5.1 interface definition

Here is an example to illustrate how to define an interface. OpenGL applications are often used in the past. In order to run different applications in an application platform, we abstract an OpenGL application into an interface as follows:

typedef struct sIGLApp {
  int __thisoffset; 
  int (*QueryInterface)(HOBJECT object, IIDTYPE iid, const void **pInterface); 
  int (*AddRef)(HOBJECT object); 
  int (*Release)(HOBJECT object); 
  int (*IsValid)(HOBJECT object); 
  int (*Render)(HOBJECT object, int x, int y, int width, int height);
  int (*SwitchIn)(HOBJECT object);
  int (*SwitchOut)(HOBJECT object);
  int (*GetOption)(HOBJECT object, int option);
  int (*MouseLeftDoubleClick)(HOBJECT object, int x, int y, int ctrlkey);
  int (*MouseLeftDown)(HOBJECT object, int x, int y, int ctrlkey);5  int (*MouseLeftUp)(HOBJECT object, int x, int y, int ctrlkey);
  int (*MouseRightDoubleClick)(HOBJECT object, int x, int y, int ctrlkey);
  int (*MouseRightDown)(HOBJECT object, int x, int y, int ctrlkey);
  int (*MouseRightUp)(HOBJECT object, int x, int y, int ctrlkey);
  int (*MouseMove)(HOBJECT object, int x, int y, int ctrlkey);
  int (*KeyDown)(HOBJECT object, int key, int ctrlkey);
  int (*KeyUp)(HOBJECT object, int key, int ctrlkey);
}IGLApp;

This interface is divided into several parts: the first is the public interface function and offset value variable specified by LCOM. After that is the rendering function Render, and then the processing functions of mouse messages and keyboard messages. In addition, there is a function to return the of OpenGL application, such as whether it needs external timing call (to realize animation effect), etc. This interface is only exemplary, so it is defined casually. But it is still abstract enough to run on various platforms.

5.2 interface realization

For example, we want to implement the application of drawing MandelBrot set mentioned in the previous article( Drawing MandelBrot set with OpenGL 4.0 ), you can define an object like this (mandelbrot_app.h):

#ifndef __GLAPP_MANDELBROT_H
#define __GLAPP_MANDELBROT_H

#ifdef __cplusplus
extern "C" {
#endif

#include "guid.h"

DEFINE_GUID(CLSID_MANDELBROT, 0xdc03ff75, 0x27f1, 0x4330, 0x82, 0xab, 0x3, 0x5c, 0x5, 0x66, 0x81, 0xa2);

int glappMandelbrotCreate(IGLApp ***pApp);

#ifdef __cplusplus
}
#endif
	
#endif

It also declares a global function glappMandelbrotCreate, which can be used to generate MandelBrot application objects, where the objectCreateObejctEx function is called to generate a class CLSID_. Mandelbrot object and returns the interface of IGLApp it implements. In fact, the application does not care about the object, only about the IGLApp interface.

The implementation of the object is as follows. The implementation code has no auxiliary code, so it is very complex and lengthy. It is hard to read. If you don't want to try, you can skip it. However, some implementation considerations are given in the code comments, and researchers can look back:

#include "stdio.h"
#include "object.h"
#include "glapp.h"
#include "gl/glew.h"

#include "loadprogram.h"
/*
Implement is defined here_ Guid, which is the glapp_ mandelbrot. Defined in H
CLSID_MANDELBROT Implemented in this document
*/
#define IMPLEMENT_GUID
#include "glapp_mandelbrot.h"
#undef IMPLEMENT_GUID

typedef struct _sMandelBrotApp {
    /*Object Interface pointer*/
	const IObject * __IObject_ptr;
	/*Reference count*/
	int __object_refcount;
	/*Class ID pointer*/
	IIDTYPE __object_clsid;
	/*IGLApp Interface pointer*/
	const IGLApp* __IGLApp_ptr;

	/*Class implementation variable*/
	GLuint m_program;
	double psize;
	double pfromx;
	double pfromy;
	int	psizeLoc;
	int pfromLoc;
	int vertexLoc;
	int inited;
	int x, y, w, h;
}sMandelBrotApp;

/*The functions of the public interface and the functions required for object management are declared below, which is necessary for each object implementation*/
static int mandelbrotappQueryInterface(HOBJECT object, IIDTYPE iid, const void **pInterface); 
static int mandelbrotappAddRef(HOBJECT object); 
static int mandelbrotappRelease(HOBJECT object); 
static int mandelbrotappIsValid(HOBJECT object); 
static void mandelbrotappDestroy(HOBJECT object); 
static int mandelbrotappCreate(const PARAMITEM * pParams, int paramcount, HOBJECT* pObject); 
static int mandelbrotappValid(HOBJECT object);
/*Define an IObject object that implements the IObject interface,
MandelBrotApp The first member of the object must be a pointer to the data*/
static const IObject mandelbrotapp_object_interface = { 		    
  0, /*IObject The pointer to must be the first member of the object implementation, so the offset is 0*/ 
  mandelbrotappQueryInterface, 
  mandelbrotappAddRef, 
  mandelbrotappRelease, 
  mandelbrotappIsValid 
}; 

static const char * mandelbrotappModuleInfo();
/*To implement the object class registration function is to register the class ID of the object, 
The object generation function and the information function of the object implementation module are submitted to the object management module*/
static int mandelbrotappRegister() {
  return objectCreateRegister(CLSID_MANDELBROT, mandelbrotappCreate, mandelbrotappModuleInfo); 
}
OFUNCPTR A_u_t_o_registor_mandelbrotapp = (OFUNCPTR)mandelbrotappRegister;
/*The functions of the IGLApp interface are declared below*/	
static int mandelbrotapp_glapp_Render(HOBJECT object, int x, int y, int width, int height);   
static int mandelbrotapp_glapp_SwitchIn(HOBJECT object); 
static int mandelbrotapp_glapp_SwitchOut(HOBJECT object); 
static int mandelbrotapp_glapp_GetOption(HOBJECT object, int option); 
static int mandelbrotapp_glapp_MouseLeftDoubleClick(HOBJECT object, int x, int y, int ctrlkey); 
static int mandelbrotapp_glapp_MouseLeftDown(HOBJECT object, int x, int y, int ctrlkey); 
static int mandelbrotapp_glapp_MouseLeftUp(HOBJECT object, int x, int y, int ctrlkey); 
static int mandelbrotapp_glapp_MouseRightDoubleClick(HOBJECT object, int x, int y, int ctrlkey); 
static int mandelbrotapp_glapp_MouseRightDown(HOBJECT object, int x, int y, int ctrlkey); 
static int mandelbrotapp_glapp_MouseRightUp(HOBJECT object, int x, int y, int ctrlkey); 
static int mandelbrotapp_glapp_MouseMove(HOBJECT object, int x, int y, int ctrlkey); 
static int mandelbrotapp_glapp_KeyDown(HOBJECT object, int key, int ctrlkey); 
static int mandelbrotapp_glapp_KeyUp(HOBJECT object, int key, int ctrlkey); 
/*The following defines a pointer to IGLApp. In the object, a pointer member should point to this structure*/
static const IGLApp mandelbrotapp_glapp_interface = { 
  (int)&(((const sMandelBrotApp*)0)->__IGLApp_ptr), 
  /*The offset of this interface is the offset of the pointer to this structure in the object*/
  mandelbrotappQueryInterface, 
  mandelbrotappAddRef, 
  mandelbrotappRelease, 
  mandelbrotappIsValid,
  mandelbrotapp_glapp_Render, 
  mandelbrotapp_glapp_SwitchIn, 
  mandelbrotapp_glapp_SwitchOut, 
  mandelbrotapp_glapp_GetOption, 
  mandelbrotapp_glapp_MouseLeftDoubleClick, 
  mandelbrotapp_glapp_MouseLeftDown, 
  mandelbrotapp_glapp_MouseLeftUp, 
  mandelbrotapp_glapp_MouseRightDoubleClick, 
  mandelbrotapp_glapp_MouseRightDown, 
  mandelbrotapp_glapp_MouseRightUp, 
  mandelbrotapp_glapp_MouseMove, 
  mandelbrotapp_glapp_KeyDown, 
  mandelbrotapp_glapp_KeyUp, 
};

static int mandelbrotappAddRef(HOBJECT object) 
{ 
  sMandelBrotApp* pD; 
  if (!objectIsValid(object)) 
    return EIID_INVALIDOBJECT; 
  pD = (sMandelBrotApp*)objectThis(object); 
  pD->__object_refcount++; 
  return pD->__object_refcount; 
} 

static int mandelbrotappRelease(HOBJECT object) 
{ 
  sMandelBrotApp* pD; 
  int ret; 
  if (!objectIsValid(object)) 
    return EIID_INVALIDOBJECT; 
  pD = (sMandelBrotApp*)objectThis(object); 
  pD->__object_refcount--; 
  ret = pD->__object_refcount; 
  if (pD->__object_refcount <= 0) { 
    pD->__object_refcount = 1; /*In order to ensure that recursive calls do not occur in the Destroy process, the reference count is set to 1 here*/ 
	mandelbrotappDestroy(object); 
  } 
  return ret; 
} 

static int mandelbrotappIsValid(HOBJECT object)
{
  sMandelBrotApp* pD; 
  if (object == 0) 
    return 0; 
  pD = (sMandelBrotApp*)objectThis(object); 
  if (pD->__object_clsid != CLSID_MANDELBROT) 
    return 0; 
  if (pD->__object_refcount < 1) 
    return 0; 
  return mandelbrotappValid(object); 
}
/*QueryInterface The object implements two interfaces IObject and IGLApp,
It can be seen that the so-called interface actually returned is actually a pointer in the object to the interface definition,
Passing hobjet in the system is actually this pointer. The first data of the address pointed to by this pointer is
 The offset value of the pointer in the object, so subtracting the offset value from the pointer is the address of the object:
#define objectThis(_obj) (((Ouint8_t *)(_obj))-((*(IObject **)(_obj))->__thisoffset))
*/
static int mandelbrotappQueryInterface(HOBJECT object, IIDTYPE iid, const void **pInterface)
{
  /*This module only provides CLSID_MANDELBROT class object support*/
  if (!objectIsClass(object, CLSID_MANDELBROT))
    return EIID_INVALIDOBJECT;
  /*Make sure the object is valid*/
  if (!objectIsValid(object)) 
	return EIID_INVALIDOBJECT; 
  if (pInterface == 0) 
	return EIID_INVALIDPARAM; 
  *pInterface = 0; 
  if (isGUIDEqual(iid ,IID_OBJECT)) {
    *pInterface = objectThis(object); 
	objectAddRef(object); 
	return EIID_OK; 
  } else if (isGUIDEqual(iid, IID_GLAPP)) {
    /*Query IID_ In the glapp interface, the returned object__ IGLApp_ Address of PTR*/ 
	*pInterface = &((sMandelBrotApp*)(objectThis(object)))->__IGLApp_ptr;
	/*Querying an interface is equal to referencing an object once, so increase the reference count,
	When the queried interface is not used, Release should be called to avoid memory leakage*/ 
	objectAddRef(object); 
	return EIID_OK; 
  } 
  return EIID_UNSUPPORTEDINTERFACE;
}

static int mandelbrotappCreate(const PARAMITEM * pParams, int paramcount, HOBJECT * pObject)
{
	sMandelBrotApp * pobj;
	pobj = (sMandelBrotApp *)malloc(sizeof(sMandelBrotApp));
	if (pobj == NULL)
		return -1;
	pobj->m_program = 0;
	pobj->psize = 4.0;
	pobj->pfromx = -0.75;
	pobj->pfromy = 0;
	pobj->inited = 0;
	*pObject = 0;
    pobj->__IGLApp_ptr = &mandelbrotapp_glapp_interface;  	
	/*Returns the generated object*/
	pobj->__IObject_ptr = &mandelbrotapp_object_interface; 
	pobj->__object_refcount = 1; 
	pobj->__object_clsid = CLSID_MANDELBROT;	
	*pObject= (HOBJECT)&pobj->__IObject_ptr; 
	return EIID_OK;
}

static const char *mandelbrotappModuleInfo()
{
  /*Suggested module information format: version number date time module description*/
  return "1.0.0-20210408.1108 MandelBrot App";
}

static void mandelbrotappDestroy(HOBJECT object)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);
	free(pApp);
}

/*
    Function: judge whether the object is a valid object
    Parameters:
        object -- Object data pointer
    Return value:
        0 -- The object is invalid
        1 -- Object is valid
*/
static int mandelbrotappValid(HOBJECT object)
{
    return 1;
}

static ShaderItem shaders[] = { 
	{GL_VERTEX_SHADER, "d:/openglshader/mandelbrot.vert", 0},
	{GL_FRAGMENT_SHADER, "d:/openglshader/mandelbrot.frag", 0},
	{0, 0},
};


static int mandelbrotapp_glapp_Init(HOBJECT object)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);
	pApp->m_program = glLoadProgram(shaders);
	pApp->psizeLoc = glGetUniformLocation(pApp->m_program, "psize");
	pApp->pfromLoc = glGetUniformLocation(pApp->m_program, "pfrom");
	pApp->vertexLoc = glGetAttribLocation(pApp->m_program, "vertex");
	pApp->inited = 1;
	return 0;
}

static int mandelbrotapp_glapp_DeInit(HOBJECT object)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);
	
	return 0;
}

static float squad[] = {
		-1.0f, -1.0f,
		-1.0f,  1.0f,
		1.0f,  1.0f,
		-1.0f,  -1.0f,
		1.0f,  1.0f,
		1.0f, -1.0f
};

extern int OutputString(int append, const char * info);

static int mandelbrotapp_glapp_Render(HOBJECT object, int x, int y, int width, int height)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);
	
	if (pApp->inited == 0) {
		mandelbrotapp_glapp_Init(object);
	}

	pApp->x = x; 	
	pApp->y = y;
	pApp->w = width;
	pApp->h = height;


    glViewport(0, 0, (GLint) width, (GLint) height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glDisable(GL_DEPTH_TEST);

	glUseProgram(pApp->m_program);

	glUniform1d(pApp->psizeLoc, pApp->psize);
	glUniform2d(pApp->pfromLoc, pApp->pfromx, pApp->pfromy);
	

	glEnableVertexAttribArray(pApp->vertexLoc);
	glVertexAttribPointer(pApp->vertexLoc, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)&squad[0]);
	
	glDrawArrays(GL_TRIANGLES, 0, 6);

	glDisableVertexAttribArray(pApp->vertexLoc);
	{
		char buf[90];
		sprintf(buf, "%20.17lle %20.17lle %20.17lle", pApp->pfromx, pApp->pfromy, pApp->psize);
		OutputString(0, buf);
	}
	return 0;
}

static int mandelbrotapp_glapp_SwitchIn(HOBJECT object)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);

	return 0;
}

static int mandelbrotapp_glapp_SwitchOut(HOBJECT object)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);

	return 0;
}

static int mandelbrotapp_glapp_GetOption(HOBJECT object, int option)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);

	return 0;
}


static int mandelbrotapp_glapp_MouseLeftDoubleClick(HOBJECT object, int x, int y, int ctrlkey)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);

	return 0;
}

static int mandelbrotapp_glapp_MouseLeftDown(HOBJECT object, int x, int y, int ctrlkey)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);
	if (ctrlkey & GLAPP_SPECIAL_KEY_CONTROL) {
		pApp->psize *= 2;
	} else if (ctrlkey & GLAPP_SPECIAL_KEY_SHIFT) {
		pApp->psize /= 2;
	} else {
		int cx, cy;
		double diffx, diffy;
		cx = pApp->x + pApp->w / 2;
		cy = pApp->y + pApp->h / 2;
		diffx = cx - x;
		diffy = cy - y;
		diffx /= pApp->w;
		diffy /= pApp->h;
		diffx *= pApp->psize;
		diffy *= -pApp->psize;
		pApp->pfromx -= diffx;
		pApp->pfromy -= diffy;
	}
	return 1;
}

static int mandelbrotapp_glapp_MouseLeftUp(HOBJECT object, int x, int y, int ctrlkey)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);

	return 0;
}

static int mandelbrotapp_glapp_MouseRightDoubleClick(HOBJECT object, int x, int y, int ctrlkey)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);

	return 0;
}

static int mandelbrotapp_glapp_MouseRightDown(HOBJECT object, int x, int y, int ctrlkey)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);

	return 0;
}

static int mandelbrotapp_glapp_MouseRightUp(HOBJECT object, int x, int y, int ctrlkey)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);

	return 0;
}

static int mandelbrotapp_glapp_MouseMove(HOBJECT object, int x, int y, int ctrlkey)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);

	return 0;
}

static int mandelbrotapp_glapp_KeyDown(HOBJECT object, int key, int ctrlkey)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);

	return 0;
}

static int mandelbrotapp_glapp_KeyUp(HOBJECT object, int key, int ctrlkey)
{
	sMandelBrotApp * pApp;
	pApp = (sMandelBrotApp *)objectThis(object);

	return 0;
}

int glappMandelbrotCreate(IGLApp ***pApp)
{
	int ret;
	A_u_t_o_registor_mandelbrotapp();
	ret = objectCreateEx(CLSID_MANDELBROT, NULL, 0, IID_GLAPP, (const void **)pApp);
	return ret;
}

5.3 realized memory overhead

To implement an object, there must be a pointer to IObject, a reference count and a pointer to class ID in the object. Then, for each additional interface implemented, a pointer to the definition of the interface must be added. The total cost is acceptable.

6 using macros to assist in implementation

If you have the patience to read the code in 5.2, what is your feeling? It's too redundant. I feel heavy and verbose. In addition, it's too fine. Various names are intertwined. If you accidentally write an underscore or something wrong, the whole program will be in disorder. With so many lines of code, more than half of them are used to deal with LCOM, which is not practical. A practical model should let programmers focus on the algorithms that need to be implemented, rather than spend most of their energy dealing with the red tape that meets the management requirements.
Therefore, LCOM defines a series of macros to assist the implementation. These macros include:

/*This macro is used to declare the basic members of each object, including pointers to iobjects
 The reference count and CLSID can be placed directly in front of the structure of the object implementation
*/
#define OBJECT_HEADER   \
	const IObject * __IObject_ptr; \
	int __object_refcount; \
	IIDTYPE __object_clsid; \
/*
This macro declaration specifies a pointer to the interface, which can be standardized and consistent with other parts.
*/
#define INTERFACE_DECLARE(__interface) \
 const __interface * __##__interface##_ptr;

/*
This macro defines the Object interface variables and functions that must be implemented by each interface, which is placed at the front of the interface definition
*/
#define OBJECT_INTERFACE  \
	int __thisoffset; \
	int (*QueryInterface)(HOBJECT object, IIDTYPE iid, const void **pInterface); \
	int (*AddRef)(HOBJECT object); \
	int (*Release)(HOBJECT object); \
	int (*IsValid)(HOBJECT object); \

/*This macro initializes the pointer variable of the specified interface, followed by interface_ Corresponding to declare,
Put in object Create function
*/
#define INTERFACE_INIT(__interface, objptr, _obj, interfacename) \
    objptr->__##__interface##_ptr = &_obj##_##interfacename##_interface;

/*This macro declares public object interfaces and functions related to object management,
Implement the Register function and define the data of the public object interface*/
#define OBJECT_FUNCDECLARE(_obj, _clsid)\
	static int _obj##QueryInterface(HOBJECT object, IIDTYPE iid, const void **pInterface); \
	static int _obj##AddRef(HOBJECT object); \
	static int _obj##Release(HOBJECT object); \
	static int _obj##IsValid(HOBJECT object); \
	static void _obj##Destroy(HOBJECT object); \
	static int _obj##Create(const PARAMITEM * pParams, int paramcount, HOBJECT* pObject); \
	static int _obj##Valid(HOBJECT object);\
	static const IObject _obj##_object_interface = { \
		0, \
		_obj##QueryInterface, \
		_obj##AddRef, \
		_obj##Release, \
		_obj##IsValid \
	}; \
	static const char * _obj##ModuleInfo();\
	static int _obj##Register() {\
		return objectCreateRegister(_clsid, _obj##Create, _obj##ModuleInfo); \
	}\
	OFUNCPTR A_u_t_o_registor_##_obj = (OFUNCPTR)_obj##Register;
	
/*This macro defines the functions and variables of the public object, which is placed at the front of the interface definition*/
#define INTERFACE_HEADER(_obj, __interface, _localstruct) \
		(int)&(((const _localstruct*)0)->__##__interface##_ptr), \
		_obj##QueryInterface, \
		_obj##AddRef, \
		_obj##Release, \
		_obj##IsValid,

/*This macro defines the beginning of the implementation of the QueryInterface function and implements the query of the IObject interface*/
#define QUERYINTERFACE_BEGIN(_obj, _clsid) \
static int _obj##QueryInterface(HOBJECT object, IIDTYPE iid, const void **pInterface) \
{ \
	if (!objectIsClass(object, _clsid)) \
		return EIID_INVALIDOBJECT; \
  if (!objectIsValid(object)) \
		return EIID_INVALIDOBJECT; \
	if (pInterface == 0) \
		return EIID_INVALIDPARAM; \
	*pInterface = 0; \
	if (isGUIDEqual(iid ,IID_OBJECT)) { \
		*pInterface = objectThis(object); \
		objectAddRef(object); \
		return EIID_OK; \
	}

/*This macro follows queryinterface_ After the begin macro, every interface implemented is followed by an item,
Realize the query function of this interface*/
#define QUERYINTERFACE_ITEM(_iid, __interface, _localstruct) \
	else if (isGUIDEqual(iid, _iid)) { \
		*pInterface = &((_localstruct *)(objectThis(object)))->__##__interface##_ptr; \
		objectAddRef(object); \
		return EIID_OK; \
	} \

/*QueryInterface End of function implementation*/
#define QUERYINTERFACE_END \
	return EIID_UNSUPPORTEDINTERFACE; \
}

/*This macro initializes the public object interface in the object and returns the generated object,
As the last part of the Create function*/
#define OBJECT_RETURN_GEN(_obj, _objptr, _retvar, _sid) \
do \
{ \
	_objptr->__IObject_ptr = &_obj##_object_interface; \
	_objptr->__object_refcount = 1; \
	_objptr->__object_clsid = _sid;	\
	*_retvar = (HOBJECT)&_objptr->__IObject_ptr; \
}while (0)

/*This macro provides AddRef in the public object interface, 
Release, IsValid Function implementation*/
#define OBJECT_FUNCIMPL(_obj, _localstruct, _sid) \
static int _obj##AddRef(HOBJECT object) \
{ \
	_localstruct * pD; \
  if (!objectIsValid(object)) \
		return EIID_INVALIDOBJECT; \
  pD = (_localstruct *)objectThis(object); \
	pD->__object_refcount++; \
	return pD->__object_refcount; \
} \
 \
static int _obj##Release(HOBJECT object) \
{ \
	_localstruct * pD; \
	int ret; \
  if (!objectIsValid(object)) \
		return EIID_INVALIDOBJECT; \
  pD = (_localstruct *)objectThis(object); \
	pD->__object_refcount--; \
	ret = pD->__object_refcount; \
	if (pD->__object_refcount <= 0) { \
	  pD->__object_refcount = 1; /*In order to ensure that recursive calls do not occur in the Destroy process, the reference count is set to 1 here*/ \
		_obj##Destroy(object); \
	} \
	return ret; \
} \
\
static int _obj##IsValid(HOBJECT object) \
{ \
	_localstruct * pD; \
	if (object == 0) \
		return 0; \
    pD = (_localstruct *)objectThis(object); \
	if (pD->__object_clsid != _sid) \
		return 0; \
	if (pD->__object_refcount < 1) \
		return 0; \
	return _obj##Valid(object); \
}

Use these macros to rewrite the interface definition in 5.1 and define the auxiliary macros implemented by IGLApp at the same time:

typedef struct sIGLApp {
  OBJECT_INTERFACE
  int (*Render)(HOBJECT object, int x, int y, int width, int height);
  int (*SwitchIn)(HOBJECT object);
  int (*SwitchOut)(HOBJECT object);
  int (*GetOption)(HOBJECT object, int option);
  int (*MouseLeftDoubleClick)(HOBJECT object, int x, int y, int ctrlkey);
  int (*MouseLeftDown)(HOBJECT object, int x, int y, int ctrlkey);
  int (*MouseLeftUp)(HOBJECT object, int x, int y, int ctrlkey);
  int (*MouseRightDoubleClick)(HOBJECT object, int x, int y, int ctrlkey);
  int (*MouseRightDown)(HOBJECT object, int x, int y, int ctrlkey);
  int (*MouseRightUp)(HOBJECT object, int x, int y, int ctrlkey);
  int (*MouseMove)(HOBJECT object, int x, int y, int ctrlkey);
  int (*KeyDown)(HOBJECT object, int key, int ctrlkey);
  int (*KeyUp)(HOBJECT object, int key, int ctrlkey);
}IGLApp;

#define GLAPP_VARDECLARE
#define GLAPP_VARINIT(_objptr, _sid)

#define GLAPP_FUNCDECLARE(_obj, _clsid, _localstruct) \
  static int _obj##_glapp_Render(HOBJECT object, int x, int y, int width, int height); \
  static int _obj##_glapp_SwitchIn(HOBJECT object); \
  static int _obj##_glapp_SwitchOut(HOBJECT object); \
  static int _obj##_glapp_GetOption(HOBJECT object, int option); \
  static int _obj##_glapp_MouseLeftDoubleClick(HOBJECT object, int x, int y, int ctrlkey); \
  static int _obj##_glapp_MouseLeftDown(HOBJECT object, int x, int y, int ctrlkey); \
  static int _obj##_glapp_MouseLeftUp(HOBJECT object, int x, int y, int ctrlkey); \
  static int _obj##_glapp_MouseRightDoubleClick(HOBJECT object, int x, int y, int ctrlkey); \
  static int _obj##_glapp_MouseRightDown(HOBJECT object, int x, int y, int ctrlkey); \
  static int _obj##_glapp_MouseRightUp(HOBJECT object, int x, int y, int ctrlkey); \
  static int _obj##_glapp_MouseMove(HOBJECT object, int x, int y, int ctrlkey); \
  static int _obj##_glapp_KeyDown(HOBJECT object, int key, int ctrlkey); \
  static int _obj##_glapp_KeyUp(HOBJECT object, int key, int ctrlkey); \
  static const IGLApp _obj##_glapp_interface = { \
		INTERFACE_HEADER(_obj, IGLApp, _localstruct) \
             _obj##_glapp_Render, \
             _obj##_glapp_SwitchIn, \
             _obj##_glapp_SwitchOut, \
             _obj##_glapp_GetOption, \
             _obj##_glapp_MouseLeftDoubleClick, \
             _obj##_glapp_MouseLeftDown, \
             _obj##_glapp_MouseLeftUp, \
             _obj##_glapp_MouseRightDoubleClick, \
             _obj##_glapp_MouseRightDown, \
             _obj##_glapp_MouseRightUp, \
			 _obj##_glapp_MouseMove, \
             _obj##_glapp_KeyDown, \
             _obj##_glapp_KeyUp, \
};

Rewrite the implementation code in 5.2, and the result is:

#include "stdio.h"
#include "object.h"
#include "glapp.h"
#include "gl/glew.h"
#include "loadprogram.h"

#define IMPLEMENT_GUID
#include "glapp_mandelbrot.h"
#undef IMPLEMENT_GUID

typedef struct _sMandelBrotApp {
	OBJECT_HEADER
	INTERFACE_DECLARE(IGLApp)
	GLAPP_VARDECLARE
	GLuint m_program;
	double psize;
	double pfromx;
	double pfromy;
	int	psizeLoc;
	int pfromLoc;
	int vertexLoc;
	int inited;
	int x, y, w, h;
}sMandelBrotApp;

OBJECT_FUNCDECLARE(mandelbrotapp, CLSID_MANDELBROT);
GLAPP_FUNCDECLARE(mandelbrotapp, CLSID_MANDELBROT, sMandelBrotApp);

OBJECT_FUNCIMPL(mandelbrotapp, sMandelBrotApp, CLSID_MANDELBROT);

QUERYINTERFACE_BEGIN(mandelbrotapp, CLSID_MANDELBROT)
QUERYINTERFACE_ITEM(IID_GLAPP, IGLApp, sMandelBrotApp)
QUERYINTERFACE_END

static int mandelbrotappCreate(const PARAMITEM * pParams, int paramcount, HOBJECT * pObject)
{
	sMandelBrotApp * pobj;
	pobj = (sMandelBrotApp *)malloc(sizeof(sMandelBrotApp));
	if (pobj == NULL)
		return -1;
	pobj->m_program = 0;
	pobj->psize = 4.0;
	pobj->pfromx = -0.75;
	pobj->pfromy = 0;
	pobj->inited = 0;
	*pObject = 0;
	GLAPP_VARINIT(pobj, CLSID_MANDELBROT);
	INTERFACE_INIT(IGLApp, pobj, mandelbrotapp, glapp);
  	
	/*Returns the generated object*/
	OBJECT_RETURN_GEN(mandelbrotapp, pobj, pObject, CLSID_MANDELBROT);
	return EIID_OK;
}

static const char *mandelbrotappModuleInfo()
{
	return "1.0.0-20210408.1108 MandelBrot App";
}

/*Omit later*/

This is much simpler. The code from object definition to Create was originally 130 lines, but now it has become 6 lines of macro definition, and there will be no inconsistency. Most of the program can focus on the algorithm to be implemented.
This time is a little long. Later, I will have the opportunity to introduce how to use to realize the general interface of data structures such as linked list, binary tree and so on.

Keywords: C

Added by the max on Sat, 19 Feb 2022 19:11:37 +0200