pezpezpez
pezpezpez

Reputation: 694

Should I use Boost Variant for class with different type parameters

I'm new to C++ and I am not sure the best way to model a class to represent a table of columns; where a column is wrapper around a STL vector with a name (string) and should either be Column<int>, Column<float> or Column<std::string>.

At the moment I have hardcoded it to Column<int> but need to support Column<float> or Column<std::string>.

Should I go down the boost variant route (aka tagged union)?

boost::variant<Column<int>*, Column<float>*, Column<std::string>*>

Not sure if there's a better solution since it just the type parameter that is different.

I would be grateful to the C++ gods to share their wisdom.

template <typename T>
class Column
{
public:
    Column(std::string& name, std::vector<T>& vec) : name(name), vec(vec) {}
    T& at(const size_t i) { return vec[i]; }
private:
    std::string name;
    std::vector<T> vec;
};

class Table
{
public:
    Table(std::string& name) : name{name} {}

    void addColumn(Column<int>* vec) 
    {
        columns.push_back(vec);
    }

    Column<int>*& getColumn(const size_t i)
    {
        return columns[i];
    }
private:
    std::string name;
    std::vector<Column<int>*> columns;
};

Upvotes: 3

Views: 591

Answers (2)

sehe
sehe

Reputation: 393134

If a variant is appropriate for your use case: sure! The template instances are just types.

  1. You might be able to switch the design to something more like

    Column<boost::variant<int, float, std::string>>
    

    instead though

  2. You can generate a variant from a typelist:

    column_variant<int, float, std::string>
    

    In c++11 this is rather trivial:

    Live On Coliru

    template <typename... Ts>
    using make_column_variant = typename boost::make_variant_over<boost::mpl::vector<Column<Ts>...>>::type;
    

    Here's a c++03 version of that:

    Live On Coliru

    #include <boost/variant.hpp>
    #include <boost/mpl/vector.hpp>
    #include <iostream>
    
    template <typename T> struct Column { 
        T v; 
        Column(T const& v) : v(v) {}
        friend std::ostream& operator<<(std::ostream& os, Column<T> const& cv) {
            return os << cv.v;
        }
    };
    
    /* c++03 helper */
    namespace mpl = boost::mpl;
    
    template <typename Seq>
    struct make_column_variant
    {
        typedef typename mpl::transform<
            Seq, 
            Column<mpl::_1>,
            mpl::back_inserter<mpl::vector<> > 
        >::type columns;
    
        typedef typename boost::make_variant_over<columns>::type type;
    };
    
    int main() {
        make_column_variant<mpl::vector<int, float, std::string> >::type v(std::string("hello world"));
        std::cout << v;
    }
    

    This can be considerably shorter in C++11

Upvotes: 1

John Zwinck
John Zwinck

Reputation: 249293

Yes, using Boost.Variant is reasonable for this. However, you don't need to use pointers at all; instead:

boost::variant<Column<int>, Column<float>, Column<std::string>>

Upvotes: 1

Related Questions