Информация об изменениях

Сообщение Интерфейс плагина и его vtable от 12.10.2017 8:26

Изменено 12.10.2017 8:28 AlexGin

Интерфейс плагина и его vtable
Доброе время суток, уважаемые коллеги!

Суть вопроса в том, что у нас в разработке уже около года идёт приложение на C++ (MSVC2015) с плагинами.
Идея, заложенная в систему плагинов, кратко изложена здесь: http://www.andynicholas.com/?p=27
В основу интерфейса (aka абстрактный базовый класс в контексте C++) первоначельно заложен такой вот код:
class IPluginlInterface
{
public:
    virtual ~IPluginInterface() {};
    virtual void Init() = 0;
    virtual char* GetInterfaceName() const = 0;
    virtual char* GetInterfaceDescription() const = 0;
    virtual char* GetInterfaceVersion() const = 0;
    virtual char* GetInterfaceDateTime() const = 0;
    virtual int  GetInterfaceCategory() const = 0; 
    virtual int  GetInterfaceFlags() const = 0;
    virtual void SetCallbackNotifyer(INotifyer1* pNotyfier) = 0;
    virtual void SetIXMLConfigPointer(IXMLConfig* pXMLConfig) = 0;
    virtual void SetIDALPointer(IDAL* pIDAL) = 0;
    virtual void SetDataItem1(DataItem1* pItem) = 0; 
    virtual void SetDataItem2(DataItem2* pItem) = 0;
    virtual void SetBackupOptions(int iOptions) = 0;
    virtual INotifyer1* GetCallbackNotifyer() const = 0;
    virtual DataItem1* GetDataItem1() const = 0; 
    virtual DataItem2* GetDataItem2() const = 0;
    virtual long PerformCalculations() = 0;
    virtual void DeInit(int& aiDeInitCounter) = 0;
};

Эта версия — самая первая (старая).
Теперь интерфейс развивается — добавляются новые вирт-методы.
Я предлагаю их добавлять в самый конец — после DeInit.
Это позволит использовать те плагины, которые компилировались под старой версией интнрфейса.
Коллега по работе добавил новый метод SuspendCalculations. При этом сделал так:
class IPluginlInterface
{
public:
    virtual ~IPluginInterface() {};
    virtual void Init() = 0;
    virtual char* GetInterfaceName() const = 0;
    virtual char* GetInterfaceDescription() const = 0;
    virtual char* GetInterfaceVersion() const = 0;
    virtual char* GetInterfaceDateTime() const = 0;
    virtual int  GetInterfaceCategory() const = 0; 
    virtual int  GetInterfaceFlags() const = 0;
    virtual void SetCallbackNotifyer(INotifyer1* pNotyfier) = 0;
    virtual void SetIXMLConfigPointer(IXMLConfig* pXMLConfig) = 0;
    virtual void SetIDALPointer(IDAL* pIDAL) = 0;
    virtual void SetDataItem1(DataItem1* pItem) = 0; 
    virtual void SetDataItem2(DataItem2* pItem) = 0;
    virtual void SetBackupOptions(int iOptions) = 0;
    virtual INotifyer1* GetCallbackNotifyer() const = 0;
    virtual DataItem1* GetDataItem1() const = 0; 
    virtual DataItem2* GetDataItem2() const = 0;
    virtual long SuspendCalculations() = 0;  // Добавлен этот новый вирт-метод!!!
    virtual long PerformCalculations() = 0;
    virtual void DeInit(int& aiDeInitCounter) = 0;
};

После чего, в моих кодах вместо вызова DeInit происходил вызов PerformCalculations.
Это и понятно, так как на позицию, где ранее в таблице виртуальных функций был DeInit "переехал" PerformCalculations.
В общем — проблему вроде и пофиксили, но пока открыт такой вот вопрос:

Я полагаю, что все новые вирт-методы должны добавляться после DeInit, это обеспечит запуск старых плагинов.
Коллега по работе предлагает размещать так, как оно кажется логичнее (без ориентации на совместимость со старымы плагинами),
мотивируя это тем, что все плагины также придётся пересобирать.

А как бы сделалы Вы, уважаемые?

P.S. Технологию COM не предлагать.
Я раньше много работал с COM и знаю эту технологию, однако использовать в нашем проекте не хочу.
COM добавляет лишние сложности там, где их совсем даже не требуется.
Интерфейс плагина и его vtable
Доброе время суток, уважаемые коллеги!

Суть вопроса в том, что у нас в разработке уже около года идёт приложение на C++ (MSVC2015) с плагинами.
Идея, заложенная в систему плагинов, кратко изложена здесь: http://www.andynicholas.com/?p=27
В основу интерфейса (aka абстрактный базовый класс в контексте C++) первоначельно заложен такой вот код:
class IPluginlInterface
{
public:
    virtual ~IPluginInterface() {};
    virtual void Init() = 0;
    virtual char* GetInterfaceName() const = 0;
    virtual char* GetInterfaceDescription() const = 0;
    virtual char* GetInterfaceVersion() const = 0;
    virtual char* GetInterfaceDateTime() const = 0;
    virtual int  GetInterfaceCategory() const = 0; 
    virtual int  GetInterfaceFlags() const = 0;
    virtual void SetCallbackNotifyer(INotifyer1* pNotyfier) = 0;
    virtual void SetIXMLConfigPointer(IXMLConfig* pXMLConfig) = 0;
    virtual void SetIDALPointer(IDAL* pIDAL) = 0;
    virtual void SetDataItem1(DataItem1* pItem) = 0; 
    virtual void SetDataItem2(DataItem2* pItem) = 0;
    virtual void SetBackupOptions(int iOptions) = 0;
    virtual INotifyer1* GetCallbackNotifyer() const = 0;
    virtual DataItem1* GetDataItem1() const = 0; 
    virtual DataItem2* GetDataItem2() const = 0;
    virtual long PerformCalculations() = 0;
    virtual void DeInit(int& aiDeInitCounter) = 0;
};

Эта версия — самая первая (старая).
Теперь интерфейс развивается — добавляются новые вирт-методы.
Я предлагаю их добавлять в самый конец — после DeInit.
Это позволит использовать те плагины, которые компилировались под старой версией интерфейса.
Коллега по работе добавил новый метод SuspendCalculations. При этом сделал так:
class IPluginlInterface
{
public:
    virtual ~IPluginInterface() {};
    virtual void Init() = 0;
    virtual char* GetInterfaceName() const = 0;
    virtual char* GetInterfaceDescription() const = 0;
    virtual char* GetInterfaceVersion() const = 0;
    virtual char* GetInterfaceDateTime() const = 0;
    virtual int  GetInterfaceCategory() const = 0; 
    virtual int  GetInterfaceFlags() const = 0;
    virtual void SetCallbackNotifyer(INotifyer1* pNotyfier) = 0;
    virtual void SetIXMLConfigPointer(IXMLConfig* pXMLConfig) = 0;
    virtual void SetIDALPointer(IDAL* pIDAL) = 0;
    virtual void SetDataItem1(DataItem1* pItem) = 0; 
    virtual void SetDataItem2(DataItem2* pItem) = 0;
    virtual void SetBackupOptions(int iOptions) = 0;
    virtual INotifyer1* GetCallbackNotifyer() const = 0;
    virtual DataItem1* GetDataItem1() const = 0; 
    virtual DataItem2* GetDataItem2() const = 0;
    virtual long SuspendCalculations() = 0;  // Добавлен этот новый вирт-метод!!!
    virtual long PerformCalculations() = 0;
    virtual void DeInit(int& aiDeInitCounter) = 0;
};

После чего, в моих кодах вместо вызова DeInit происходил вызов PerformCalculations.
Это и понятно, так как на позицию, где ранее в таблице виртуальных функций был DeInit "переехал" PerformCalculations.
В общем — проблему я пофиксил. Тем не менее, пока открыт такой вот вопрос:

Я полагаю, что все новые вирт-методы должны добавляться после DeInit, это обеспечит запуск старых плагинов.
Коллега по работе предлагает размещать так, как оно кажется логичнее (без ориентации на совместимость со старымы плагинами),
мотивируя это тем, что все плагины также придётся пересобирать.

А как бы сделалы Вы, уважаемые?

P.S. Технологию COM не предлагать.
Я раньше много работал с COM и знаю эту технологию, однако использовать в нашем проекте не хочу.
COM добавляет лишние сложности там, где их совсем даже не требуется.