Использование разных несовместимых версий интерфейса CORBA в одном приложении / модуле?
Учитывая два определения IDL: (Я только реализую клиента, серверная сторона исправлена.)
// Version 1.2
module Server {
interface IObject {
void Foo1();
void Foo2() raises(EFail);
string Foo3();
// ...
}
};
// Version 2.3
module Server {
interface IObject {
// no longer available: void Foo1();
void Foo2(string x) raises(ENotFound, EFail); // incompatible change
wstring Foo3();
// ...
}
};
(Изменить примечание: добавлен метод Foo3, который не может быть перегружен из-за изменения типа возвращаемого значения.)
Можно ли как-то скомпилировать оба файла-заглушки в одном клиентском приложении C++ CORBA?
Используя значения по умолчанию компилятора IDL, два приведенных выше определения IDL приведут к тому, что код заглушки не может быть скомпилирован в один и тот же модуль C++, поскольку вы получите несколько ошибок определения от компоновщика. Однако клиент должен иметь возможность общаться с обеими версиями сервера.
Каковы возможные решения?
(Примечание: мы используем omniORB)
2 ответа
(Добавление ответа от одного Стефана Густафссона, опубликовано в comp.object.corba 2011-03-08)
Если вы рассматриваете это как проблему C++ вместо проблемы CORBA, решение - это пространства имен C++. Вы можете попытаться обернуть разные реализации в разные пространства имен C++. Подобно:
namespace v1 {
#include "v1/foo.h" // From foo.idl version 1
}
namespace v2 {
#include "v2/foo.h" // from foo.idl version 2
}
И чтобы иметь возможность компилировать код прокси / заглушки C++, вам нужно создать основные файлы C++, такие как:
// foo.cpp
namespace v1 {
#include "v1/foo_proxy.cpp" // filename depend on IDL compiler
}
namespace v2 {
#include "v2/foo_proxy.cpp"
}
Это предотвратит жалобу компоновщика C++, так как имена будут другими. Конечно, вы можете столкнуться с проблемами с компиляторами C++, не поддерживающими вложенные пространства имен.
Второе решение заключается в реализации вызова с использованием
DII
Вы могли бы написать класс C++
class ServerCall {
void foo2_v1() {
// create request
// invoke
}
void foo2_v2(String arg) {
// create_list
// add_value("x",value,ARG_IN)
// create_request
// invoke
}
}
Используя DII, вы можете создавать любые вызовы, которые вам нравятся, и можете полностью контролировать свой клиентский код.
Я думаю, что это хорошая идея, но я пока не смог ее опробовать, поэтому могут возникнуть некоторые неожиданные сюрпризы в отношении вещей, которых больше нет в глобальном пространстве имен.
Мне приходит в голову разделение клиентского кода на отдельные библиотеки для каждой версии. Затем вы можете выбрать правильный клиент в зависимости от используемой версии. В недавнем проекте мы справились с этим, представив сервисный уровень без зависимости от CORBA IDL. Например:
class ObjectService
{
public:
virtual void Foo1() = 0;
virtual void Foo2() = 0;
virtual void Foo2(const std::string &x) = 0;
};
Для каждой версии создайте класс, производный от ObjectService, и реализуйте операции, вызвав CORBA::Object. Каждый производный класс должен находиться в отдельной библиотеке.
В клиентской реализации вы работаете только с экземплярами ObjectService.
CORBA::Object_var remoteObject=... // How to get the remote object depends on your project
ObjectService *serviceObject=0;
// create a service object matching the remote object version
// Again, this is project specific
switch (getRemoteObjectVersion(remoteObject))
{
case VERSION_1_2:
serviceObject=new ServiceObjectImpl12(remoteObject);
break;
case VERSION_2_3:
serviceObject=new ServiceObjectImpl23(remoteObject);
break;
default:
// No matching version found, throw exception?
break;
}
// Access remote object through service object
serviceObject->Foo2("42");