Automatic registration of class specialisations

E

edd

Hello all,

Please consider:

template<typename T> class my_class;

template<> class my_class<int>
{
//...
};

template<typename T> void register_type()
{
extern std::set<const std::type_info *> available_types;
available_types.insert(&typeid(T));
}

I would like to know how many specialisations of my_class have been
defined in the program (and with which types). Ideally, to achieve this
I would like register_type<U>() to get called by virtue of the
existence of a my_class<U> specialisation, for all types U.

I've thought about it for a while now and I think that this is probably
impossible, but if anyone can think of a way I'd be eternally grateful!

To give some more context: I'm wanting to implement a small
unit-testing framework where each test-fixture is a specialisation of a
fixture<> template class e.g.

template<typename test_t, int test_id> class fixture;

enum test_ids
{
equality,
comparison,
serialisation,
//...
}

template<> class fixture<gadget, equality>
{
public:
fixture() { /* set up */ }
~fixture() { /* tear down */ }
void run_tests() { /* ... */ }
};

template<> class fixture<gadget, serialisation>
{
//...
};

This would allow me to index/iterate-over tests by class and test-id.
Given this final goal, someone may be able to spot another way to do
what I'm aming for...?

Regards,

Edd
 
M

mlimber

Please consider:

template<typename T> class my_class;

template<> class my_class<int>
{
//...
};

template<typename T> void register_type()
{
extern std::set<const std::type_info *> available_types;
available_types.insert(&typeid(T));
}

I would like to know how many specialisations of my_class have been
defined in the program (and with which types). Ideally, to achieve this
I would like register_type<U>() to get called by virtue of the
existence of a my_class<U> specialisation, for all types U.

I've thought about it for a while now and I think that this is probably
impossible, but if anyone can think of a way I'd be eternally grateful!

You're trying to inherit behavior without specifying the inheritance
relationship in the specializations. You can't do that automatically.
If you forced all specializations to inherit from a common base class
(perhaps using some template metaprogramming tricks to make sure the
creator of the specializations didn't forget to inherit from the base
class), you could do the registering in its constructor. Something
like:

struct test_base
{
typedef std::set<const std::type_info*> my_set;
static my_set available_types_;

test_base( const std::type_info* const ti )
{
const pair<my_set::iterator, bool> result
= available_types_.insert( ti );
assert( result.second );
}

virtual ~test_base() {}
};

template<class T> struct my_class;

template<> struct my_class<int> : test_base
{
my_class() : test_base( &typeid(*this) ) {}
// ...
};

Then, where you iterate over the types, you could do a compile-time
assertion on std::tr1::type_traits::is_base_of<test_base, T>::value
(which is the same as Boost's type_traits) or on Loki's SUPERSUBCLASS
(see http://sourceforge.net/projects/loki-lib/).

Not ideal, but it seems like an ok workaround.

Cheers! --M
 
E

edd

Hi there,
You're trying to inherit behavior without specifying the inheritance
relationship in the specializations. You can't do that automatically.
If you forced all specializations to inherit from a common base class
(perhaps using some template metaprogramming tricks to make sure the
creator of the specializations didn't forget to inherit from the base
class), you could do the registering in its constructor. Something
like:

struct test_base
{
typedef std::set<const std::type_info*> my_set;
static my_set available_types_;

test_base( const std::type_info* const ti )
{
const pair<my_set::iterator, bool> result
= available_types_.insert( ti );
assert( result.second );
}

virtual ~test_base() {}
};

template<class T> struct my_class;

template<> struct my_class<int> : test_base
{
my_class() : test_base( &typeid(*this) ) {}
// ...
};

I already have a couple of iterations comparable to what you suggest
here. I may have to settle for this but unfortunately it's not really
what I want. The above system would only become aware of each
specialisation when instances are created (manually by the tester),
where as what I'd really like is the system to be aware of the
specialisations merely by the existence of their definitions -- by the
T-specialisation definition triggering a call to register_fixture<T>(),
for example. That way only the fixtures that are requested/queued by
the tester may be created and run.

So doing it as above would mean calling a constructor for each
specialisation manually, instead of calling register_fixture<T>()
manually, for each T. I think the latter is still preferable since the
fixtures wouldn't have to be set-up to be registered.

Nevertheless, your comments seem to confirm that there probably is no
way to do exactly what I want, which in it's own way is helpful --
thanks.

Edd
 
G

Greg

Hello all,

Please consider:

template<typename T> class my_class;

template<> class my_class<int>
{
//...
};

template<typename T> void register_type()
{
extern std::set<const std::type_info *> available_types;
available_types.insert(&typeid(T));
}

I would like to know how many specialisations of my_class have been
defined in the program (and with which types). Ideally, to achieve this
I would like register_type<U>() to get called by virtue of the
existence of a my_class<U> specialisation, for all types U.

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.
I've thought about it for a while now and I think that this is probably
impossible, but if anyone can think of a way I'd be eternally grateful!

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.

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).
To give some more context: I'm wanting to implement a small
unit-testing framework where each test-fixture is a specialisation of a
fixture<> template class e.g.

template<typename test_t, int test_id> class fixture;

enum test_ids
{
equality,
comparison,
serialisation,
//...
}

template<> class fixture<gadget, equality>
{
public:
fixture() { /* set up */ }
~fixture() { /* tear down */ }
void run_tests() { /* ... */ }
};

template<> class fixture<gadget, serialisation>
{
//...
};

This would allow me to index/iterate-over tests by class and test-id.
Given this final goal, someone may be able to spot another way to do
what I'm aming for...?

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.

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

}

Greg
 
E

edd

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
 
E

edd

Gosh, I've just realised that I mis-understood your code. I'll give it
some more thought, now.
My apologies.

Edd
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top