Выделение поля в произвольных типах классов для "внешнего использования"

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

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 из моей библиотеки, так как это не может быть случайно.

РЕДАКТИРОВАТЬ

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

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

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

1 ответ

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

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

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;

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

Другие вопросы по тегам