Use C ++ to implement the plug-in architecture

Source: Internet
Author: User

This article discusses a simple but effective plug-in architecture. It uses C ++, dynamic link library, and is based on the idea of object-oriented programming.
First, let's take a look at what the plug-in mechanism can bring to us.
In order to select and use it properly.
1. Enhance code transparency
Consistency
: Because plug-ins usually encapsulate code written by third-party class libraries or other people, you need to clearly define interfaces and use clear and consistent interfaces to deal with everything. Your code will not be
The special customization requirements of conversion programs or libraries are messy.
2. improve engineering Modularization
:
Your code is clearly divided into multiple independent modules, which can be placed in a file group in the Child Project. This decoupling makes it easier to reuse the created components.
3. Shorter Compilation Time
: For the purpose of interpreting the declarations of some classes
The compiler no longer needs to parse the header files of the External library, because the specific implementation is completed in private form.
4. Replace and add Components
: If you need to release patches to users, it is more effective to update individual plug-ins instead of replacing each installed file. When you use a new ingress
You can easily implement a set of plug-ins that can be provided to the engine when using a dye or new unit type to expand your game.
5. Use the GPL code in the project that closes the source code
: Generally, if you use the Code released by GPL, you also need to open your source code
. However, If You encapsulate the GPL component in the plug-in, you do not have to publish the plug-in source code.

Introduction

First, let's briefly explain what a plug-in system is and how it works: In a common program, If you need code to execute a special task, you have two options:
Select: Either write it yourself or search for an existing library that meets your needs. Now your requirements have changed, so you have to rewrite the code or find another different library. Either method causes
The code in your framework code that depends on the external library is rewritten.
Now, we can have another option: In a plug-in system, any component in the project is no longer bound to a specific implementation (like the Renderer
It can be based on OpenGL or Direct3D), which will be removed from the framework code and put into the dynamic link library through specific methods.
The so-called specific methods include
Interfaces created in framework code are decoupled from dynamic libraries. The plug-in provides interface implementation. We separate plug-ins from common dynamic link libraries because they are loaded differently: the program will not directly
Link to the plug-in, but may be searched in some directories. If found, the plug-in is loaded. All plug-ins can use a common method to connect to applications.

Common Errors

Some programmers may give
Add a function similar to the following to the dynamic library used by the plug-in: PluginClass * createInstance (const char *);
Then it
Let the plug-ins provide some class implementations. The engine queries the loaded plug-ins one by one with the expected object name until a plug-in returns. This is a typical practice of the "responsibility chain" mode in the design mode. Some smarter
Programmers will make new designs to enable plug-ins to register themselves in the engine, or use custom implementations to replace the internal default implementations of the engine:
Void
DllStartPlugin (PluginManager & pm );
Void
DllStopPlugin (PluginManager & pm );
The main problem of the first design is that the objects created by the plug-in factory need to be used.
Reinterpret_cast <>. Generally, a plug-in is derived from a common base class (pluginclass), which references some insecure feelings. Real
This is meaningless. The plug-in should "Silently" respond to the request from the input device and then submit the result to the output device.
In this structure, to provide multiple implementations of the same interface, you must
The required work becomes abnormal and complex. If the plug-in can register itself with different names (such as direct3drenderer and
Openglrenderer), but the engine does not know which specific implementation is effective for the user's choice. If the list of all possible implementations is hardcoded into the program, the purpose of using the plug-in structure is also
It makes no sense.
Assume that the plug-in system uses a framework or library (such as a game engine)
Implementation, the architect will certainly expose the function to the application. In this way, there will be some problems, such as how to use plug-ins in applications, how plug-ins author engine header files, etc., which includes potential three
The possibility of version conflict.
Independent Factory

Interface, is cited
The plugin is clearly defined, not the plug-in. The engine defines the interface to guide the plug-in on what to do, and the plug-in provides specific functions. Let the plug-in register the special implementation of its own engine interface. Of course, directly create a plug-in implementation class
And registration is stupid. In this way, all possible implementations exist at the same time, occupying both memory and CPU resources. The solution is the factory class, which only aims to create another class at the request.
Instance. If the engine defines an interface to communicate with the plug-in, it should also define an interface for the factory class:
Template
<Typename
Interface>
Class

Factory {
Virtual
Interface * create () = 0;
};
 
Class
Renderer {
Virtual
Void
BeginScene () = 0;
Virtual
Void
EndScene () = 0;
};
Typedef
Factory <Renderer> RendererFactory;

Select 1: plug-in Manager

Next, we should consider how the plug-ins register their factories in the engine, and how the engine actually uses this
Some registered plug-ins. One option is to work well with the existing code, which is done by writing the plug-in manager. This allows us to control which components are allowed to be extended.
Class
PluginManager {
Void

RegisterRenderer (std: auto_ptr <RendererFactory> RF );
Void

RegisterSceneManager (std: auto_ptr <SceneManagerFactory> SMF );
};
When
When an engine needs a Renderer, It accesses the plug-in Manager to see which Renderer has been registered through the plug-in. Then the plug-in manager is required to create the desired Renderer, And the plug-in manager then uses the factory class to generate Rendering
Plug-in manager does not even need to know the implementation details.
The plug-in is composed of dynamic libraries. The latter exports a function that can be called by the plug-in Manager to register itself:
Void
RegisterPlugin (PluginManager
& PM );
The plug-in Manager simply loads all dll files in a specific directory and checks whether they have an export function named registerPlugin. Of course
You can also use xml documents to specify which plug-ins are to be loaded.

Select 2:
Fully Integrated

In addition to the plug-in manager, you can also design a code framework from the ground up to support plug-ins. The best way is to divide the engine into several
Subsystem, build a system core to manage these subsystems. It may be like the following:

Class Kernel {
StorageServer & getStorageServer () const;
 
GraphicsServer & getGraphicsServer () const;
};
 
Class
StorageServer {
 
// Provided for the plug-in to register a new reader

Void
AddArchiveReader (std: auto_ptr <ArchiveReader> AL );
// Query all registered readers until the specified format can be opened.

 
Std: auto_ptr <Archive> openArchive (const std: string
& SFilename );
};
 
Class GraphicsServer {
// Used by the plug-in to add a driver

Void
AddGraphicsDriver (std: auto_ptr <GraphicsDriver> AF );

// Obtain the number of valid image drivers

Size_t
GetDriverCount () const;
// Return driver

 
GraphicsDriver & getDriver (size_t Index );
};
There are two subsystems, they use"
Server "as the suffix. The first Server maintains a list of valid image loaders. Each time you want to load an image, the image loaders are queried one by one until a specific
To process images in a specific format. Another subsystem has a list of GraphicsDrivers, which are used as the Renderers factory. Yes
Direct3DgraphicsDriver or OpenGLGraphicsDrivers, which are responsible for Direct3Drenderer and
OpenGLRenderer creation. The engine provides a valid driver list for users to choose from. by installing a new plug-in, the new driver can also be added.

Version

In the above two optional methods, you are not required to place specific implementations
Plug-in. Assume that your engine provides a default Implementation of the reader to support custom file package formats. You can put it into the engine itself and register it automatically when StorageServer starts.
Now
Another question is not discussed: if you are not careful, plug-ins that do not match the engine (for example, outdated ones) will be loaded. Some changes to the sub-system class or plug-in Manager are enough to cause memory Layout
Changed. A conflict or even crash may occur when mismatched plug-ins attempt to register. What's annoying is that these are difficult to find during debugging.
Fortunately, it is very easy to identify outdated or incorrect plug-ins. The most reliable method is to place a preprocessing constant in your core system. Any plug-in has a function that can return this constant to the engine:
// Somewhere in your core system

# Define
MyEngineVersion 1;
 
//
Plugin

Extern int getExpectedEngineVersion (){
Return
MyEngineVersion;
}
After this constant is compiled into the plug-in, when the constant in the engine changes, any plug-in that has not been re-compiled
GetExpectedEngineVersion
() The method returns the previous value. Based on this value, the engine rejects plug-ins that do not match. To make the plug-in work again, you must recompile it. Of course, the biggest danger is that you forget to update constants.
Value. In any case, you should have an automatic version management tool to help you.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.