Turn: An overview of plug-in architecture-----implemented in C + +

Source: Internet
Author: User

An overview of a plug-in architecture-----implemented in C + +

This paper discusses a simple but effective plug-in architecture that uses C + +, dynamic-link libraries, and object-oriented programming ideas.
Let's start by looking at the benefits of using the plug-in mechanism to give us a reasonable choice to use at the right time.
1, enhanced transparency and consistency of code: Because plug-ins often encapsulate third-party libraries or code written by others, they need to clearly define the interface and face everything with a clear and consistent interface. Your code will not be confused by the conversion process or the special customization requirements of the library.
2, improve the modularity of the project: Your code is split into separate modules that can be placed in sub-project filegroups. This decoupling process makes it easier to reuse the components that are created.
3, shorter compile time: if only to explain the declarations of certain classes, and these classes use external libraries internally, the compiler no longer needs to parse the header files of the external libraries, because the implementation is done privately.
4, replacing and adding components: If you need to post patches to users, it is more efficient to update individual plugins instead of replacing each installed file. When you expand your game with a new renderer or a new unit type, you can easily implement a set of plugins for the engine.
5, use the GPL code in the project to close the source code: Generally, if you use the GPL-issued code, you also need to open your source code. However, if you encapsulate the GPL component in a plugin, you don't have to publish the plugin's source code.

Introduced
Let's start by explaining what a plug-in system is and how it works: In a normal program, if you need code to perform a special task, you have two options: either you write it yourself, or you're looking for a library that already exists to meet your needs. Now that your request has changed, you have to rewrite the code or look for a different library. Either way, it will result in code rewriting in your framework code that depends on the external library.
Now, we can have another option: in a plug-in system, any component in the project is no longer tied to a particular implementation (like the renderer can be either OpenGL-based or Direct3D-selectable), which is stripped out of the framework code and placed in a dynamic-link library by a specific method.
The so-called specific methods include creating interfaces in the framework code that decouple the framework from the dynamic library. Plug-ins provide an implementation of the interface. We distinguish plugins from normal dynamic-link libraries because they are loaded differently: The program does not directly link the plug-in, but may be found in some directories and loaded if discovered. All plug-ins can be connected to the application using a common approach.

Common Mistakes
Some programmers, when designing a plug-in system, may add a function similar to the following function for each dynamic library used as a plug-in: Pluginclass *createinstance (const char*);
They then let the plugin provide some class implementations. The engine queries the loaded plug-ins with the desired object name until a plug-in is returned, which is the practice of the "chain of responsibility" pattern in a typical design pattern. Some smarter programmers will make new designs that allow plugins to register themselves in the engine or replace the engine's internal default implementations with a custom implementation:
Void Dllstartplugin (PlugInManager &pm);
Void Dllstopplugin (PlugInManager &pm);
The main problem with the first design is that the objects created by the plug-in factory need to be converted using reinterpret_cast<>. Typically, a plug-in derives from a common base class (this is referred to as pluginclass) and references some insecure feelings. In fact, this is meaningless, the plugin should "silently" respond to input device requests, and then submit the results to the output device.
In this structure, in order to provide a number of different implementations of the same interface, the required work becomes extremely complex, if the plug-in can register themselves with different names (such as Direct3drenderer and Openglrenderer), but the engine does not know which specific implementation of the user's choice is valid. If you hardcode all possible implementation lists into the program, then the purpose of using the plug-in structure is meaningless.
If the plug-in system is implemented through a framework or library (such as a game engine), the architect will certainly expose the functionality to the application. In this way, there are some problems like how to use plug-ins in applications, plug-in authors how to engine header files, etc., which contains the possibility of a potential version conflict between the three.
A separate factory
interface, which is clearly defined by the engine, not the plugin. The engine defines the interface to guide what the plug-in does, and the plug-in specifically implements the functionality. We let the plugin register the special implementation of its own engine interface. Of course, it is rather foolish to create an instance of a plug-in implementation class directly and register it. This allows all possible implementations at the same time to be present at the same time, consuming memory and CPU resources. The solution is the factory class, whose sole purpose is to create instances of the other class at the request. If the engine defines an interface to communicate with the plug-in, then the interface should also be defined 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;

Option 1: Plug-in Manager
The next step is to consider how plug-ins register their factories in the engine and how the engines actually use these registered plugins. One option is to have a good engagement with the existing code, which is done through the Write plugin 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 the engine needs a renderer, it accesses the plug-in manager to see which renderers have been registered with the plugin. Then ask the plug-in manager to create the desired renderer, and the plug-in manager then uses the factory class to generate the renderer, and the plug-in manager doesn't even need to know the implementation details.
A plug-in consists of a dynamic library, which 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 to see if they have an export function named Registerplugin (). Of course, XML documents are also available to specify which plugins are to be loaded.

Option 2: Complete integration with fully Integrated
In addition to using the plug-in manager, you can also design a code framework from scratch to support plugins. The best way is to divide the engine into subsystems and build a system core to manage the subsystems. It might look like this:

Class Kernel {
Storageserver &getstorageserver () const;
Graphicsserver &getgraphicsserver () const;
};

Class Storageserver {
Available to plug-ins, register a new reader
void Addarchivereader (std::auto_ptr<archivereader> AL);
Query all registered readers until you find a reader that can open the specified format
Std::auto_ptr<archive> openarchive (const std::string &sfilename);
};

Class Graphicsserver {
Used by plugins to add drivers
void Addgraphicsdriver (std::auto_ptr<graphicsdriver> AF);

Get the number of active graphics drivers
size_t getdrivercount () const;
Back to Drive
Graphicsdriver &getdriver (size_t Index);
};
There are two subsystems, which use "Server" as the suffix. The first server maintains a list of valid image loaders, each time the user wants to load a picture, the image loader is queried by one by one until a particular implementation is found to handle a particular format of the picture. Another subsystem has a list of graphicsdrivers, which are used as Renderers's factory. It can be direct3dgraphicsdriver or openglgraphicsdrivers, which are responsible for the creation of Direct3drenderer and Openglrenderer, respectively. The engine provides a valid driver list for the user to choose to use, and the new driver can be added by installing a new plugin.

Version
In the two options above, you are not forced to put a specific implementation into the plug-in. Suppose your engine provides a default implementation of a reader to support the custom package format. You can put it in the engine itself and register it automatically when the storageserver starts.
Now there is one more question that is not discussed: If you are not careful, the plug-in that does not match the engine (for example, obsolete) will be loaded. Some changes in the subsystem classes or changes in the plug-in manager are sufficient to cause changes in the memory layout, which can occur when mismatched plug-ins attempt to register or even crash. More annoying is that these are difficult to find during debugging. Fortunately, it's easy to identify outdated or incorrect plugins. The surest way to do this 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;

The plugin
extern int Getexpectedengineversion () {
return myengineversion;
}
After the constant is compiled into the plugin, any plug-in that is not recompiled will return the previous value when the constant in the engine changes. The engine can refuse to load a mismatched plug-in based on this value. In order for the plugin to work again, it must be recompiled. Of course, the biggest danger is that you forget to update the constant value. Anyway, you should have an automated version management tool to help you.

English Original address: Http://www.nuclex.org/articles/building-a-better-plugin-architecture
There are sample code downloads.

Turn: An overview of plug-in architecture-----implemented in C + +

Related Article

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.