看起来您每次切换库时都要重新编译,在这种情况下,您可以使用模板特化而不是继承。
选择使用哪个几乎是你所拥有的(#if
基于某些东西),但你会保存虚拟成员,这意味着编译器仍然可以内联,这意味着在某些情况下它可能会更快。
首先,采用描述每个实现的结构。在这里,您可以将在所有库中以相同方式工作的基本 API 名称。例如,如果它们都支持加法操作,该操作采用指向两个大数字的指针并返回一个指向包含结果的新大数字的指针,您可以执行以下操作:
(请注意,我没有通过编译器运行它,我不知道实际的 API 是什么样的,但它应该足以给出该方法的一般概念)
struct GMP {
GMP_ptr* add(GMP_ptr *l, GMP_ptr*r) {
return GMPadd(l, r);
}
};
struct OpenSSL {
OpenSSL_ptr* add(OpenSSL_ptr*, OpenSSL_ptr*) {
OpenSSL_ptr ret = NULL;
OpenSSLadd(l, r, &ret);
return ret;
}
};
现在我们可以定义一个通用的超类,其中包含这些易于映射的 API 的使用:
template< typename B, typename R >
class common {
public:
// Assume that all types have the same API
R operator + (const common &r) {
return R(B::add(l.ptr, r.ptr));
}
};
B 类是定义大数 API 的结构体,R 类是真正的实现子类。通过像这样传入 R,我们解决了协变返回问题。
对于真正的实现,我们定义了一个模板来为我们完成工作:
template< typename B >
class big_num;
现在我们可以将其专门用于实现:
template<>
class big_num<OpenSSL> : common< OpenSSL, big_num<OpenSSL> > {
OpenSSL_ptr *ptr;
public:
big_num(OpenSSL_ptr*p)
: ptr(p) {
}
big_num(const char *s)
: ptr(OpenSSLBigNumFromString(s)) {
}
~big_num() {
OpenSSLBigNumFree(ptr)
}
};
将operator +
来自超类,您现在可以像这样使用它们:
void foo() {
big_num< GMP > gmp1("123233423"), gmp2("234");
big_num< GMP > gmp3 = gmp1 + gmp2;
big_num< OpenSSL > ossl1("1233434123"), ossl2("234");
big_num< OpenSSL > ossl3 = ossl1 + ossl2;
}
此处的优势在于,由于使用结构来适应相似的 API 功能和一个模板中的通用实现,因此专业化之间的代码重复最少。给定 API 的细节现在在模板特化中,但没有虚拟,也没有通用的超类。这意味着编译器可以内联包装器中的几乎所有内容,这将使它们基本上尽可能快。
由于专业化,您还可能可以访问所有实现,这可能会使您的单元测试更容易编写/管理(您也应该能够编写这些的模板版本)。
如果您只希望其中一个可见,则如下所示:
#if BIGNUM=="GMP"
typedef big_num<GMP> used_big_num;
#elif BIGNUM=="OpenSSL"
typedef big_num<OpenSSL> used_big_num;
#endif
如果标题并不总是可用,您可能还需要围绕专业设置防护,在这种情况下,您也需要一组HAVE_GMP_BIGNUM
和HAVE_OPENSSL_BIGNUM
宏。