本文最后更新于 1640 天前,其中的信息可能已经有所发展或是发生改变。
内容纲要
背景
CUPOJ支持许多的语言进行编译并运行。当需要给判题机增加一个新的语言时,更改源代码内容、编译、测试、发布,整个过程需要频繁改动内部代码,这样破坏了开闭原则。
因此不妨使用动态链接库解决这个问题。
然而经过查询,动态链接库是不支持直接导出类型的。好在我们可以通过对基类的继承,在动态链接库的源代码暴露一个一个方法用于返回动态链接库提供的对象。
本文实现的方式基于如何在运行时加载C++函数和类,这里提供一个将以上内容整合到CMake项目中的解决方案。
CMakeLists.txt
add_library(c11 SHARED C11.h C11.cpp Language.h Language.cpp)
其中c11
是生成的动态链接库文件的名字,最终这个库在Linux系统上被编译为libc11.so
。因此需要加上prefixlib
。
注意,动态链接库需要标注SHARED
后面加上这个库包含的所有文件。在本例中包含C11的类,以及语言Language
基类。
Language.h
#ifndef JUDGE_CLIENT_LANGUAGE_H
#define JUDGE_CLIENT_LANGUAGE_H
#define extlang extern "C" Language*
#define deslang extern "C" void
#include
#include
class Language {
public:
virtual void run(int memory) = 0;
virtual void setProcessLimit();
virtual void setCompileProcessLimit();
virtual void compile(std::vector&, const char*, const char*);
protected:
virtual void setCPULimit();
virtual void setFSizeLimit();
virtual void setASLimit();
virtual void setAlarm();
};
typedef Language* createLanguageInstance();
typedef void destroyLanguageInstance(Language*);
#endif //JUDGE_CLIENT_LANGUAGE_H
设计Language为抽象类,设计两个typedef用于调用构造函数和析构函数。
在需要使用动态链接库的地方,使用dlfcn.h
头文件引入动态链接库。
如下所示
#include
Language* getLanguageModel(int language) {
string languageName = languageNameReader.GetString(to_string(language));
void* languageHandler = dlopen(("/usr/lib/cupjudge/lib" + languageName + ".so").c_str(), RTLD_LAZY);
if (!languageHandler) {
cerr << "Cannot load library: " << dlerror() << endl;
exit(1);
}
dlerror();
createLanguageInstance* createInstance = (createLanguageInstance*) dlsym(languageHandler, (string("createInstance") + languageName).c_str());
const char* dlsym_error = dlerror();
if (dlsym_error) {
cerr << "Cannot load symbol create: " << dlsym_error << endl;
exit(1);
}
// destroyLanguageInstance* destroyInstance = (destroyLanguageInstance*) dlsym(languageHandler, (string("destroyInstance") + languageName).c_str());
// dlsym_error = dlerror();
// if (dlsym_error) {
// cerr << "Cannot load symbol create: " << dlsym_error << endl;
// exit(1);
// }
Language* languageInstance = createInstance();
return languageInstance;
}
我们就可以通过这个指针对其对象进行访问。
shared_ptr languageModel(getLanguageModel(lang));
languageModel->setCompileProcessLimit();
languageModel->run();
同时 在需要动态引入库的CMake项目中,加入以下内容, 引入CMAKE_DL_LIBS库
CMakeLists.txt
target_link_libraries(judge_client ${CMAKE_DL_LIBS})