Skip to content

Instantly share code, notes, and snippets.

@tcsavage
Created January 31, 2010 18:25
Show Gist options
  • Save tcsavage/291177 to your computer and use it in GitHub Desktop.
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
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)
#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();
}
#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;
};
#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);
}
#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
#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;
}
-- 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)
#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);
}
#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