/*
* 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