clstaudt
clstaudt

Reputation: 22438

Can this be done with static typing?

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

Answers (2)

Vladislav Khorev
Vladislav Khorev

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

James Kanze
James Kanze

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

Related Questions