Newer
Older
Import / research / reflection / source / csv_parser.hpp
/*
 * 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