Как выразить ограничения между членами с помощью шаблонов? - PullRequest
2 голосов
/ 13 февраля 2012

Скажем, у меня есть структура с кучей членов:

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

Как это случается, stuff будет указывать на массив bar с длиной len.Я хотел бы закодировать это в типе stuff.Так что-то вроде:

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

Тогда я мог бы реализовать DependentLength, чтобы вести себя как указатель на массив столбцов, но это подтверждается при попытке взглянуть на индекс больше foo::len.Однако я не могу реализовать DependentLength<&foo::len>::operator[], потому что operator [] принимает только один параметр - индекс, и ему нужно знать местоположение объекта 'foo', чтобы разыменовать параметр шаблона указателя на член и выполнить проверку assert.

Однако я знаю, что DependentLength будет когда-либо использоваться здесь только как член 'foo'.Что я действительно хотел бы сделать, так это сказать DependentLength, где найти len относительно себя , а не относительно указателя foo.Так что-то вроде DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;, но это не разрешено C ++.Есть что-то хорошее или неудачное, что может заставить эту работу работать?

Ответы [ 2 ]

3 голосов
/ 13 февраля 2012

Так что-то вроде DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;

Вы просите шаблоны выполнять вычисления на основе динамических свойств, передаваемых им во время выполнения ... это не будет работать для шаблоновпоскольку они должны быть созданы со значениями, которые позволяют компиляции создавать код, запрошенный параметрами шаблона в compile time.Таким образом, любые значения, передаваемые в шаблон, должны разрешаться во время компиляции, а не во время выполнения.

Вам нужно будет использовать динамический тип контейнера.Например, std::vector соответствует вашему запросу, где функция std::vector::at() будет выдавать исключение, если вы выходите за пределы нижележащего контейнера.К сожалению, это не так удобно, как static_assert, но, опять же, использование static_assert невозможно в этой ситуации, так как вам нужна проверка границ во время выполнения.Кроме того, std::vector также включает перегрузку для operator[], итераторы, запросы на его размер и т. Д.

2 голосов
/ 13 февраля 2012

Вы можете указать шаблону смещение элемента для использования в качестве длины.

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, чтобы указать смещение поля размера и определить его метод после определения 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.

...