[英]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++ 中是否有一个通用构造,它可以让透明地围绕这些结构创建一个包装器,例如我不必显式编写代码来访问long
或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);
}
我知道为什么这不起作用。 模板是在编译时确定的,因此当使用没有实际类型参数的泛型SWapper
时,编译器无法确定S
实际上是什么,并且使用类型参数会创建一个排除其他类型的擦除,因此它们不能同时分配给同一个地址。
除了 C 宏或为实现相同接口的两个类实现所有代码之外,C++ 中是否有一些构造可以产生相同的效果?
[更新]
在考虑了几种模式之后,我确定了一种我认为“编写成本最低”的模式。 它非常接近@JaMiT 的答案。
我看到的好处是:
<>
。再次请考虑这个伪代码。
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>
与A
和B
一样不同。
为了有一个指向不同类型的指针,这些类型需要共享一个共同的祖先。 当您对从 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.