Выделение поля в произвольных типах классов для "внешнего использования"
Мой контейнер должен хранить немного информации о его элементах. Обычно я храню это отдельно от элементов. Тем не менее, я хотел бы дать пользователям возможность сэкономить память, выделив поле в типе структуры элемента для внешнего использования. Например:
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 в качестве параметра шаблона.