C++ 从动态库返回的向下转换类型

C++ 从动态库返回的向下转换类型,c++,c++11,c++14,dynamic-cast,dynamic-library,C++,C++11,C++14,Dynamic Cast,Dynamic Library,我正在编写一个动态库,它返回指向基类的指针,但我想将它们向下转换到派生类。例如: //Library code: class A { public: A(void) = default; virtual ~A(void) = default; virtual void Foo(void) { std::cout << "A::Foo\n"; }; }; class B : public A { public: B(vo

我正在编写一个动态库,它返回指向基类的指针,但我想将它们向下转换到派生类。例如:

//Library code:
class A
{
public:
    A(void) = default;
    virtual ~A(void) = default;
    virtual void Foo(void)
    {
        std::cout << "A::Foo\n";
    };
};

class B : public A
{
public:
    B(void) = default;
    void Foo(void) override
    {
        std::cout << "B::Foo\n";
    }
    virtual void Bar(void)
    {
        std::cout << "B::Bar\n";
    }
};

class Factory
{
public:
    Factory(void) = default;
    virtual std::shared_ptr<A> CreateB(void)
    {
        return std::shared_ptr<A>{new B{}};
    }
};

extern "C" std::shared_ptr<Factory> CreateFactory(void)
{
    return std::make_shared<Factory>();
}


//Application code:
int main(int argc, char* argv[])
try
{
    auto handle = dlopen("./Dynamic.so", RTLD_LAZY);
    if (handle == nullptr)
    {
        throw std::runtime_error{dlerror()};
    }
    auto factoryaddress = 
        reinterpret_cast<std::shared_ptr<Factory>(*)(void)>(
        dlsym(handle, "CreateFactory"));
    if (factoryaddress == nullptr)
    {
        dlclose(handle);
        throw std::runtime_error{dlerror()};
    }
    auto factory = factoryaddress();
    auto a = factory->CreateB();
    a->Foo();
    auto b = std::dynamic_pointer_cast<B>(a);
    b->Bar();
    std::cin.get();
}

我打赌您显示的代码与您实际尝试编译的代码不同。为了解决构建时链接问题,您必须

  • 使用RTTI支持编译,这是因为您使用了
    dynamic\u cast
    ,并且
  • 在类中有一个非内联虚函数,使用它的RTTI信息
  • 在您的代码中,所有内容都是内联的,但是如果您使用
    ~A()
    并将其放入一个单独的源文件中(而不是在公共头文件中),则会出现此错误

    解决方案很简单,但有点难看。所有导出的类必须具有所有内联虚函数。您可以使用具有实际实现的私有非内联非虚拟函数,只需从一个函数调用另一个函数

    这一切背后的原因是,如果翻译单元定义类的第一个非内联虚拟函数,则会发出RTTI信息(
    typeinfo
    和一些其他符号)。在您的例子中,当您编译应用程序时,编译器看不到任何虚拟函数定义,并假定
    typeinfo
    s位于其他对象文件中。这是不正确的,因此链接器失败


    当所有虚拟函数都是内联函数时,RTTI填充在引用该类的每个对象文件中作为弱(也称为“comdat”部分)发出。因此,您可以在库和应用程序之间共享该类,两者都具有相同的弱RTTI符号。当应用程序加载库时,第二组重复的RTTI符号被丢弃,库代码链接到应用程序代码中已经存在的符号。这是常见的弱符号机制。

    我打赌您显示的代码与您实际尝试编译的代码不同。为了解决构建时链接问题,您必须

  • 使用RTTI支持编译,这是因为您使用了
    dynamic\u cast
    ,并且
  • 在类中有一个非内联虚函数,使用它的RTTI信息
  • 在您的代码中,所有内容都是内联的,但是如果您使用
    ~A()
    并将其放入一个单独的源文件中(而不是在公共头文件中),则会出现此错误

    解决方案很简单,但有点难看。所有导出的类必须具有所有内联虚函数。您可以使用具有实际实现的私有非内联非虚拟函数,只需从一个函数调用另一个函数

    这一切背后的原因是,如果翻译单元定义类的第一个非内联虚拟函数,则会发出RTTI信息(
    typeinfo
    和一些其他符号)。在您的例子中,当您编译应用程序时,编译器看不到任何虚拟函数定义,并假定
    typeinfo
    s位于其他对象文件中。这是不正确的,因此链接器失败


    当所有虚拟函数都是内联函数时,RTTI填充在引用该类的每个对象文件中作为弱(也称为“comdat”部分)发出。因此,您可以在库和应用程序之间共享该类,两者都具有相同的弱RTTI符号。当应用程序加载库时,第二组重复的RTTI符号被丢弃,库代码链接到应用程序代码中已经存在的符号。这是常见的弱符号力学。

    是的,在我的测试用例中,我将定义放在一个单独的文件中。我会试试你的建议。我可以在导出的类中使用纯虚函数吗?可以。关于主题,纯虚拟函数的行为类似于内联。纯虚函数没有二进制定义可以用来确定哪个对象文件是类的主要实现者。我会试试你的建议。我可以在导出的类中使用纯虚函数吗?可以。关于主题,纯虚拟函数的行为类似于内联。纯虚函数并没有二进制定义可以用来确定哪个对象文件是类的主要实现者。
    /usr/include/c++/5/bits/shared_ptr.h:458: undefined reference to `typeinfo for B'
    /usr/include/c++/5/bits/shared_ptr.h:458: undefined reference to `typeinfo for A'