Рассмотрим следующий код:
struct I
{
SomeInternalState m_internalState;
};
struct S
{
I * m_i;
set_I (I * i)
{
m_i = i;
makeSomeChangesToTheInternalStateOfI(m_i);
}
};
struct S_1 : S { ... };
struct S_2 : S { ... };
...
struct S_n : S { ... };
Дано, что может быть создано произвольное количество экземпляров S_1
, ... S_n
, и все они вызовут set_I()
только один раз.
Теперь я хочу, чтобы экземпляры от S_1
, ... S_n
до makeSomeChangesToTheInternalStateOfI()
только один раз для экземпляра I
для типа S_x
, чтобы я мог вызывать set_I()
из разных экземпляров одного и того же класса S_x
с одинаковым экземпляром I
и быть уверенным, что внутреннее состояние I
будет изменено только во время первого вызова.
Вероятное решение - поместить некоторую таблицу диспетчеризации в I
, но я не могу придумать разумного ключа для нее, основанной исключительно на типе S_x
экземпляра и не связанной с каким-либо рукописным "типом среды выполненияid "константы для всех возможных типов S_1
, ... S_n
.
Как мне это сделать?
РЕДАКТИРОВАТЬ:
Точки, которые я должен был подчеркнуть:
1) Может быть более одного экземпляра I
одновременно, и классы S_x
Вы сможете изменить состояние нескольких экземпляров I
, но только один раз для каждого экземпляра.То есть:
I i1, i2;
S_1 s1a, s1b;
S_2 s2a, s2b;
// all possible combinations:
s1a.changeStateOfI(i1);
s1b.changeStateOfI(i1);
s2a.changeStateOfI(i1);
s2b.changeStateOfI(i1);
s1a.changeStateOfI(i2);
s1b.changeStateOfI(i2);
s2a.changeStateOfI(i2);
s2b.changeStateOfI(i2);
В этом фрагменте состояния как i1
, так и i2
должны изменяться только один раз методом S_1
(через s1a
) и один раз S_2
(через s2a
).
2) Полагаю, для решения проблемы можно использовать подсчет ссылок - нет необходимости точно знать, сколько раз происходила инициализация, достаточно знать, была ли она или нет.
EDIT2
Я отметил нм в качестве ответа, хотя мое окончательное решение немного отличается.Вот так, чтобы другие тоже могли его использовать:
struct AbstractS
{
I * m_i;
virtual void set_I (I * i) = 0;
};
template <typename self_T>
struct RegS : AbstractS
{
static std::set<I *> s_registeredContexts;
virtual void set_I (I * i)
{
m_i = i;
if (i == NULL || s_registeredContexts.count(i) > 0) return;
makeSomeChangesToTheInternalStateOfI(i);
contexts.insert(i);
}
};
template <typename self_T>
std::set<I *> InterpreterState<self_T>::s_registeredContexts;
struct S_1 : RegS<S_1> { ... };
struct S_2 : RegS<S_2> { ... };
...
struct S_n : RegS<S_n> { ... };
Разница по сравнению с вариантом nm заключается в том, что я использовал здесь шаблон CRTP вместо перечисления экземпляровто, чего я тоже хотел избежать.