我已经使用C++抽象类定义了一个接口(interface)。我想针对我项目的不同变体实例化它多次(变体可以是硬件变体,也可以是OS变体)。我的目标是为每个变体创建共享库,并为给定项目使用正确的库,而无需重新编译任何内容。
我正在使用公共(public)函数来检索给定变量的必需实例。在每个变量的cpp文件中,我定义了一个函数以返回该变量的实例。在主代码中,我对该函数进行了外部声明。我打算使用返回的对象来调用接口(interface)的方法。
// Interface.hpp
class Interface {
public:
virtual void routine() = 0;
};
// variantA.cpp
class variantA : public Interface {
public:
void routine() {
printf( "We are in variantA\n" );
}
} variant;
Interface& getVariant() {
return variant;
}
// variantB.cpp
class variantB : public Interface {
public:
void routine() {
printf( "We are in variantB\n" );
}
} variant;
Interface& getVariant() {
return variant;
}
// main.cpp
#include "Interface.hpp"
extern Interface& getVariant();
int main() {
Interface& interface = getVariant();
interface.routine();
}
// build.sh
g++ -c -fPIC -O0 variantA.cpp
g++ -c -fPIC -O0 variantB.cpp
g++ variantA.o -shared -o libvariantA.so
g++ variantB.o -shared -o libvariantB.so
ln -s libvariantA.so libvariant.so
g++ -L. -lvariant main.cpp -o main
主要问题:我的方法在理论上正确吗?这样做是一个好主意吗?或者,这种方法有什么明显的陷阱吗?如果是这样,有人可以推荐我一种更好的方法吗?
第二个问题:我可能会通过一些试验弄清楚自己。但是我仍然发布此消息,因为这可能会使该方法不可用。问题是编译错误:
In function `main':
main.cpp:(.text+0x9): undefined reference to `getVariant()'
collect2: error: ld returned 1 exit status
最佳答案
首先,不要在使用之前声明声明,该声明属于接口(interface) header ,这正是 header 所擅长的。
问题
但我担心我们在这里得到了XY problem:
那是不可能的:看到您通常不能为硬件或OS变体a编译libvarianta,而为硬件或OS变体b编译libvariantb并不能在一个同时链接到a和b的程序中一起使用。
问题在于,通常需要再次编译是有原因的:不同的硬件和不同的操作系统通常需要不同的机器代码。即使您执行的printf
调用也可以转换为针对不同编译器或操作系统的不同机器代码。这就是C++标准和参考对运行时和实现的含义。
同样,您甚至可能甚至无法调用该函数,因为在使用共享链接时,您会遇到ABI(不是API)的问题,这也意味着即使对于具有相同体系结构的相同Microsoft编译器版本,也必须对其进行一次编译以进行调试。一次用于发布版本(unless further precaution are followed)。
通过使用预处理器,大多数库甚至具有在不同平台上编译的不同高级代码。
解
因此,您仍然必须为库和库的使用者编译所有所需的不同版本,这将需要复杂的逻辑才能知道要加载哪个二进制文件。换句话说:如果您将它们简单地编译在一起,就必须找到一个共享库二进制文件。因此,简单地将其静态链接并使用一个API,至少在运行时,不需要抽象类或接口(interface)会更容易。您可能需要在编译时执行不同的操作,因为您可能没有可移植的方法来执行操作。然后,您可以使用预处理器宏或compile time polymorphism。这符合John3136给您的建议。
回答
因此,否,共享链接无法解决问题,即,据我所知,达到您的尝试。这只会造成更复杂的问题。根据纯标准C++,共享链接既不可移植,也不能移植。但是,有一些技巧可以使不同的操作系统尝试实现ABI稳定性(例如Windows上的COM对象),但是如果您的代码需要针对不同的硬件或运行时,这仍然不能解决问题。