Конфликт классов c ++ в dll

У меня есть базовый абстрактный класс (interface), который используется во многих библиотеках DLL для наследования. Каждая DLL имеет экспортированный фабричный символ, который динамически создает объект и возвращает его указатель. Что произойдет, если две разные библиотеки DLL будут иметь классы с одинаковым именем, унаследованные от одного и того же абстрактного класса?

class foo
{
public:
  virtual void func()const=0;
};

Dll1

class bar: public foo
{
public:
  virtual void func()const{
    std::cout << "From Dll1" << std::endl;
  }
};

Dll2

class bar: public foo
{
public:
  virtual void func()const{
    std::cout << "From Dll2" << std::endl;
  }
};

Главный

int main()
{
  foo* obj1;
  foo* obj2;
  // load DLLs
  // import factory
  // call factory to initialize objects
  obj1->func(); // output: "From Dll1"
  obj2->func(); // output: "From Dll2"
  return typeid(*obj1) == typeid(*obj2);
}

Возвраты true, означающие, что obj1 и obj2 созданы из одного и того же класса. Так же как typeid(*obj1).name() и typeid(*obj2).name() возвращает одно и то же имя class bar. Могу ли я как-нибудь различить эти объекты с помощью RTTI, если мне не разрешено управлять самими библиотеками DLL? Должен ли класс иметь механизм, обеспечивающий его UUID для этого случая?

P.S. Как сказал IInspectable, вы можете сопоставить объект с его фабрикой. Но что, если интерфейс позволяет компилировать объекты? DLL никогда не будет различать объекты, возвращаемые из разных модулей, так как не знает фабрики. Это основная причина, по которой я хотел бы использовать RTTI.


person Ivars    schedule 10.04.2016    source источник
comment
Когда вы говорите import factory, вы уже должны предоставить модуль, который его реализует, так или иначе. Если вам не хочется сохранять сопоставление между указателями функций и модулями, вы можете получить модуль из указателя функции в любое время, вызвав GetModuleHandleEx.   -  person IInspectable    schedule 10.04.2016
comment
@IInspectable Спасибо за ответ! Я хотел бы иметь возможность объединять объекты из разных модулей. В реализации производного класса могут быть некоторые ограничения, чтобы принимать только объекты определенного типа.   -  person Ivars    schedule 10.04.2016
comment
Распространение ресурсов CRT за пределы модуля - синоним входа в мир боли. Это то, что вы можете понять правильно, но это требует глубокого знания тонкостей и огромного усердия. Подробнее см. Возможные ошибки при передаче объектов CRT через границы DLL. Хотя все это выглядит так, будто вы заново изобретаете COM, почему бы вместо этого не использовать Real Thing ™?   -  person IInspectable    schedule 10.04.2016


Ответы (1)


Во-первых, такая конструкция возможна только при динамическом связывании ваших библиотек DLL во время выполнения. Если вы используете время загрузки динамическое связывание, линк будет идентифицировать повторяющиеся символы в библиотеке импорта. Итак, мы больше не в стандартном C ++, а в конкретном поведении реализации:

HINSTANCE dll1Handle = LoadLibraryA("Test0410-1DLL1.dll");
HINSTANCE dll2Handle = LoadLibraryA("Test0410-1DLL2.dll");
fct f1, f2;
foo *obj1, *obj2; 

if (dll1Handle == NULL || dll2Handle == NULL) {
    cout << "Both DLL couldn't be uploaded!" << endl; 
}
else {
    f1 = (fct)GetProcAddress(dll1Handle, "myfactory");
    f2 = (fct)GetProcAddress(dll2Handle, "myfactory");

    if (f1 == NULL || f2 == NULL) {
        cout << "Both factories couldn't be uploaded!" << endl;
    }
    else {
        obj1 = f1(); 
        obj2 = f2(); 

        obj1->func(); 
        obj2->func();

        if (typeid(obj1) == typeid(obj2))
            cout << "Same type"<<endl; 
        else cout << "Different type" << endl; 
    }
}

Код, сгенерированный MSVC, обнаруживает, что оба имеют один и тот же тип. Но ничто не обеспечивает такого поведения.

Также сложно использовать указатели виртуальной функции, потому что вы не можете взять адрес obj->func, вам нужно возьмите адрес foo::func, который является относительным адресом и будет одинаковым для обоих классов.

Поскольку ваша конструкция находится на пределе ODR и основывается на поведении, зависящем от реализации, я думаю, что Самый простой способ безопасно различать классы - это предоставить самодельную функцию идентификации виртуального типа в foo, которая, например, объединила бы класс и имя dll.

person Christophe    schedule 10.04.2016