Выделение поля в произвольных типах классов для «внешнего использования» - PullRequest
2 голосов
/ 08 августа 2010

Мой контейнер должен хранить немного информации о его элементах.Обычно я храню это отдельно от элементов.Тем не менее, я хотел бы дать пользователям возможность сэкономить память, выделив поле в типе структуры элемента для внешнего использования.Например:

struct MyStuff
{
  int           foo;
  char          bar;
  mutable char  dedicated_for_external_use;  // Because of alignment, this field
                                             // won't increase sizeof (MyStuff)
};

Идея в том, что поле не должно быть доступно для чего-либо, кроме контейнера элемента.Поскольку контейнеры хранят копию (очень похоже на std::vector), это не будет проблемой, если вы добавите какое-либо заданное значение x в несколько контейнеров.

Как бы вы разработали интерфейс для этого, если, есливозможно, будет соответствовать следующим требованиям?

  • Должно быть полностью необязательным.Т.е. должна быть возможность автоматически определить, предоставляет ли данный тип такое поле или нет, и тогда контейнер будет использовать его только при наличии.
  • В идеале, не зависит от характеристик типа и т. Д.так как мне нужна максимальная совместимость компилятора.
  • Должно быть простым в использовании.Т.е. если вы можете и хотите включить эту оптимизацию для типа MyStuff, вы можете сделать это с 3 строками кода, а не с 25. Внутренние сложности, с другой стороны, не имеют значения.
  • Желательно, чтобыполностью исключить ложные срабатывания.Я имею в виду: если вы проверяете поле foo_bar, существует небольшая вероятность того, что такое поле существует по совершенно не связанной причине (и я думаю, что типизирование утки просто не для C ++).Лучше было бы проверить, наследует ли тип класс маркера ProvidesExternalUseField из моей библиотеки, поскольку это не может быть случайно.

EDIT

Я знаю о Boost.Intrusive, но я хочу что-то другое.Если я пойду этим путем и создам класс ловушек с одним полем char, он не может быть использован для сохранения памяти во многих случаях.Если унаследованный тип имеет int в качестве первого поля, поле char будет дополнено до 4 байтов.Т.е. вам часто требуется сложное знание внутренних типов, чтобы иметь возможность «сжать» такое поле внешнего использования, но наследование на самом деле этого не обеспечивает:

struct hooks { mutable char dedicated_for_external_use; };
struct MyStuff : hooks
{
  int           foo;
  char          bar;
};

Здесь, размер MyStuffбудет 12 байтов, а не 8.

1 Ответ

0 голосов
/ 26 января 2011

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

Допустим, ваш класс интерфейса маркера выглядит следующим образом:

class ProvidesExternalUseField
{
public:
    char GetExtraField () { return 0; }
    void SetExtraField (char newVal) {}
};

Это не виртуальнодля цели: мы не хотели бы добавлять указатель vtable в класс данных только для этого.

Теперь давайте реализуем простой контейнерный класс:

template <class T>
class Container
{
public:
    char GetExtraValue ()
    {
        return 0; // here we cannot know if T is derived from the marker
    }
private:
    T m_t;
};

И вот как мыизмените его, чтобы различать 2 случая:

template <class T, bool DoesTProvideExternalUseField>
class ContainerImpl
{
public:
    char GetExtraValue () { return 0; }

private:
    T m_t;
};

template <class T>
class ContainerImpl<T, true>
{
public:
    char GetExtraValue () { return m_t.GetExtraField(); } 
private:
    T m_t;
};

template <class T>
class Container: public ContainerImpl<T,
                                      boost::is_base_of<ProvidesExternalUseField,T>::value>
{
};

Теперь вы можете определить структуры следующим образом:

struct A
{
    int m_intVal;
};

struct B: public ProvidesExternalUseField
{
    char GetExtraField () { return m_extraField; }
    void SetExtraField (char newVal) { m_extraField = newVal; }

    int m_intVal;
    char m_charVal;
    char m_extraField;
};

И точно так же использовать класс контейнера:

Container<A> a;
Container<B> b;

Вы также можете дополнительно автоматизировать (шаблонизировать) геттеры и сеттеры в интерфейсе маркера, используя в качестве параметра шаблона указатель на член.

...