繁体   English   中英

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

[英]Is there a way to transparently wrap comparable C structs in C++ generic classes?

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

struct s32 {
  int a:
  int b;
};

struct s64 {
  long a;
  long b;
};

我试图确定 C++ 中是否有一个通用构造,它可以让透明地围绕这些结构创建一个包装器,例如我不必显式编写代码来访问longint变体。

因为除了制作接口和实现两个版本之外,我不知道什么构造会提供这个 - 这只是伪代码。

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);
}

我知道为什么这不起作用。 模板是在编译时确定的,因此当使用没有实际类型参数的泛型SWapper时,编译器无法确定S实际上是什么,并且使用类型参数会创建一个排除其他类型的擦除,因此它们不能同时分配给同一个地址。

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

[更新]

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

我看到的好处是:

  • 无需完全实现两次访问 C 结构体的代码。
  • 使用包装类时无需不断重新定义泛型类型<>

再次请考虑这个伪代码。

class SWrapper {
private:
  const std::unique_ptr<s32> s32impl_;
  const std::unique_ptr<s64> s64impl_;
public:
  SWrapper(s32* s) : s32impl_(s),       s64impl_(nullptr) {};
  SWrapper(s64* s) : s32impl_(nullptr), s64impl_(s)       {};

  long get_a() {
    // could be improved with macros
    return (s32impl_->get() == nullptr) ? s32impl_->a : s64impl_->a;
  }
}

你的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";

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM