Reputation: 1643
We are using a PubSub framework that uses template methods to register topics and identify as a publisher or subscriber. For example, to register a topic name in the system you'd call:
RegisterTopic<T>(std::string topicName);
I would like to be able to create a parameter list (or configuration list if you will) to identify the needed registration topics in use by a given application configuration using a simple struct like
struct RegistrationTopicType
{
std::string name;
int type; // replace with some mechanism to store a data type
};
However I haven't found a working mechanism to replace the int in the struct above with something to store a data type.
I'm trying to do this so that I can maintain a list of topics and their types in the system:
std::vector<RegistrationTopicType> topicList = {
{"topic1", MyClass},
{"topic2", MyOtherClass}, // and on and on.
};
which I could use later do things like call the templated Registration method.
for (auto & topic : topicList) {
RegisterTopic<topic.type>(topic.name);
}
I have experimented with templated struct where I tried to store the data type such as
template<typename T>
struct TemplatedRegistrationTopic
{
using DataType = T;
std::string name;
};
But while I can implement instances of this struct,
TemplatedRegistrationTopic <float> topic{"floatTopic"};
TemplatedRegistrationTopic <MyClass> topic{"MyClassTopic"};
I can't seem to access the "DataType" variable as a datatype to use in the Registration method.
RegisterTopic<topic.DataType>(topic.name); // this fails.
I saw somewhere to use '::' but that also fails:
Register<topic::DataType>(topic.name); // this also fails.
Problems of storing a bunch of topics of different types in a common list aside, is what I'm trying to do (store a data type for later use in a templated method) even possible?
In the ol' days I might have created an enum list with an enum for of the possible types and then used that to select the actual type in a massive switch statement, but I don't want to have to maintain a mapping between all of the possible types and an enum in a giant switch statement which seems to defeat the purpose of using templates. eg I don't want to end up with this, but its effectively what I'm trying to do:
enum typelist { Type1, Type2 }; // one for each type that might be used as a topic
struct EnumRegistrationTopicType
{
std::string name;
typelist type;
};
std::vector<EnumRegistrationTopicType> enumTopicList = {
{"topic1", Type1},
{"topic2", Type2}, // and on and on.
};
for (auto & topic : enumTopicList) {
switch (topic.type) {
case typelist::Type1:
RegisterTopic<MyClass1>(topic.name);
break;
case typelist::Type2:
RegisterTopic<MyClass2>(topic.name);
break;
}
}
Upvotes: 0
Views: 70
Reputation: 120021
Let's do a classic OO design.
struct RegistratorBase {
virtual ~RegistratorBase() = default;
virtual void doRegistration(const std::string& topic) const = 0;
};
template <typename T>
struct Registrator {
void registerTopic(const std::string& topic) const override {
doRegistration<MyClass1>(topic);
}
};
struct RegistrationTopicType {
std::string name;
std::unique_ptr<RegistratorBase> registrator;
}
Now you can add these things to a vector
std::vector<RegistrationTopicType> topicList {
{ "topic1", new Registrator<MyClass1> },
{ "topic2", new Registrator<MyClass2> },
};
and register everything
for (const auto& topic: topicList) {
topic.registrator->doRegistration(topic.name);
}
Of course now one starts to wonder, why is RegistrationTopicType needed at all? Why not shove the name directly to the Registrator? OK let's try:
struct RegistratorBase {
virtual ~RegistratorBase() = default;
// no need for other virtual members
};
template <typename T>
struct Registrator {
Registrator (const string& topic) {
RegisteerTopic<T>(topic);
}
};
Nice! Now let's put this into a vector:
std::vector<std::unique_ptr<RegistratorBase>> topicList {
new Registrator<MyClass1>("topic1"),
new Registrator<MyClass2>("topic2")
};
Great! But why do we need a vector of these things? We construct the vector of registrators and never use it anymore. Why not just create a bunch of variables instead?
Registrator<MyClass1> r1("topic1");
Registrator<MyClass2> r2("topic2");
But that's not really different from a list of function calls:
RegisterTopic<MyClass1>("topic1");
RegisterTopic<MyClass2>("topic2");
So variables are not needed either, nor are their classes. Put the calls somewhere and you are done. They are your configuration list.
We have successfully designed, simplified, and finally eliminated a totally redundant software component!
Perhaps some of the simplification steps will not be applicable to your case, in which case the component will not be redundant for you. But I have no way of knowing that.
Upvotes: 2