Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 大型库的通用包装类_C++_Design Patterns_Wrapper_Libraries - Fatal编程技术网

C++ 大型库的通用包装类

C++ 大型库的通用包装类,c++,design-patterns,wrapper,libraries,C++,Design Patterns,Wrapper,Libraries,我正在从事的项目可能会受益于在不同的大型库(GMP、OpenSSL等)之间轻松交换的可能性。我当前的实现定义了一个模板抽象基类,我在其中实现了所有必需的操作符(只是为了有一些语法糖)我定义了所需的纯虚函数 对于每个库,我都有这样一个派生类:类BigNumberGmp:public BigNumberBase。我知道它有点打破OOP,但是C++协方差函数太过限制,它不允许我从方法返回BigunbBaseObjor对象,只引用引用,这是非常不可取的…… 问题是,我希望使用自定义包装的代码能够与任何此

我正在从事的项目可能会受益于在不同的大型库(GMP、OpenSSL等)之间轻松交换的可能性。我当前的实现定义了一个模板抽象基类,我在其中实现了所有必需的操作符(只是为了有一些语法糖)我定义了所需的纯虚函数

对于每个库,我都有这样一个派生类:
类BigNumberGmp:public BigNumberBase
。我知道它有点打破OOP,但是C++协方差函数太过限制,它不允许我从方法返回BigunbBaseObjor对象,只引用引用,这是非常不可取的…… 问题是,我希望使用自定义包装的代码能够与任何此类包装一起使用:BigNumberGmp、BigNumberProperteSL等。为了实现这一点,我定义了typedef BigNumberGmp BigNumber,并将其放入一些条件宏中,如下所示:

#if defined(_LIB_GMP)
    typedef BigNumberGmp BigNumber;
#endif
此外,我还以类似的方式包含适当的标题。这个实现需要我在编译器选项中定义_LIB_GMP符号

正如你所看到的,这是一种相当粗糙的技术,我并不感到自豪。此外,它不会以任何方式隐藏专用类(BigNumberGmp、BigNumberPropertensSL等)。我还可以多次定义BigNumber类,包装在_LIB_XXX条件宏中,或者我可以多次在BigNumber类中实现所需的方法,对于每个库,也包装在_LIB_XXX条件宏中。后两种想法似乎比typedef实现更糟糕,它们肯定会弄乱doxygen输出,因为它无法解释为什么我有多个同名项。我想避免使用doxygen预处理器,因为我仍然依赖于_LIB_XXX定义


有没有一种优雅的设计模式可以替代?您将如何处理这样一个问题?

我将使用pimpl习惯用法来解决这个问题。我首先定义一个包装器类:

class BigNumber {
private:
    class BigNumber_impl {
        virtual void do_something() = 0;
    }
    BigNumber_impl * impl;
public:
    void do_something() {
        impl->do_something();
    }
};
然后我会让BigNumberGMP等继承BigNumber::BigNumber\u impl。然后,您可以返回大数字的对象,但仍具有多态性


这并不能解决创建BigNumbers的问题,您还必须担心如何添加具有不同BigNumber_impl类型的BigNumbers。因此,您最初的解决方案可能符合您的目的。在某种程度上,您将不得不依赖一些预处理器的魔力。

每次切换库时,您似乎都要重新编译,在这种情况下,您可以使用模板专门化而不是继承

选择使用哪一个将与您拥有的基本相同(基于
#如果
的话),但您会保存虚拟成员,这意味着编译器仍然可以内联,这意味着在某些情况下它可能会更快

首先,以描述每个实现的结构为例。在这里,您可以在所有库中使用相同方式工作的基本API名称。例如,如果它们都支持一个add操作,该操作获取指向两个大num的指针,并返回指向包含结果的新大num的指针,则可以执行以下操作:

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;
}
#if BIGNUM=="GMP"
    typedef big_num<GMP> used_big_num;
#elif BIGNUM=="OpenSSL"
     typedef big_num<OpenSSL> used_big_num;
#endif
(请注意,我还没有通过编译器运行过这个程序,我也不知道实际的API是什么样子,但应该足以给出该方法的总体概念)

现在,我们可以定义一个通用的超类,其中包含这些易于映射的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));
    }
};
这里的优点是,由于使用结构来适应类似的API特性和一个模板中的公共实现,专业化之间的代码重复最少。给定API的细节现在在模板专业化中,但是没有虚拟化,也没有通用的超类。这意味着编译器可以将几乎所有内容内联到包装器中,这将使它们基本上尽可能快

由于专业化,您还可以访问所有实现,这可能会使您的单元测试更易于编写/管理(您也应该能够编写这些测试的模板版本)

如果您只希望其中一个可见,则如下所示:

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;
}
#if BIGNUM=="GMP"
    typedef big_num<GMP> used_big_num;
#elif BIGNUM=="OpenSSL"
     typedef big_num<OpenSSL> used_big_num;
#endif
#如果BIGNUM==“GMP”
typedef big_num used_big_num;
#elif BIGNUM==“OpenSSL”
typedef big_num used_big_num;
#恩迪夫

如果标题不总是可用,那么您可能也需要对专业设置进行保护,在这种情况下,您需要一组
HAVE_GMP_BIGNUM
HAVE_OPENSSL_BIGNUM
宏。

我认为
LIB_GMP
是实现的保留名称。我认为正确的做法是使用构建允许您拥有可选包并预处理config.h或类似内容的系统。我想到了Autohell和CMake,但我肯定还有其他的。@awoodland:谷歌没有提到任何“\u LIB\u GMP”,所以我认为它是安全的。此外,GMP内部的grep也不会产生任何结果…@Robert:我试图与CMake混在一起,但我发现这对于我的需要来说相当复杂。。。这个项目还需要在多个平台(Windows和Linux)上工作,所以我最好的选择是Visual Studio和一个自定义的Makefile。@mihaitor-仅仅因为google没有找到任何点击并不意味着它可以,它以下划线和大写字母开头,因此与()不符,不知何故,我忽略了这种实现。谢谢你的提示。我现在正在试着看看我是否能将这个设计融入到我的代码中。在这种情况下,模板专门化是什么意思?我不想让BigNumber成为模板,从而使代码过于复杂。此外,我必须在每个专门化中重复大量代码(即每个运算符重载),这是我试图避免的。您仍然可以从包含公共代码的模板继承,但实际上这是sp的一种折衷
#if BIGNUM=="GMP"
    typedef big_num<GMP> used_big_num;
#elif BIGNUM=="OpenSSL"
     typedef big_num<OpenSSL> used_big_num;
#endif