如何使用模板表达成员之间的约束?

发布于 2025-01-05 07:30:14 字数 776 浏览 2 评论 0原文

假设我有一个包含一堆成员的结构:

struct foo {
    int len;
    bar *stuff;
};

碰巧 stuff 将指向一个 bar 数组,长度为 len。我想将其编码为 stuff 的类型。这样的话:

struct foo {
    int len;
    DependentLength<bar, &foo::len> stuff;
};

然后我可以实现 DependentLength 使其行为类似于指向 bar 数组的指针,但在尝试查看大于 foo::len 的索引时会断言。但是,我无法实现 DependentLength<&foo::len>::operator[] 因为operator[]只接受一个参数,即索引,并且它需要知道 ' 的位置foo' 对象,以便取消引用成员指针模板参数并进行断言检查。

然而,我碰巧知道 DependentLength 只会在这里用作“foo”的成员。我真正想做的是告诉 DependentLength 在哪里可以找到相对于自身的 len,而不是相对于 foo 指针。所以类似 DependentLength<(char*)&foo::stuff - (char*)&foo::len>; stuff;,但这不是合法的 C++。是否存在一种好的或失败的邪恶语言黑客可以使这项工作成功?

Say I have a struct with a bunch of members:

struct foo {
    int len;
    bar *stuff;
};

As it so happens stuff will point to an array of bars that is len long. I'd like to encode this in stuff's type. So something like:

struct foo {
    int len;
    DependentLength<bar, &foo::len> stuff;
};

Then I could implement DependentLength to behave like a pointer to a bar array but that asserts when trying to looking at an index bigger than foo::len. However, I can't implement DependentLength<&foo::len>::operator[] because operator[] only takes one parameter, the index, and it needs to know the location of the 'foo' object in order to dereference the member pointer template parameter and do the assert check.

However, I happen to know that DependentLength will only ever be used here as a member of 'foo'. What I'd really like to do is tell DependentLength where to find len relative to itself, rather than relative to a foo pointer. So something like DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;, but that's not legal C++. Is there a good or failing that evil language hack that could make this work?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

软的没边 2025-01-12 07:30:14

所以类似于 DependentLength<(char*)&foo::stuff - (char*)&foo::len>;东西;

您要求模板根据运行时传递给它们的动态属性执行计算...这不适用于模板,因为它们必须使用允许编译创建代码的值进行实例化在编译时由模板参数请求。因此,传递给模板的任何值都必须在编译时(而不是运行时)可解析。

您将必须使用动态容器类型。例如,std::vector 满足您的请求,其中如果超出底层容器的边界,std::vector::at() 函数将引发异常。不幸的是,它不如 static_assert 那么方便,但同样,在这种情况下使用 static_assert 是不可能的,因为您需要运行时检查边界。此外,std::vector 还包含了operator[]、迭代器、大小查询等的重载。

So something like DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;

You're asking templates to perform calculations based on dynamic properties passed to them during run-time ... that won't work for templates since they must be instantiated with values that allow the compile to create the code requested by the template parameters at compile time. Thus any values passed to a template must be resolvable at compile-time, and not run-time.

You're going to have to use a dynamic container type. For instance, std::vector meets your request where the std::vector::at() function will throw an exception if you exceed the bounds of the underlying container. It's unfortunately not as convenient as a static_assert, but again, using static_assert is impossible for this situation since you need run-time checking for the bounds. Additionally, std::vector also incorporates an overload for operator[], iterators, queries for it's size, etc.

无言温柔 2025-01-12 07:30:14

您可以告诉模板要用作长度的成员的偏移量。

template<typename T, typename LEN_T, ptrdiff_t LEN_OFFSET>
class DependentArray
{
public:
  T& operator[](LEN_T i_offset)
  {
    if (i_offset < 0) throw xxx;
    if (i_offset > this->size()) throw xxx;
    return this->m_pArr[i_offset];
  } // []
private:
  LEN_T& size()
  {
    return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + LEN_OFFSET);
  } // ()
private:
  T* m_pArr;
};

struct foo
{
  int len;
  DependentArray<bar, int, -sizeof(int)> stuff;
};

编辑2:

想到另一个解决方案。使用仅适用于 foo 的类来提供 size 字段的偏移量,并在定义 foo 后定义其方法,并且可以计算偏移量:

#define MEMBER_OFFSET(T,M) \
  (reinterpret_cast<char*>(&reinterpret_cast<T*>(0x10)->M) - \
  reinterpret_cast<char*>(reinterpret_cast<T*>(0x10)))

template<typename T, typename LEN_T, typename SIZE_OFFSET_SUPPLIER> 
class FooDependentArray
{
public:
  T& operator[](LEN_T i_offset)
  {
    if (i_offset < 0) throw xxx;
    if (i_offset > this->size()) throw xxx;
    return this->m_pArr[i_offset];
  } // []
private:
  LEN_T& size()
  {
    const ptrdiff_t len_offest = SIZE_OFFSET_SUPPLIER::getOffset();

    return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + len_offset);
  } // ()
private:
  T* m_pArr;
};

struct FooSizeOffsetSupplier
{
    static ptrdiff_t getOffset();
};

struct foo
{
  int len;
  DependentArray<bar, int, FooSizeOffsetSupplier> stuff;
};

ptrdiff_t FooSizeOffsetSupplier::getOffset()
{
  return MEMBER_OFFSET(Foo,m_len) - MEMBER_OFFSET(Foo,m_pArr);
} // ()

这使得可以在 foo 中添加和删除成员。

You can tell the template the offset of the member to use as length.

template<typename T, typename LEN_T, ptrdiff_t LEN_OFFSET>
class DependentArray
{
public:
  T& operator[](LEN_T i_offset)
  {
    if (i_offset < 0) throw xxx;
    if (i_offset > this->size()) throw xxx;
    return this->m_pArr[i_offset];
  } // []
private:
  LEN_T& size()
  {
    return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + LEN_OFFSET);
  } // ()
private:
  T* m_pArr;
};

struct foo
{
  int len;
  DependentArray<bar, int, -sizeof(int)> stuff;
};

Edit 2:

Thought of another solution. Use a class that is good for foo only to supply the offset of the size field and define its method after foo is defined and offsets can be calculated:

#define MEMBER_OFFSET(T,M) \
  (reinterpret_cast<char*>(&reinterpret_cast<T*>(0x10)->M) - \
  reinterpret_cast<char*>(reinterpret_cast<T*>(0x10)))

template<typename T, typename LEN_T, typename SIZE_OFFSET_SUPPLIER> 
class FooDependentArray
{
public:
  T& operator[](LEN_T i_offset)
  {
    if (i_offset < 0) throw xxx;
    if (i_offset > this->size()) throw xxx;
    return this->m_pArr[i_offset];
  } // []
private:
  LEN_T& size()
  {
    const ptrdiff_t len_offest = SIZE_OFFSET_SUPPLIER::getOffset();

    return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + len_offset);
  } // ()
private:
  T* m_pArr;
};

struct FooSizeOffsetSupplier
{
    static ptrdiff_t getOffset();
};

struct foo
{
  int len;
  DependentArray<bar, int, FooSizeOffsetSupplier> stuff;
};

ptrdiff_t FooSizeOffsetSupplier::getOffset()
{
  return MEMBER_OFFSET(Foo,m_len) - MEMBER_OFFSET(Foo,m_pArr);
} // ()

This makes it possible to add and remove members from foo.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文