Consider the following code:
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 { ... };
It is given that an arbitrary count of instances of S_1
, ... S_n
may be created, and all of them will call set_I()
only once.
Now, I want the instances of S_1
, ... S_n
to makeSomeChangesToTheInternalStateOfI()
only once per instance of I
per type of S_x
, so that I could call set_I()
from different instances of the same class S_x
with the same instance of I
and be sure that the internal state of I
will be modified only during the first call.
The likely decision is to put some dispatch table into I
, but I can't think of a sensible key for it, based solely on the type of S_x
instance and not involving any hand-written "runtime type id" constants for all possible types S_1
, ... S_n
.
How do I do it?
EDIT:
The points that I should have stressed:
1) There may be more than one instance of I
at a time, and the S_x
-classes should be able to change the state of multiple instances of I
, but only once per each instance. That is:
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);
In this fragment, the states of both i1
and i2
should only be changed once by S_1
's method (via s1a
) and once by S_2
's (via s2a
).
2) I suppose, reference-counting could be used to solve the problem - there's no need to know exactly, how many times the initialisation occurred, it's enough to know if it did or not.
EDIT2
I've marked n.m.'s suggestion as the answer, though my final solution differs a bit. Here it is, so that others may use it too:
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> { ... };
The difference compared to n.m.'s variant is that I've used the CRTP pattern here inst开发者_Go百科ead of enumerating the instantiations, the thing I wanted to avoid too.
You can use typeinfo
as a key, but it's a bad idea. You should not count types in your program. Let me explain with a simple example.
Let's say you have a Vehicle
type and its descendants Car
, Truck
and Bike
. You call your function once per each of these classes. So far so good. Now you need, for a completely unrelated reason, to handle SUVs, RacingCars, GarbageTrucks, Trikes, RedCars, ReddishCars and YellowishReddishWithGreenishTintCars. Your decision on the number of times your function is going to be called should be completely orthogonal to your decision about introducing or not introducing separate classes for each of these cases.
So you need something to tag your Vehicles as distinct or similar, solely for the purpose of calling your function once per a bunch of similar objects. One way to achieve that is with a class template and a bunch of type parameters (any kind of type parameters).
class ChangerOfInternalStateOfI
{
public:
ChangerOfInternalStateOfI (I* i) {
makeSomeChangesToTheInternalStateOfI(i);
}
};
template <int n>
class S_N : public S
{
public:
S_N() {
static ChangerOfInternalStateOfI changer;
}
};
typedef S_N<1> S_1;
typedef S_N<2> S_2;
You can use enum instead of int, or a typename, doesn't really matter. The point is that all of your ChangerOfInternalStateOfI are distinct because they belong to distinct classes, and each of the constructors is going to be called once.
If the way with static data member n.m. mentioned doesn't meet the objective,
how about having a set containing types processed before in I
?
As type_info
itself isn't less-than-comparable, a simple wrapper
type_info_
is used in the following code.
If type-check has to be done polymorphically(through base class S
),
runtime type information is needed.
So I made changeStateOfI
be virtual
.
#include <typeinfo>
#include <set>
using namespace std;
struct type_info_ {
type_info const *t;
type_info_( type_info const* t ) : t( t ) {}
bool operator<( type_info_ const& x ) const { return t->before( *x.t ); }
};
struct I {
set< type_info_ > types;
void f( type_info const& t, char const* s ) {
if ( types.insert( type_info_( &t ) ).second ) { puts( s ); }
}
};
struct S {
virtual void changeStateOfI( I& i, char const* s ) {
i.f( typeid( *this ), s );
}
};
struct S_1 : S {};
struct S_2 : S {};
int main() {
I i1, i2;
S_1 s1a, s1b;
S_2 s2a, s2b;
s1a.changeStateOfI(i1, "s1a i1");
s1b.changeStateOfI(i1, "s1b i1");
s2a.changeStateOfI(i1, "s2a i1");
s2b.changeStateOfI(i1, "s2b i1");
s1a.changeStateOfI(i2, "s1a i2");
s1b.changeStateOfI(i2, "s1b i2");
s2a.changeStateOfI(i2, "s2a i2");
s2b.changeStateOfI(i2, "s2b i2");
}
The above code printed s1a i1
, s2a i1
, s1a i2
, s2a i2
in my environment.
精彩评论