Created
January 31, 2010 18:25
-
-
Save tcsavage/291177 to your computer and use it in GitHub Desktop.
This source code defines the following: - A generic data class for integer, floating point, string and collection types - A data collection class for generic data objects - Access operator for collections (period '.' separated paths) - Functions for split
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
project (dataAPI) | |
# Set Debug Mode | |
ADD_DEFINITIONS(-g) | |
find_package(Lua51 REQUIRED) | |
include_directories(${LUA_INCLUDE_DIR}) | |
set(LIBS ${LIBS} ${LUA_LIBRARIES}) | |
link_libraries (${LIBS}) | |
link_directories(${LUA_INCLUDE_DIR}) | |
set(DAPI_SRC | |
main.cpp | |
Utilities.cpp | |
GenericData.cpp | |
Exceptions.cpp | |
) | |
add_executable (dataAPI ${DAPI_SRC}) | |
target_link_libraries (dataAPI ${LIBS} dl) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Exceptions.h" | |
Exception::Exception(void) throw() | |
{ | |
} | |
Exception::Exception(std::string s) throw() | |
{ | |
message=s; | |
} | |
Exception::Exception(const char* s) throw() | |
{ | |
message=s; | |
} | |
Exception::~Exception(void) throw() | |
{ | |
} | |
const char* Exception::what(void) const throw() | |
{ | |
return message.c_str(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
class Exception : public std::exception | |
{ | |
public: | |
Exception(void) throw(); | |
Exception(std::string s) throw(); | |
Exception(const char* s) throw(); | |
virtual ~Exception(void) throw(); | |
const char* what(void) const throw(); | |
private: | |
std::string message; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "GenericData.h" | |
// Constructors... | |
GenericData::GenericData(void) | |
{ | |
data = NULL; | |
type = NONE; | |
parentcontainer = NULL; | |
} | |
GenericData::GenericData(int in) | |
{ | |
int* p_int = new int; | |
*p_int = in; | |
data = &(*p_int); | |
type = INTEGER; | |
parentcontainer = NULL; | |
} | |
GenericData::GenericData(float in) | |
{ | |
double* p_float = new double; | |
*p_float = in; | |
data = &(*p_float); | |
type = FLOAT; | |
parentcontainer = NULL; | |
} | |
GenericData::GenericData(double in) | |
{ | |
double* p_float = new double; | |
*p_float = in; | |
data = &(*p_float); | |
type = FLOAT; | |
parentcontainer = NULL; | |
} | |
GenericData::GenericData(bool in) | |
{ | |
bool* p_bool = new bool; | |
*p_bool = in; | |
data = &(*p_bool); | |
type = BOOLEAN; | |
parentcontainer = NULL; | |
} | |
GenericData::GenericData(std::string in) | |
{ | |
std::string* p_string = new std::string; | |
*p_string = in; | |
data = &(*p_string); | |
type = STRING; | |
parentcontainer = NULL; | |
} | |
GenericData::GenericData(const char* in) | |
{ | |
std::string* p_string = new std::string; | |
p_string->append(in); | |
data = &(*p_string); | |
type = STRING; | |
parentcontainer = NULL; | |
} | |
GenericData::GenericData(DataContainer* in) | |
{ | |
DataContainer* p_collection = in; | |
p_collection->parentdata = this; | |
data = &(*p_collection); | |
type = COLLECTION; | |
parentcontainer = NULL; | |
} | |
// Assignment opperators... | |
GenericData GenericData::operator=(int op2) | |
{ | |
if (type == INTEGER) { | |
*(int*)data = op2; | |
} else { | |
void* oldData = data; | |
int* p_int = new int; | |
*p_int = op2; | |
data = &(*p_int); | |
DeleteVoidPointer(oldData, type); | |
type = INTEGER; | |
} | |
return *this; | |
} | |
GenericData GenericData::operator=(float op2) | |
{ | |
if (type == FLOAT) { | |
*(double*)data = op2; | |
} else { | |
void* oldData = data; | |
double* p_float = new double; | |
*p_float = op2; | |
data = &(*p_float); | |
DeleteVoidPointer(oldData, type); | |
type = FLOAT; | |
} | |
return *this; | |
} | |
GenericData GenericData::operator=(double op2) | |
{ | |
if (type == FLOAT) { | |
*(double*)data = op2; | |
} else { | |
void* oldData = data; | |
double* p_float = new double; | |
*p_float = op2; | |
data = &(*p_float); | |
DeleteVoidPointer(oldData, type); | |
type = FLOAT; | |
} | |
return *this; | |
} | |
GenericData GenericData::operator=(bool op2) | |
{ | |
if (type == BOOLEAN) { | |
*(bool*)data = op2; | |
} else { | |
void* oldData = data; | |
bool* p_bool = new bool; | |
*p_bool = op2; | |
data = &(*p_bool); | |
DeleteVoidPointer(oldData, type); | |
type = BOOLEAN; | |
} | |
return *this; | |
} | |
GenericData GenericData::operator=(std::string op2) | |
{ | |
if (type == STRING) { | |
*(std::string*)data = op2; | |
} else { | |
void* oldData = data; | |
std::string* p_string = new std::string; | |
*p_string = op2; | |
data = &(*p_string); | |
DeleteVoidPointer(oldData, type); | |
type = STRING; | |
} | |
return *this; | |
} | |
GenericData GenericData::operator=(const char* op2) | |
{ | |
if (type == STRING) { | |
*(std::string*)data = op2; | |
} else { | |
void* oldData = data; | |
std::string* p_string = new std::string; | |
*p_string = op2; | |
data = &(*p_string); | |
DeleteVoidPointer(oldData, type); | |
type = STRING; | |
} | |
return *this; | |
} | |
GenericData GenericData::operator=(DataContainer* op2) | |
{ | |
if (type == COLLECTION) { | |
*(DataContainer*)data = *op2; | |
} else { | |
void* oldData = data; | |
DataContainer* p_collection = op2; | |
data = &(*p_collection); | |
DeleteVoidPointer(oldData, type); | |
type = COLLECTION; | |
} | |
((DataContainer*)data)->parentdata = this; | |
return *this; | |
} | |
void GenericData::DeleteVoidPointer(void* data, GenericDataTypes type) | |
{ | |
switch (type) { | |
case INTEGER: | |
delete (int*)data; | |
break; | |
case FLOAT: | |
delete (double*)data; | |
break; | |
case STRING: | |
delete (std::string*)data; | |
break; | |
case BOOLEAN: | |
delete (bool*)data; | |
break; | |
case COLLECTION: | |
delete (DataContainer*)data; | |
break; | |
default: | |
// Don't know what type of data this is. | |
throw(Exception("Unknown or no data type defined")); | |
break; | |
} | |
} | |
// Get path. | |
std::string GenericData::path(void) | |
{ | |
std::string path = this->key; | |
if (parentcontainer != NULL) { | |
// We are inside a container. | |
if (parentcontainer->parentdata != NULL) { | |
// We are not at root. | |
path.insert(0, "."); | |
path.insert(0, this->parentcontainer->parentdata->path()); | |
} else { | |
// We are at root. | |
} | |
} else { | |
// We are not inside a container. | |
} | |
return path; | |
} | |
// Collection access. | |
/**DataContainer* GenericData::operator->() | |
{ | |
if (type == COLLECTION) { | |
return (DataContainer*)data; | |
} else { | |
return NULL; | |
} | |
}**/ | |
// Output operator. | |
std::ostream& operator << (std::ostream& stream, GenericData ob) | |
{ | |
if (ob.type == INTEGER) | |
stream << *((int*)ob.data); | |
else if (ob.type == FLOAT) | |
stream << *(double*)ob.data; | |
else if (ob.type == BOOLEAN) | |
stream << *(bool*)ob.data; | |
else if (ob.type == STRING) | |
stream << ((std::string*)ob.data)->c_str(); | |
else if (ob.type == COLLECTION) | |
stream << "<COLLECTION>"; | |
else | |
stream << "<NONE>"; | |
return stream; | |
} | |
// Constructor. | |
DataContainer::DataContainer(void) | |
{ | |
parentdata = NULL; | |
} | |
// Insert method. | |
void DataContainer::insert(std::string key, GenericData* value) | |
{ | |
//std::cout << "INSERT REQUESTED FOR " << key.c_str() << "=" << *value << std::endl; | |
// Important not to use any invalid characters like !,/\=+"' etc. | |
size_t loc; | |
loc=key.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_."); | |
if (loc!=std::string::npos) | |
{ | |
// We found an illegal character in the key so throw an exception. | |
throw(Exception("Illegal character in data key")); | |
} else { | |
// Split string up into tokens using split(). | |
std::vector<std::string> tokens = split(key, '.'); | |
// Vector itterator. | |
std::vector<std::string>::iterator token; | |
// Map itterator. | |
std::map<std::string, GenericData*>::iterator found; | |
// Pointer to the collection we want to insert into. | |
DataContainer* collection = this; | |
// Pointer to the generic data. | |
GenericData* data = NULL; | |
// If we have multiple tokens. | |
if (tokens.size() > 1) { | |
//std::cout << "-->> We have multiple tokens in path" << std::endl; | |
// For each token in the path... | |
for (token=tokens.begin(); token < (tokens.end()-1); token++) { | |
//std::cout << "---->> For path token: " << (*token).c_str() << std::endl; | |
// Find token. | |
found = collection->dataset.find((*token).c_str()); | |
// Check we actually found something valid. | |
if (found == collection->dataset.end()) { | |
// Element doesn't exist. | |
throw(Exception("Element doesn't exist while inserting.")); | |
} | |
// Set data to what we found. | |
data = found->second; | |
//std::cout << "---->> Found data: " << *data << std::endl; | |
// Check we have a collection. | |
if (data->type != COLLECTION) { | |
throw(Exception("Non collection data found as part of insert path.")); | |
} else { | |
collection = ((DataContainer*)(data->data)); | |
} | |
} | |
// Insert the data. | |
collection->dataset.insert(std::pair<std::string, GenericData*>(*token,value)); | |
//std::cout << "---->> Inserted data '" << *value << "' into '" << collection->parentdata->key.c_str() << "' under key '" << (*token).c_str() << "'" << std::endl; | |
// Find data. | |
found = collection->dataset.find(*token); | |
// Check we actually found something valid. | |
if (found == dataset.end()) { | |
// Element doesn't exist. | |
throw(Exception("Element doesn't exist after inserting.")); | |
} | |
} else { | |
//std::cout << "-->> We DO NOT have multiple tokens in path" << std::endl; | |
// Insert the data. | |
dataset.insert(std::pair<std::string, GenericData*>(key,value)); | |
//std::cout << "---->> Inserted data" << std::endl; | |
// Find data. | |
found = dataset.find(key.c_str()); | |
// Check we actually found something valid. | |
if (found == dataset.end()) { | |
// Element doesn't exist. | |
throw(Exception("Element doesn't exist after inserting.")); | |
} | |
} | |
// Set data to what we found. | |
data = found->second; | |
//std::cout << "-->> Data: " << *data << std::endl; | |
// Set key. | |
token = tokens.end()-1; | |
data->key = (*token).c_str(); | |
// Set parent container. | |
data->parentcontainer = collection; | |
} | |
} | |
// List data. | |
void DataContainer::listToStdOut(void) | |
{ | |
std::map<std::string, GenericData*>::iterator it; | |
// For each piece of data, output using "key => data" format. | |
for ( it=dataset.begin() ; it != dataset.end(); it++ ) | |
if ((*it).second->type == BOOLEAN) { | |
if (*((bool*)((*it).second->data)) == true) { | |
std::cout << "\t" << (*it).first.c_str() << " => true" << std::endl; | |
} else { | |
std::cout << "\t" << (*it).first.c_str() << " => false" << std::endl; | |
} | |
} else { | |
std::cout << "\t" << (*it).first.c_str() << " => " << *((*it).second) << std::endl; | |
} | |
} | |
// Set size. | |
int DataContainer::size(void) | |
{ | |
return dataset.size(); | |
} | |
// Get data from path. | |
GenericData& DataContainer::get(std::string key) { | |
// Split string up into tokens using split(). | |
std::vector<std::string> tokens = split(key, '.'); | |
// Vector itterator. | |
std::vector<std::string>::iterator token; | |
// Map itterator. | |
std::map<std::string, GenericData*>::iterator found; | |
// Find the map entry. | |
token=tokens.begin(); | |
found = dataset.find((*token).c_str()); | |
// Check we actually found something valid. | |
if (found == dataset.end()) { | |
// Element doesn't exist. | |
std::cout << "EDE: " << (*token).c_str() << " in: " << this->parentdata->path().c_str() << std::endl; | |
throw(Exception("Element doesn't exist")); | |
} | |
// Set data to the actual data contained in the pair. | |
GenericData* data = found->second; | |
// If we have a collection... | |
if (data->type == COLLECTION) { | |
// For each remaining token in the path... | |
for ( token=tokens.begin()+1 ; token < tokens.end(); token++ ) { | |
// Set data. | |
data = &(((DataContainer*)data->data)->get(*token)); | |
// If the retrieved data is not a collection, end here even if there are still tokens in the path. | |
if (data->type != COLLECTION) { | |
break; | |
} | |
} | |
} | |
return *data; | |
} | |
GenericData& DataContainer::operator[] (std::string key) | |
{ | |
// Alias for get(). | |
return get(key); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _GENERIC_DATA_H_ | |
#define _GENERIC_DATA_H_ | |
#include <iostream> | |
#include <string> | |
#include <cstring> | |
#include <sstream> | |
#include <vector> | |
#include <map> | |
#include <algorithm> | |
#include <iterator> | |
#include "Utilities.h" | |
#include "Exceptions.h" | |
// Forward declaration of DataContainer because it is used in GenericData. | |
class DataContainer; | |
// Types of data supported by GenericData. | |
// TODO: Add support for BOOL. | |
enum GenericDataTypes { NONE , INTEGER , FLOAT , STRING, BOOLEAN, COLLECTION }; | |
/** Generic data class. Not quite weak typing, but close. | |
@author Tom Savage | |
**/ | |
class GenericData | |
{ | |
public: | |
/** The type of the data contained within. **/ | |
GenericDataTypes type; | |
/** Data pointer. **/ | |
void* data; | |
/** Name or key of the data. **/ | |
std::string key; | |
/** Pointer to parent container. **/ | |
DataContainer* parentcontainer; | |
/** Default constructor. Creates with type NONE. **/ | |
GenericData(void); | |
/** Integer constructor. **/ | |
GenericData(int in); | |
/** Float constructor. **/ | |
GenericData(float in); | |
/** Double precision float constructor. **/ | |
GenericData(double in); | |
/** Boolean data constructor. **/ | |
GenericData(bool in); | |
/** String constructor. **/ | |
GenericData(std::string in); | |
/** Const char* constructor. **/ | |
GenericData(const char* in); | |
/** Collection constructor. **/ | |
GenericData(DataContainer* in); | |
/** Operator providing direct access to the collection container. **/ | |
//DataContainer *operator->(); | |
/** Assignment for integer data. **/ | |
GenericData operator=(int op2); | |
/** Assignment for float data. **/ | |
GenericData operator=(float op2); | |
/** Assignment for double data. **/ | |
GenericData operator=(double op2); | |
/** Assignment for boolean data. **/ | |
GenericData operator=(bool op2); | |
/** Assignment for string data. **/ | |
GenericData operator=(std::string op2); | |
/** Assignment for char data. **/ | |
GenericData operator=(const char* op2); | |
/** Assignment for collection data. **/ | |
GenericData operator=(DataContainer* op2); | |
/** Output operator. Outputs raw data. **/ | |
friend std::ostream& operator << (std::ostream& stream, GenericData data); | |
/** Delete void* data from type data. **/ | |
void DeleteVoidPointer(void* data, GenericDataTypes type); | |
/** Return string of path to this data. **/ | |
std::string path(void); | |
}; | |
/** Container for generic data. Provides heirarchical access by (.) delimeted string keys. | |
@author Tom Savage. | |
**/ | |
class DataContainer | |
{ | |
public: | |
/** Map of data contained within. **/ | |
std::map<std::string, GenericData*> dataset; | |
/** Pointer to parent data store. **/ | |
GenericData* parentdata; | |
/** Constructor. **/ | |
DataContainer(void); | |
/** Insert method. | |
@param key The name of the key under which the data is stored. **/ | |
void insert(std::string key, GenericData* value); | |
/** Lists data to console. **/ | |
void listToStdOut(void); | |
/** Returns the size of the dataset. **/ | |
int size(void); | |
/** Get data from path. **/ | |
GenericData& get(std::string key); | |
/** Access operator. **/ | |
GenericData& operator [] (std::string key); | |
}; | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
extern "C" { | |
#include "lua.h" | |
#include "lualib.h" | |
#include "lauxlib.h" | |
} | |
#include "GenericData.h" | |
DataContainer _dataset; | |
static int testfunc(lua_State* L) | |
{ | |
std::cout << "Lua triggered this text!" << std::endl; | |
return 0; | |
} | |
static int echo(lua_State* L) | |
{ | |
int args = lua_gettop(L); | |
for (int i = 1; i<=args; i++) { | |
std::cout << lua_tostring(L, i) << std::endl; | |
} | |
return 0; | |
} | |
static int getdata(lua_State* L) | |
{ | |
int args = lua_gettop(L); | |
GenericData data; | |
for (int i = 1; i<=args; i++) { | |
//try { | |
data = _dataset[lua_tostring(L, i)]; | |
//} catch (Exception& e) { | |
// std::cerr << "Error: " << e.what() << std::endl; | |
// return 0; | |
//} | |
switch (data.type) { | |
case INTEGER: | |
lua_pushinteger(L, *((int*)data.data)); | |
break; | |
case FLOAT: | |
lua_pushnumber(L, *((double*)data.data)); | |
break; | |
case BOOLEAN: | |
lua_pushboolean(L, *((bool*)data.data)); | |
break; | |
case STRING: | |
lua_pushstring(L, ((std::string*)data.data)->c_str()); | |
break; | |
case COLLECTION: | |
// Create a table. | |
lua_newtable(L); | |
// Set metatable. | |
lua_getglobal(L, "__gtidatamt__"); | |
lua_setmetatable(L, lua_gettop(L)-1); | |
// Return table. | |
break; | |
default: | |
lua_pushnil(L); | |
break; | |
} | |
} | |
return args; | |
} | |
static int getdatatype(lua_State* L) | |
{ | |
int args = lua_gettop(L); | |
if (args != 1) { | |
GenericDataTypes type = _dataset[lua_tostring(L, lua_gettop(L))].type; | |
switch (type) { | |
case INTEGER: | |
lua_pushstring(L, "INTEGER"); | |
break; | |
case FLOAT: | |
lua_pushstring(L, "FLOAT"); | |
break; | |
case BOOLEAN: | |
lua_pushstring(L, "BOOLEAN"); | |
break; | |
case STRING: | |
lua_pushstring(L, "STRING"); | |
break; | |
case COLLECTION: | |
lua_pushstring(L, "COLLECTION"); | |
break; | |
default: | |
lua_pushstring(L, "UNKNOWN"); | |
break; | |
} | |
} else { | |
lua_pushnil(L); | |
} | |
return 1; | |
} | |
static int setdata(lua_State* L) | |
{ | |
int args = lua_gettop(L); | |
if (args != 2) { | |
return 0; | |
} | |
std::string key = lua_tostring(L, 1); | |
GenericData& data = _dataset[key]; | |
switch (lua_type(L, 2)) { | |
case LUA_TNUMBER: | |
data = lua_tonumber(L, 2); | |
break; | |
case LUA_TSTRING: | |
data = lua_tostring(L, 2); | |
break; | |
case LUA_TBOOLEAN: | |
data = lua_toboolean(L, 2); | |
break; | |
default: | |
break; | |
} | |
return 0; | |
} | |
static int insertdata(lua_State* L) | |
{ | |
int args = lua_gettop(L); | |
if (args != 2) { | |
return 0; | |
} | |
std::string key = lua_tostring(L, 1); | |
GenericData* data; | |
switch (lua_type(L, 2)) { | |
case LUA_TNUMBER: | |
try { | |
data = new GenericData(lua_tonumber(L, 2)); | |
} catch(Exception& e) { | |
std::cerr << "Error: " << e.what() << std::endl; | |
} | |
break; | |
case LUA_TSTRING: | |
data = new GenericData(lua_tostring(L, 2)); | |
break; | |
case LUA_TBOOLEAN: | |
data = new GenericData(lua_toboolean(L, 2)); | |
break; | |
default: | |
break; | |
} | |
try { | |
_dataset.insert(key, data); | |
} catch(Exception& e) { | |
std::cerr << "Error: " << e.what() << std::endl; | |
} | |
} | |
int main ( int argc, char *argv[] ) | |
{ | |
_dataset.insert("eggs",new GenericData(5)); | |
_dataset.insert("cheese",new GenericData(7.7)); | |
_dataset.insert("spam",new GenericData("test")); | |
_dataset.insert("ham",new GenericData(true)); | |
_dataset.insert("level",new GenericData(new DataContainer)); | |
//((DataContainer*)_dataset["level"].data)->insert("x",new GenericData(1)); | |
//((DataContainer*)_dataset["level"].data)->insert("y",new GenericData(2.1)); | |
//((DataContainer*)_dataset["level"].data)->insert("z",new GenericData("hello")); | |
_dataset.insert("level.x",new GenericData(1)); | |
_dataset.insert("level.y",new GenericData(2.1)); | |
_dataset.insert("level.z",new GenericData("hello")); | |
std::cout << "Lua/C++ interaction test." << std::endl; | |
lua_State* L = lua_open(); | |
luaL_openlibs(L); | |
lua_register(L, "testfunc", testfunc); | |
lua_register(L, "myecho", echo); | |
lua_register(L, "getdata", getdata); | |
lua_register(L, "getdatatype", getdatatype); | |
lua_register(L, "setdata", setdata); | |
lua_register(L, "insertdata", insertdata); | |
luaL_dofile(L, "test.lua"); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- The getdata() function returns the values of the generic data contained at the provided keys. | |
-- Takes any number of argumants. | |
tbl= {} | |
__gtidatamt__ = {} | |
__gtidatamt__.__index = function(table, key) | |
--print ("__index request for: "..key) | |
local path = "" | |
if rawget(table, "__path__") then | |
path = rawget(table, "__path__").."." | |
data = getdata(path..key) | |
else | |
data = getdata(key) | |
end | |
if type(data)=="table" then | |
local newpath = (path..key) | |
rawset(data, "__path__", newpath) | |
end | |
return data | |
end | |
__gtidatamt__.__newindex = function(table, key, value) | |
--print ("__newindex request for: "..key.."="..value) | |
local path = "" | |
if rawget(table, "__path__") then | |
path = rawget(table, "__path__").."." | |
end | |
setdata(path..key, value) | |
return getdata(path..key) | |
end | |
setmetatable(tbl, __gtidatamt__) | |
print("spam => "..tbl.spam) | |
tbl.spam = "Help! I think it's working!" | |
print("spam => "..tbl.spam) | |
print("level.y => "..tbl.level.y) | |
tbl.level.y = "Yay! I think it's working!" | |
print("level.y => "..tbl.level.y) | |
print("Inserting data: level.a => 9000") | |
insertdata("level.a", 9000) | |
print("level.a => "..tbl.level.a) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Utilities.h" | |
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) { | |
std::stringstream ss(s); | |
std::string item; | |
while(std::getline(ss, item, delim)) { | |
elems.push_back(item); | |
} | |
return elems; | |
} | |
std::vector<std::string> split(const std::string &s, char delim) { | |
std::vector<std::string> elems; | |
return split(s, delim, elems); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <string> | |
#include <vector> | |
#include <sstream> | |
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems); | |
std::vector<std::string> split(const std::string &s, char delim); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment