Newer
Older
Import / research / reflection / source / csv_parser.cpp
/*
 * CSV file parser
 * by John Ryland
 * (C) Copyright 2019
 */
#include "csv_parser.hpp"
#include <fstream>
#include <sstream>
#include <iostream>


// https://stackoverflow.com/questions/236129/how-do-i-iterate-over-the-words-of-a-string
std::vector<std::string> tokenize(const std::string& str,
                                  const std::string& delimiters = " ",
                                  bool               trimEmpty = false)
{
  std::vector<std::string> tokens;
  std::size_t start = 0, end, length = str.length();
  while (length && start < length + 1)
  {
    end = str.find_first_of(delimiters, start);
    if (end == std::string::npos)
    {
      end = length;
    }
    if (end != start || !trimEmpty)
      tokens.push_back(std::string(str.data() + start, end - start));
    start = end + 1;
  }
  return tokens;
}


bool CSVParser::Read(std::string aFileName)
{
  std::ifstream input(aFileName.c_str());
  if (!input.is_open())
  {
    printf("couldn't open input file %s\n", aFileName.c_str());
    return false;
  }

  std::string line;
  std::getline(input, line);
  mFieldNames = tokenize(line, ",", false);
  for (int i = 0; i < mFieldNames.size(); ++i)
  {
    mFieldNameMap[mFieldNames[i]] = i;
  }
  while (!input.eof())
  {
    std::getline(input, line);
    if (!line.empty())
    {
      mData.push_back(tokenize(line, ",", false));
    }
  }

  return true;
}


template <>
bool string_cast(const std::string& aString)
{
  return (aString == "true" || aString == "True" || aString == "TRUE");
}


template <>
int string_cast(const std::string& aString)
{
  return std::strtol(aString.c_str(), nullptr, 10);
}


template <>
double string_cast(const std::string& aString)
{
  return std::strtod(aString.c_str(), nullptr);
}


#ifdef TEST


struct DataId
{
  int id;
};


template <typename V>
void visit(DataId& obj, V&& visitor)
{
  visitor("id", access(obj.id));
}


// Test
struct PlainOldData
{
  // public members
  DataId          id;
  int             x, y, z;

  // Accessors
  int             getW() const   { return w; }

  // Mutators
  void            setW(int newW) { w = newW; }

private:
  int             w;
};


// This can be hidden in a c++ file
// Visit each member
template <typename V>
void visit(PlainOldData& obj, V&& visitor)
{
  visit(obj.id, visitor);
  visitor("x", access(obj.x));
  visitor("y", access(obj.y));
  visitor("z", access(obj.z));
  visitor("w", access(obj, &PlainOldData::getW, &PlainOldData::setW));
}


/*
int main(int argc, char* argv[])
{
  CSVParser parser;
  parser.SetExpectedFieldNames({
      "id",
      "x",
      "y",
      "z"
  });
  parser.Read("input_test.csv");

  PlainOldData data;
  parser.GetValue(0, data);

  printf("[0] = { %i, %i, %i };\n", data.x, data.y, data.z);

  return 0;
}
*/


class CollectFieldNamesVisitor
{
public:
  template <typename T>
  const std::vector<std::string>& GetFieldNames()
  {
    T dummy;
    visit(dummy, *this);
    return mFieldNames;
  }
  template <typename T>
  void operator()(const char* aFieldName, Accessor<T> aValue)
  {
    mFieldNames.push_back(aFieldName);
  }
  /*
  template <typename T>
  void operator()(const char* aFieldName, T& aValue)
  {
    mFieldNames.push_back(aFieldName);
  }
  template <typename T, typename G, typename S>
  void operator()(const char* aFieldName, T& aObject, G&& aGetter, S&& aSetter)
  {
    mFieldNames.push_back(aFieldName);
  }
  */
private:
  std::vector<std::string> mFieldNames;
};


class CollectFieldsVisitor
{
public:
  template <typename T>
  const std::vector<std::string>& GetFields(T& aValue)
  {
    mFields.clear();
    visit(aValue, *this);
    return mFields;
  }
  template <typename T>
  void operator()(const char* aFieldName, Accessor<T> aValue)
  {
    std::stringstream ss;
    //T tmp = aValue.get();
    //ss << tmp;//aValue.get();
    ss << aValue.get();
    mFields.emplace_back(ss.str());
  }
  /*
  template <typename T>
  void operator()(const char* aFieldName, T& aValue)
  {
    std::stringstream ss;
    ss << aValue;
    mFields.emplace_back(ss.str());
  }
  template <typename T, typename G, typename S>
  void operator()(const char* aFieldName, T& aObject, G&& aGetter, S&& aSetter)
  {
    std::stringstream ss;
    ss << (aObject.*aGetter)();
    mFields.emplace_back(ss.str());
  }
  */
private:
  std::vector<std::string> mFields;
};


void PrintAsCSV(const std::vector<std::string>& aList)
{
  for (int i = 0; i < aList.size(); ++i)
    printf("%s%s", aList[i].c_str(), ((i+1)==aList.size()) ? "" : ",");
  printf("\n");
}


// This example is to stdout
template <typename T>
void CSVWrite(const std::vector<T>& aData)
{
  CollectFieldNamesVisitor fields;
  PrintAsCSV(fields.GetFieldNames<T>());

  CollectFieldsVisitor visitor;
  for (int i = 0; i < aData.size(); ++i)
  {
    PrintAsCSV(visitor.GetFields(const_cast<T&>(aData[i])));
  }
}


int main(int argc, char* argv[])
{
  CSVParser parser;
  auto data = parser.ReadData<PlainOldData>("input_test.csv");
  CSVWrite(data);
  return 0;
}


#endif // TEST