【发布时间】:2021-04-21 13:19:28
【问题描述】:
我有一个带有虚拟方法foo的类:
class SimpleClass {
public:
SimpleClass() = default;
virtual ~SimpleClass() {
std::cout << "in virtual destructor\n";
}
virtual int foo() {
std::cout << "in virtual method\n";
x = 42;
return x;
}
private:
int x;
};
我想动态加载它并创建实例。这是我的做法:
interfaces.h:
#include <string>
class AbstractClass
{
friend class ClassLoader;
public:
explicit AbstractClass();
~AbstractClass();
protected:
void* newInstanceWithSize(size_t sizeofClass);
struct ClassImpl* pImpl;
};
template <class T>
class Class
: public AbstractClass
{
public:
T* newInstance()
{
size_t classSize = sizeof(T);
void* rawPtr = newInstanceWithSize(classSize);
return reinterpret_cast<T*>(rawPtr);
}
};
enum class ClassLoaderError {
NoError = 0,
FileNotFound,
LibraryLoadError,
NoClassInLibrary
};
class ClassLoader
{
public:
explicit ClassLoader();
AbstractClass* loadClass(const std::string &fullyQualifiedName);
ClassLoaderError lastError() const;
~ClassLoader();
private:
struct ClassLoaderImpl* pImpl;
};
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <new>
#include <string>
#include <dlfcn.h>
#include "interfaces.h"
typedef void* (*constructor_t)();
struct ClassImpl {
void* lib = nullptr;
std::string class_name;
};
AbstractClass::AbstractClass(): pImpl(new ClassImpl()) {}
AbstractClass::~AbstractClass() {
if (pImpl->lib) {
dlclose(pImpl->lib);
}
delete pImpl;
}
std::string CreateSymbolicName(const std::string& class_name) {
size_t pos = 0;
size_t new_pos = 0;
std::string sym_name = "_ZN";
while ((new_pos = class_name.find("::", pos)) != std::string::npos) {
sym_name += std::to_string(new_pos - pos);
sym_name += class_name.substr(pos, new_pos - pos);
pos = new_pos + 2;
}
sym_name += std::to_string(class_name.size() - pos);
sym_name += class_name.substr(pos);
sym_name += "C1Ev";
return sym_name;
}
void* AbstractClass::newInstanceWithSize(size_t size_of_class) {
std::string sym_name = CreateSymbolicName(pImpl->class_name);
constructor_t constructor =
reinterpret_cast<constructor_t>(dlsym(pImpl->lib, sym_name.c_str()));
void* obj = constructor();
void* memory = new char[size_of_class];
memcpy(memory, obj, size_of_class);
return memory;
}
struct AbstractClassList {
AbstractClass* abstract_class = nullptr;
AbstractClassList* next = nullptr;
};
struct ClassLoaderImpl {
AbstractClassList* head = nullptr;
AbstractClassList* cur = nullptr;
};
ClassLoader::ClassLoader(): pImpl(new ClassLoaderImpl()) {}
ClassLoader::~ClassLoader() {
while (!pImpl->head) {
pImpl->cur = pImpl->head;
pImpl->head = pImpl->head->next;
delete pImpl->cur;
}
delete pImpl;
}
std::string parsePath(const std::string& fully_qualified_name) {
std::string result = fully_qualified_name;
std::replace(result.begin(), result.end(), ':', '/');
return std::string(std::getenv("CLASSPATH")) + "/" + result + ".so";
}
AbstractClass* ClassLoader::loadClass(const std::string& fully_qualified_name) {
auto path = parsePath(fully_qualified_name);
void* lib = dlopen(path.c_str(), RTLD_NOW|RTLD_LOCAL);
if (!lib) {
return nullptr;
}
if (!pImpl->head) {
pImpl->head = new AbstractClassList();
pImpl->cur = pImpl->head;
} else {
pImpl->cur->next = new AbstractClassList();
pImpl->cur = pImpl->cur->next;
}
pImpl->cur->abstract_class = new AbstractClass();
pImpl->cur->abstract_class->pImpl->lib = lib;
pImpl->cur->abstract_class->pImpl->class_name = fully_qualified_name;
return pImpl->cur->abstract_class;
}
在foo 是虚拟的之前,它可以正常工作。如果foo 是虚拟的,我在尝试调用它时会收到段错误。
怎么了?
【问题讨论】:
-
为什么不能只调用会创建所需对象的函数? IE。类似:
SomeClass *CreateSomeClass() {return new SomeClass();}然后你只需使用返回的指针,没有任何“魔法”。 -
这是徒劳的练习; C++ 虚拟类不能手动加载。编译器生成具有弱链接的 RTTI 位,然后链接器决定是否将它们包含在程序中。因此,任何手动事后加载都将不起作用,因为各种位(RTTI、vtables、析构函数等)已经嵌入到程序中。