gpt4 book ai didi

C++类成员变量知道自己的偏移量

转载 作者:可可西里 更新时间:2023-11-01 15:16:23 25 4
gpt4 key购买 nike

是否有可能有一个成员变量,它能够从指向自身的指针(在它的方法中)计算指向包含对象的指针?

让我们像这样用 API 包装一个外部调用接口(interface):

template <typename Class, MethodId Id, typename Signature>
class MethodProxy;

template <typename Class, MethodId Id, typename ReturnT, typename Arg1T>
class MethodProxy<Class, Id, ReturnT ()(Arg1T) {
public:
ReturnT operator()(Class &invocant, Arg1T arg1);
};

类似地,对于从 0 到 N 的其他数量的参数。对于外部的每个类,一个 C++ 类被声明为具有一些特征,并且该模板使用这些特征(以及参数类型的更多特征)来查找和调用国外的方法。这可以像这样使用:

Foo foo;
MethodProxy<Foo, barId, void ()(int)> bar;
bar(foo, 5);

现在我想做的是以这样的方式定义 Foo,我可以这样调用:

Foo foo;
foo.bar(5);

无需多次重复签名。(显然创建静态成员并将调用包装在方法中很简单,对吧)。嗯,其实还是很简单的:

template <typename Class, MethodId Id, typename Signature>
class MethodMember;
template <typename Class, MethodId Id, typename ReturnT, typename Arg1T>
class MethodMember<Class, Id, ReturnT ()(Arg1T) {
MethodProxy<Class, Id, Signature> method;
Class &owner;
public:
MethodMember(Class &owner) : owner(owner) {}
ReturnT operator()(Arg1T arg1) { return method(owner, arg1); }
};

然而,这意味着该对象最终将包含许多指向其自身的指针拷贝。所以我正在寻找一种方法,使这些实例能够根据 this 和一些额外的模板参数计算所有者指针。

我的思路是

template <typename Class, size_t Offset, ...>
class Member {
Class *owner() {
return reinterpret_cast<Class *>(
reinterpret_cast<char *>(this) - Offset);
}
...
};
class Foo {
Member<Foo, offsetof(Foo, member), ...> member;
...
};

但这会提示 Foo 在这一点上是不完整的类型。

是的,我知道 offsetof 应该只适用于“POD”类型,但实际上对任何非虚拟成员都有效,这将是有效的。我同样尝试在该参数中传递指向(那个)成员的指针(使用虚拟基类),但这也不起作用。

请注意,如果这可行,它还可以用于实现类似 C# 的属性,委托(delegate)给包含类的方法。

我知道如何使用 boost.preprocessor 执行上面提到的包装器方法,但参数列表必须以一种奇怪的形式指定。我知道如何编写宏以通过模板生成通用包装器,但这可能会导致诊断效果不佳。如果调用看起来像 foo.bar()(5),那也很简单。但我想知道是否可以使用一些巧妙的技巧(而且只有这种巧妙的技巧可能也可用于属性)。

注意:成员类型实际上不能专用于指向它的成员指针或它的偏移量,因为在分配偏移量之前必须知道类型。那是因为类型会影响所需的对齐方式(考虑显式/局部特化)。

最佳答案

提出问题是了解答案的最佳方式,所以这就是我得到的地方:

偏移量不能是模板参数,因为在计算偏移量之前必须知道类型。所以它必须由参数的函数返回。让我们添加一个标签类型(虚拟结构),然后将重载函数放入 Owner 或直接放入标签。这样我们就可以在一个地方定义我们需要的一切(使用宏)。以下代码在 gcc 4.4.5 下编译良好,并为所有成员打印正确的指针:

#include <cstddef>
#include <iostream>

using namespace std;

(只是序言让它真正编译)

template <typename Owner, typename Tag>
struct offset_aware
{
Owner *owner()
{
return reinterpret_cast<Owner *>(
reinterpret_cast<char *>(this) - Tag::offset());
}
};

这是让对象知道它自己的偏移量所需要的。可以自由添加属性或仿函数或一些其他代码以使其有用。现在我们需要声明一些额外的东西连同成员本身,所以让我们定义这个宏:

#define OFFSET_AWARE(Owner, name) \
struct name ## _tag { \
static ptrdiff_t offset() { \
return offsetof(Owner, name); \
} \
}; \
offset_aware<Owner, name ## _tag> name

这将结构定义为标记,并放入一个返回所需偏移量的函数中。比它定义数据成员本身。

请注意,成员需要像此处定义的那样公开,但我们可以轻松地为标签支持 protected 和私有(private)属性添加“ friend ”声明。现在让我们使用它。

struct foo
{
int x;
OFFSET_AWARE(foo, a);
OFFSET_AWARE(foo, b);
OFFSET_AWARE(foo, c);
int y;
};

很简单,不是吗?

int main()
{
foo f;

cout << "foo f = " << &f << endl
<< "f.a: owner = " << f.a.owner() << endl
<< "f.b: owner = " << f.b.owner() << endl
<< "f.c: owner = " << f.c.owner() << endl;
return 0;
}

这会在所有行上打印相同的指针值。 C++ 标准不允许成员的大小为 0,但与指针的 4 或 8(取决于平台)字节相比,它们将只有实际内容的大小或 1 字节(如果它们为空)。

关于C++类成员变量知道自己的偏移量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4936987/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com