2017-03-15 4 views
0

J'essaye d'ajouter une capacité de plugin à ma base de code C++. La difficulté vient du fait que les plugins doivent contenir de la plomberie que le plugin ne doit pas connaître (en gardant ainsi le fichier include simple). Donc, voici la configuration:Système de plugins C++ où les plugins héritent de la classe de base

"PluginBase.h". C'est la classe dont le plugin hériterait.

class PluginBase { 
    virtual void PluginProcess() = 0; // the plugin-specific capability 
}; 

"PluginPlumbing.h". La classe qui contient la plomberie.

class PluginPlumbing : public PluginBase { 
    void PlumbingFunction() { 
    // Some stuff 
    PluginProcess(); 
    // Some more stuff 
    } 
}; 

Le code-cadre extérieur serait (en chargeant la DLL/donc du plug-in) acquérir un pointeur vers une instance de la classe PluginPlumbing, puis appelez PlumbingFunction() sur elle. Cependant, l'énigme que j'ai est, je ne peux pas simplement upcast un pointeur de PluginBase que je reçois de la DLL/ainsi à un pointeur de PluginPlumbing car il ne hérite pas clairement de PluginPlumbing. Et je ne peux pas avoir le plugin hérité de PluginPlumbing, parce que je suis de retour à la première place d'exposer la plomberie à l'auteur de plugin.

La seule solution que je peux imaginer est que, au lieu de bien hériter, le PluginBase et le PluginPlumbing sont des classes entièrement séparées. Le PluginBase serait instancié par la DLL/so, et l'instance de PluginPlumbing serait instanciée par le framework, et a donné ce pointeur PluginBase pour qu'il puisse faire les appels de plomberie. Est-ce la seule solution pour y arriver?

Répondre

0

Si vous souhaitez exposer certaines fonctionnalités de vos plugins à des logiciels externes, vous devez absolument fournir une interface pour cela.

Dans votre exemple, vous avez fourni l'interface PluginBase avec la fonction PluginProcess(), ainsi, tout autre utilisateur de votre interface PluginBase peut l'appeler sans se soucier de son implémentation.

Si vous avez besoin d'une autre interface avec une autre méthode, faites-le de la même manière.

class PluginPlumbing { 
public: 
    virtual void PlumbingFunction() = 0; 
}; 

Et cacher la mise en œuvre dans votre implémentation DLL:

class PluginPlumbingImpl : public PluginPlumbing { 
public: 
    void PlumbingFunction() override { 
     // do the stuff 
    } 
} 

Si elle a besoin de paramètres supplémentaires - passer aussi comme des classes d'interface abstraite ou des structures de POD. Vous devriez également avoir une déclaration pour votre fonction de plugin qui créera des instances exactes pour vos implémentations d'interface (qui devraient être accessibles par les utilisateurs de vos plugins).

Pour résumer, vous devriez avoir quelque chose comme ça:

// myplugininterface.h 
// this header will be exposed to plugin implementors and 
// plugin consumers 

class IMyPluginClass1 { 
public: 
    virtual void func1() = 0; 
    virtual void func2() = 0; 
} 

// another interface, ties together other functionality 
class IMyPluginClass2 { 
public: 
    virtual void func1() = 0; 
    // you can even pass around your interface classes 
    virtual void doSomethingWithAnotherObject(IMyPluginClass1 *obj) = 0; 

    // or use "factory" methods to create objects 
    virtual IMyPluginClass1 *createObject() = 0; 
} 

// this is functions implemented by a plugins, they should create 
// instances for your plugin objects 
// you could do them as a static methods of your classes if you don't 
// plan to expose that as C compatible plugins 
IMyPluginClass1 *createObject1(); 
IMyPluginClass2 *createObject2(); 


// mycoolplugin.cpp 
// specific implementation of your plugin, you or someone else 
// compile this to plugin DLL 

#include "myplugininterface.h" 

class IMyPluginClass1Impl : public IMyPluginClass1 { 
public: 
    IMyPluginClass1Impl() : 
     myMyValue(100500) 
    {} 

    void func1() override { 
     // implement 
    } 
    void func2() override { 
     // implement 
    } 
private: 
    // you can have any private or even public members in your implementation 
    int mMyValue; 
}; 

class IMyPluginClass2Impl : public IMyPluginClass2 { 
public: 
    void func1() override { 
     // implement 
    } 

    void doSomethingWithAnotherObject(IMyPluginClass1 *obj) override { 
     // implement 
     // but don't assume you can cast IMyPluginClass1 to 
     // something specific, because it might be not yours implementation 
     // it depends on how carefully you design your interfaces and 
     // explain to plugin writers what is allowed and what is not 
    } 

    IMyPluginClass1 *createObject() { 
     // be careful with that, in that case you MUST declare 
     // virtual destructor as a part of your interface class 
     return new IMyPluginClass1Impl(); 
    } 
}; 

IMyPluginClass1 *createObject1() { 
    return new IMyPluginClass1Impl(); 
} 
IMyPluginClass2 *createObject2() { 
    return new IMyPluginClass2Impl(); 
} 

Et les utilisateurs de votre plugin peut utiliser uniquement en incluant myplugininterface.h et obtenir des adresses de create fonctions (ce qui est dépendant de la plateforme).

Gardez à l'esprit que si vous revenez instances créées par new et permettent aux utilisateurs de vos plugins à delete objets créés comme ça - vous devez déclarer destructor virtuel pour vos classes d'interface.

Ceci est une approche générale.Il a quelques pièges quand vous avez vos objets plugin, vous ne pouvez pas partager l'implémentation commune pour vos classes abstraites sans mettre un effort supplémentaire (en supposant que vous ne voulez pas avoir copypasta)