Reputation: 22438
This method attempts to select a (std::vector<?>
) based on a key (std::string
), where ?
is either int
or float
:
template<typename L>
inline void EnsembleClustering::Graph::forNodesWithAttribute(std::string attrKey, L handle) {
// get nodemap for attrKey
auto nodeMap; // ?
auto findIdPair = this->attrKey2IdPair.find(attrKey);
if (findIdPair != this->attrKey2IdPair.end()) {
std::pair<index, index> idPair = findIdPair->second;
index typeId = idPair.first;
index mapId = idPair.second;
// nodemaps are in a vector, one for each node attribute type int, float, NodeAttribute
switch (typeId) {
case 0:
nodeMap = this->nodeMapsInt[mapId];
break;
case 1:
nodeMap = this->nodeMapsFloat[mapId];
break;
}
// iterate over nodes and call handler with attribute
this->forNodes([&](node u) {
auto attr = nodeMap[u];
handle(u, attr);
});
} else {
throw std::runtime_error("node attribute not found");
}
}
The relevant member of the class are:
std::map<std::string, std::pair<index, index>> attrKey2IdPair; // attribute key -> (attribute type index, attribute map index)
// storage
std::vector<std::vector<int> > nodeMapsInt; // has type id 0
std::vector<std::vector<float> > nodeMapsFloat; // has type id 1
This will not compile because auto nodeMap
(= std::vector<?>
) is not initialized. But in order to initialize it, I would have to know its type at compile time.
Maybe what I am attempting cannot be done with static typing. Is there a C++ way to accomplish this?
Upvotes: 0
Views: 182
Reputation: 66
If you have limited number of variants (i.e only vector of float and vector of int), you can use boost::variant
to store it.
Define type of variant, and define visitor structure:
#include "boost/variant.hpp"
//Define type
typedef boost::variant<std::vector<int>, std::vector<float>> VectorType;
struct VectorTypeVisitor : public boost::static_visitor<void>
{
node& m_u;
VectorTypeVisitor(node& u) : m_u(u) { } //Pass node to visitor in constructor
void operator()(const std::vector<int>& nodeMap) const
{
auto attr = nodeMap[m_u];
handle(m_u, attr);
}
void operator()(const std::vector<float>& nodeMap) const
{
auto attr = nodeMap[m_u];
handle(m_u, attr); //What to do if visitor applied to float
}
}
Your code then might look like that:
template<typename L>
inline void EnsembleClustering::Graph::forNodesWithAttribute(std::string attrKey, L handle) {
// get nodemap for attrKey
VectorType nodeMap;
auto findIdPair = this->attrKey2IdPair.find(attrKey);
if (findIdPair != this->attrKey2IdPair.end()) {
std::pair<index, index> idPair = findIdPair->second;
index typeId = idPair.first;
index mapId = idPair.second;
// nodemaps are in a vector, one for each node attribute type int, float, NodeAttribute
switch (typeId) {
case 0:
nodeMap = this->nodeMapsInt[mapId];
break;
case 1:
nodeMap = this->nodeMapsFloat[mapId];
break;
}
// iterate over nodes and call handler with attribute
this->forNodes([&](node u) {
boost::apply_visitor(VectorTypeVisitor(u), nodeMap);
});
} else {
throw std::runtime_error("node attribute not found");
}
}
However, it is still not good to pass variable like typeId to identify variable type.
Upvotes: 2
Reputation: 153909
The fact that these are templates has nothing to do with it.
std::vector<std::vector<int> >
and
std::vector<std::vector<float> >
are two totally unrelated
classes, and behave as such. If you really need something like
this, you'll have to define an abstract base class and two
derived classes, each of which wraps the corresponding
std::vector
. But I don't see how you'll be able to use it, or
even define an appropriate abstract base class, because the type
contained in the vector permeates the interface. The types you
use in almost every call will also have to be different.
Upvotes: 2