/*
* CSV file parser
* by John Ryland
* (C) Copyright 2019
*/
#pragma once
#ifndef CSV_PARSER_H
#define CSV_PARSER_H
#include <string>
#include <vector>
#include <map>
// Declare the visitor pattern
template <typename T, typename V>
void visit(T& obj, V&& visitor);
// Declare string_cast
template <typename T>
T string_cast(const std::string& aString);
template <typename T>
class Accessor
{
public:
Accessor(T& aObject)
{
mRead = [aObject]() -> T { return aObject; };
mWrite = [&aObject](T aVal) { aObject = aVal; };
}
template <typename Object>
Accessor(Object& aObject, T (Object::*aGetter)() const, void (Object::*aSetter)(T))
{
mRead = [aObject, aGetter]() -> T { return (aObject.*aGetter)(); };
mWrite = [&aObject, aSetter](T aVal) { (aObject.*aSetter)(aVal); };
}
T get()
{
return mRead();
}
void set(T val)
{
mWrite(val);
}
private:
std::function<T()> mRead;
std::function<void(T)> mWrite;
};
template<typename T>
Accessor<T> access(T& object)
{
return Accessor<T>(object);
}
template <typename T, typename Object>
Accessor<T> access(Object& aObject, T (Object::*aGetter)() const, void (Object::*aSetter)(T))
{
return Accessor<T>(aObject, aGetter, aSetter);
}
class CSVParser
{
public:
CSVParser() = default;
template <typename T>
std::vector<T> ReadData(std::string aFileName)
{
Reset();
Read(aFileName);
size_t rows = GetRows();
std::vector<T> ret;
T val;
for (int i = 0; i < rows; ++i)
{
GetValue(i, val);
ret.emplace_back(val);
}
Reset();
return ret;
}
private:
void SetExpectedFieldNames(std::vector<std::string> aNames)
{
mExpectedFields = aNames;
}
bool Read(std::string aFileName);
size_t GetRows() const { return mData.size(); }
std::string GetData(int row, std::string aFieldName) const { return mData.at(row)[mFieldNameMap.at(aFieldName)]; }
// col is based on the order of expected field names
//bool GetValue(int row, int col, bool& aValue) const;
//bool GetValue(int row, int col, int& aValue) const;
//bool GetValue(int row, int col, double& aValue) const;
//bool GetValue(int row, int col, std::string& aValue) const;
template <typename T>
bool GetValue(int row, T& aValue) const
{
ReadVisitor visitor(*this, row);
visit(aValue, visitor);
return true;
}
class ReadVisitor
{
public:
ReadVisitor(const CSVParser& aParser, int aRow)
: mParser(aParser)
, mRow(aRow)
, mColumn(0)
{
}
template <typename T>
void operator()(const char* aFieldName, Accessor<T> aValue)
{
// const std::string& fieldName = mParser.mExpectedFields.at(mColumn);
const std::string& valueStr = mParser.GetData(mRow, aFieldName);
aValue.set(string_cast<T>(valueStr));
mColumn++;
}
/*
template <typename T>
void operator()(const char* aFieldName, T& aValue)
{
// const std::string& fieldName = mParser.mExpectedFields.at(mColumn);
const std::string& valueStr = mParser.GetData(mRow, aFieldName);
aValue = string_cast<T>(valueStr);
mColumn++;
}
template <typename T, typename G, typename S>
void operator()(const char* aFieldName, T& aObject, G (T::*aGetter)() const, void (T::*aSetter)(S))
{
// const std::string& fieldName = mParser.mExpectedFields.at(mColumn);
const std::string& valueStr = mParser.GetData(mRow, aFieldName);
(aObject.*aSetter)(string_cast<S>(valueStr));
mColumn++;
}
*/
private:
const CSVParser& mParser;
int mRow;
int mColumn;
};
void Reset()
{
mExpectedFields.clear();
mFieldNames.clear();
mData.clear();
mFieldNameMap.clear();
}
using Row = std::vector<std::string>;
Row mExpectedFields;
Row mFieldNames;
std::vector<Row> mData;
std::map<std::string,int> mFieldNameMap;
};
#endif // CSV_PARSER_H