有没有办法透明地将类似的C结构包装在C ++泛型类中?

时间:2019-02-14 02:52:48

标签: c++ generics

我正在尝试创建一个通用类,该通用类将C库中的结构包装为结构。考虑这两个结构定义。

struct s32 {
  int a:
  int b;
};

struct s64 {
  long a;
  long b;
};

我正在尝试确定C ++中是否存在泛型结构,该结构可以透明地围绕这些结构创建包装器,例如,我不必显式编写代码来访问long或{{ 1}}的变化。

由于我不知道除了构造一个接口并实现这两个版本外,哪种结构会提供这种功能-这只是伪代码。

int

我知道为什么这行不通。模板是在编译时确定的,因此当使用没有实际类型参数的泛型template <typename T, typename I> class SWrapper { T s; public: SWrapper(T _s) : s(_s) {}; I get_a() { return this->s->a; } } class S32 : SWrapper<s32*, int> {}; class S64 : SWrapper<s64*, long> {}; int main(void) { s32 s1 = { 1, 2 }; s64 s2 = { 3, 4 }; SWapper* S = new S32(&s1); // I know this is incorrect S = new S64(&s2); // assert(s.get_a() == 3); } 且使用类型参数时,编译器将无法确定S的实际含义,这会导致排除其他类型的擦除因此不能将它们都分配到同一地址。

除了C宏或为实现同一接口的两个类实现所有代码外,C ++中是否有某些构造可以产生相同的效果?

[更新]

在考虑了几种模式之后,我决定使用一种我认为“编写成本最低”的模式。这与@JaMiT的答案非常接近。

我看到的好处是:

  • 不需要完全实现两次访问C结构的代码。
  • 使用包装器类时,无需不断重新定义通用类型SWapper

再次请考虑以下伪代码。

<>

1 个答案:

答案 0 :(得分:1)

S的问题不在于在编译时确定模板,而是指针不能指向不相关的类型。为您提供没有模板的相同设置:

class A;
class B;
class C * S = ???  // Cannot point to another class.

您可能会看到您要使用的类之间的联系,但是与编译器SWrapper<s32*, int>SWrapper<s64*, long>的区别与AB一样。

为了使指针指向不同的类型,这些类型需要共享一个共同的祖先。当您对从C库获取的结构有足够的限制时,这样做并不难。问题是这些结构具有共同的逻辑结构,但成员类型不同。意见澄清说,成员的类型都可以隐式转换为通用类型。 (具体来说,它们都是某种形式的带符号整数。)因此,可以定义要查看的接口:

class WrapBase {
    public:
        virtual ~WrapBase() {}

        virtual long get_a() = 0;
        virtual long get_b() = 0;
};

请注意,成员函数返回long。这是事物将隐式转换为的类型。如果需要,可以为long long

接下来,使用您的模板使此基础适应各种结构:

template <typename T>
class Wrap : public WrapBase {
    private:
        T & data;

    public:
        explicit Wrap(T & from_c) : data(from_c) {}

        long get_a() { return data.a; }
        long get_b() { return data.b; }
};

现在,您可以编写将包装结构作为参数的函数。好吧,大概这就是您想要做的事情。手动调用new是不可取的,但是它确实提供了一个演示实际多态性的快速演示。

    // Demonstration using the question's main() as context:
    WrapBase * S = new Wrap<s32>(s1);
    std::cout << "s1: {" << S->get_a() << ',' << S->get_b() << "};\n";
    delete S;
    S = new Wrap<s64>(s2);
    std::cout << "s2: {" << S->get_a() << ',' << S->get_b() << "};\n";
    delete S;

由于多态性适用于指针,因此它适用于用作函数参数的引用。


上述缺点之一是您必须两次声明所有访问函数。还有其他工作可以从包装的结构中获取值。一个更简单的代码界面(和使用?)将涉及转换而不是包装数据。获取数据时,这涉及较高的前期成本,但访问成本较低。这是否一个选项取决于执行限制,但是可能值得尝试看看它的工作情况。

struct Converted {
    long a;
    long b;

    template <typename T>
    explicit Converted(T & source) :
        a(source.a),
        b(source.b)
    {}
};

再次使用long作为可以隐式转换所有内容的类型。现在,您可以使用更熟悉的访问语法来使用数据。 (如果您仍然想要getter函数(一种合理的设计选择),则编译器应该能够内联它们,这是虚拟函数无法实现的。)

    // Demonstration using the question's main() as context:
    Converted S{s1};
    std::cout << "s1: {" << S.a << ',' << S.b << "};\n";
    Converted SS{s2};
    std::cout << "s2: {" << SS.a << ',' << SS.b << "};\n";