Hi Greg, thanks for your response.
How many methods and static data members of the my_class class template
have to be instantiated in order to qualify as a my_class
"specialiazation"? Remember that (unless explicitly instantiated) the
methods and static data members of a template class are instantiated on
an individual basis. Each one being instantiated only when needed.
Ah yes, good point. Well, the run_tests() member function of a
specialization is what matters. The user can choose to give
ctor/dtor/extras to facilitate the tests, but they're not required to
do so if they're not needed.
First it is neccessary to decide exactly what to count - before
concluding that it is not possible to count whatever is to be counted.
And given that there can only be a finite number of instantiations of a
class template - it should be possible to count each one.
Indeed. But it's not instantiations I want to count. It's the number of
specialization definitions I'm after, or rather all the types for which
a particular template class is specialized in code. I'm not sure that
I've comminucated this very well up until now.
As an example, assume that the program wishes to count number of
unique, parameterized types used to construct a UnitTest class
template. One possible solution would look like the following:
struct Fixture
{
template <class T>
Fixture(const T* t)
{
static int didRegisterType;
if (didRegisterType == false)
{
++sNumFixtureTypes;
RegisterFixture( typeid(T)); }
}
}
static int sNumFixtureTypes;
...
};
int Fixture:: sNumFixtureTypes = 0;
template <class T>
struct UnitTest : public Fixture
{
UnitTest() : Fixture(static_cast<T*>(NULL)) {}
};
Here Fixture's static data member sNumFixtureTypes counts the number of
unique types used to construct UnitTest objects (which may be equal to
- or less than - the number of UnitTest objects constructed).
Ok. But again, I don't know if you realise that this isn't really what
I'm aiming for. I'll be more specific about this in a bit...
Having clients specialize an entire template class for each and every
type is likely to prove as error-prone as it is tedious. Worse, there
is no constraint on the client's specialization - the resulting class
may not even be usuable as a unit test.
I'm not sure I agree with this. All the work the user has to do to
construct a basic fixture would be the following, in an ideal world.
template<> struct fixture<vec3D>
{
void run_tests()
{
vec3D u, v; // or as members, initialized in the ctor
ensure("default equality", u == v);
ensure_not("default inequality", u != v);
}
};
If by virtue of the above definition existing in code, I could have the
register_fixture<vec3D>() function called, then the specialization
would be registered with the system. Something like:
struct fixture_runner_base
{
virtual ~fixture_runner_base() { }
virtual void run_tests() = 0;
};
template<typename test_t>
struct fixture_runner : fixture_runner_base
{
void run_tests()
{
// exception catching machinery etc omitted
fixture<test_t> f;
f.run_tests();
}
};
template<typename test_t>
void register_fixture()
{
shared_ptr p(new fixture_runner<test_t>);
tell_system_about_fixture(typeid(test_t), p);
}
Now all the fixture-types would have been registered automatically
(again, in an ideal world) and I can provide iterators to let a tester
select which tests to use at run time (perhaps through a GUI or
whatever). No fixture has been instantiated yet, and none will until
the tester calls the fixture_runner_base::run_tests() through their
custom-vetted iterators.
A much better idea would be to have clients specialize a single method
of the UnitTest class template - instead of the entire class template.
For example, clients could specialize just the RunTests() method for a
particular type. In fact, declaring RunTests an abstract method in the
Fixture base would force any client instantiating a UnitTest for a type
to specialize the RunTests method:
struct Fixture
{
template <class T>
Fixture(const T* t) { RegisterFixture( typeid(T)); }
virtual void RunTests() = 0;
};
int Fixture::sFixtureCount = 0;
template <class T>
struct UnitTest : public Fixture
{
UnitTest() : Fixture(static_cast<T*>(NULL)) {}
void RunTests();
...
}
Now the client can explicitly instantiate a UnitTest for a particular
type;
class Gadget {};
// instantiate entire UnitTest template class for Gadget
template class UnitTest<Gadget>;
which in turn forces the client to specialize its RunTests() method:
void UnitTest<Gadget>::RunTests()
{
// implement Gadget tests
}
I can see that would work fine. Indeed it's an alternative that I've
considered, but I feel that the above is actually more error prone that
what I have currently; the user must remember to derive from Fixture
and call it's constructor with the somewhat obscure cast as it's
argument.
If they forget to over-ride RunTests() they'll get a compiler error
something like "can't instantiate MyUnitTest because RunTests() is
abstract", as opposed to "No such member function
fixture<vec3D>::run_tests()". I don't think either is better than the
other -- they're both enough of a clue for the user to guess what's
gone wrong.
Regardless of which is 'best', I still don't have the registration
function called until an instantiation is made, rather than just by
virtue of some kind of definition in code.
I hope that I've understood what you're saying and that I've clarified
my aims if they were unclear before.
Edd