I am trying to translate some Java code into C and I am having some trouble with the type system.
I have a interface/pure virtual class which represents a column in a table, and I want the table to be a vector of these generic columns:
template<typename T>
class IColumn {
public:
virtual const std::string& name() = 0;
virtual size_t size() = 0;
virtual T at(size_t idx) = 0;
virtual std::vector<T> data() = 0;
virtual ~IColumn() = default;
};
class StringColumn : public IColumn<std::string> {
public:
StringColumn(std::string name, std::vector<std::string> data)
: name_(std::move(name)), data_(std::move(data)) {}
const std::string& name() override { return name_; }
size_t size() override { return data_.size(); }
const std::vector<std::string>& data() override { return data_; }
std::string at(size_t idx) override { return data_[idx]; }
private:
std::string name_;
std::vector<std::string> data_;
};
class IntColumn : public IColumn<int> {
public:
IntColumn(std::string name, std::vector<int> data)
: name_(std::move(name)), data_(std::move(data)) {}
const std::string& name() override { return name_; }
size_t size() override { return data_.size(); }
const std::vector<int>& data() override { return data_; }
int at(size_t idx) override { return data_[idx]; }
private:
std::string name_;
std::vector<int> data_;
};
However, I am having issues when declaring the vector in the Table
class:
class Table {
std::vector<std::unique_ptr<IColumn>> columns;
};
I understand that IColumn
is a template class, hence it complains about the missing template parameter, however I am not sure how to get around this issue, because the virtual functions T at()
and std::vector<T> data()
rely on T.
How could I fix this issue or could someone suggest an alternative design for this API?
EDIT: Using the std::variant<IntColumn, StringColumn>
with std::visit
as suggested:
for (auto& col: columns_) {
std::cout << std::visit([](auto&& arg) { return arg.name(); }, col) << '\t';
}
std::cout << '\n';
for (int i = 0; i < this->rowCount(); i ) {
for (auto& col : columns_) {
auto v = std::visit([i](auto&& arg) { return arg.at(i); }, col);
std::cout << v;
}
}
The first std::visit
works, but the second one doesn't.
Thanks.
CodePudding user response:
IColumn
seems strange, you can indeed go with template:
template <typename T>
class Column {
public:
Column(std::string name, std::vector<T> data) :
m_name(std::move(name)),
m_data(std::move(data))
{}
const std::string& name() const { return m_name; }
std::size_t size() const { return m_data.size(); }
const T& at(size_t idx) const { return m_data.at(idx); }
std::vector<T>& data() { return m_data; }
private:
std::string m_name;
std::vector<T> m_data;
};
and std::variant
in Table
struct Table
{
std::vector<std::variant<Column<std::string>, Column<int>>> columns;
};
Usage might be similar to
Table table;
table.columns.push_back(Column<std::string>("string column", {"data1", "data2", "data3"}));
table.columns.push_back(Column<int>("int column", {4, 8, 15}));
const char* sep = "";
for (auto& col: table.columns) {
std::visit([&sep](const auto& arg) { std::cout << sep << arg.name(); }, col);
sep = "\t";
}
std::cout << "\n";
for (std::size_t i = 0; i < table.rowCount(); i ) {
const char* sep = "";
for (const auto& col : table.columns) {
std::visit([i, &sep](const auto& arg) { std::cout << sep << arg.at(i); }, col);
sep = "\t";
}
std::cout << std::endl;
}
CodePudding user response:
Maybe my approach is suitable for your needs.
Here we add item_size()
method and return void*
instead of T
class IColumn {
public:
virtual const std::string& name() = 0;
virtual size_t size() = 0;
virtual size_t item_size() = 0;
virtual void* at(size_t idx) = 0;
virtual void* data() = 0;
virtual ~IColumn() = default;
};
And then implement column with custom types
template <class T>
class TColumn : public IColumn {
public:
TColumn(std::string name, std::vector<T> data) : name_(std::move(name)), data_(std::move(data)) {}
const std::string& name() override { /* */}
virtual size_t size() override { /* */ }
size_t item_size() override {
return sizeof(T);
}
void* at(size_t idx) override {
return &data_.at(idx);
}
void* data() override {
return data_.data();
}
private:
std::string name_;
std::vector<T> data_;
};
And then you can do something like
Table t;
std::vector<int> column = { 1, 2, 3 };
t.columns.emplace_back(std::make_unique<TColumn<int>>("name", column));
int item;
void* res = t.columns[0]->at(0);
std::memcpy(&item, res, sizeof(item));
std::cout << item << '\n';