Skip to content

Commit

Permalink
register glob function and json_array::get_array/get_object and json_…
Browse files Browse the repository at this point in the history
…object::get_array/get_object.

Register version of stringify that takes datastream argument.
This causes poco_json_array and poco_json_object classes to be exported in pocostuff.h.
  • Loading branch information
samtupy committed Jun 11, 2024
1 parent 10be848 commit a3a8c8b
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 83 deletions.
17 changes: 17 additions & 0 deletions src/filesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,17 @@ CScriptArray* FindDirectories(const string& path) {
return array;
}

CScriptArray* script_glob(const string& pattern, int options) {
// Expected to have been called from a script.
CScriptArray* array = CScriptArray::Create(asGetActiveContext()->GetEngine()->GetTypeInfoByDecl("array<string>"));
set<string> files;
try {
Glob::glob(pattern, files, options);
array->Reserve(files.size());
for (const std::string& path : files) array->InsertLast((void*)&path);
} catch(Exception&) {}
return array;
}
bool DirectoryExists(const string& path) {
try {
File f(path);
Expand Down Expand Up @@ -307,6 +318,11 @@ Timestamp FileGetModified(const string& path) {


void RegisterScriptFileSystemFunctions(asIScriptEngine* engine) {
engine->RegisterEnum("glob_options");
engine->RegisterEnumValue("glob_options", "GLOB_DEFAULT", Glob::GLOB_DEFAULT);
engine->RegisterEnumValue("glob_options", "GLOB_IGNORE_HIDDEN", Glob::GLOB_DOT_SPECIAL);
engine->RegisterEnumValue("glob_options", "GLOB_FOLLOW_SYMLINKS", Glob::GLOB_FOLLOW_SYMLINKS);
engine->RegisterEnumValue("glob_options", "GLOB_CASELESS", Glob::GLOB_CASELESS);
engine->RegisterGlobalFunction("bool directory_exists(const string& in)", asFUNCTION(DirectoryExists), asCALL_CDECL);
engine->RegisterGlobalFunction("bool directory_create(const string& in)", asFUNCTION(DirectoryCreate), asCALL_CDECL);
engine->RegisterGlobalFunction("bool directory_delete(const string& in)", asFUNCTION(DirectoryDelete), asCALL_CDECL);
Expand All @@ -317,6 +333,7 @@ void RegisterScriptFileSystemFunctions(asIScriptEngine* engine) {
engine->RegisterGlobalFunction("bool file_move(const string& in, const string& in)", asFUNCTION(FileMove), asCALL_CDECL);
engine->RegisterGlobalFunction("string[]@ find_directories(const string& in)", asFUNCTION(FindDirectories), asCALL_CDECL);
engine->RegisterGlobalFunction("string[]@ find_files(const string& in)", asFUNCTION(FindFiles), asCALL_CDECL);
engine->RegisterGlobalFunction("string[]@ glob(const string& in, glob_options = GLOB_DEFAULT)", asFUNCTION(script_glob), asCALL_CDECL);
engine->RegisterGlobalFunction("int64 file_get_size(const string& in)", asFUNCTION(FileGetSize), asCALL_CDECL);
engine->RegisterGlobalFunction("timestamp file_get_date_created(const string& in)", asFUNCTION(FileGetCreated), asCALL_CDECL);
engine->RegisterGlobalFunction("timestamp file_get_date_modified(const string& in)", asFUNCTION(FileGetModified), asCALL_CDECL);
Expand Down
182 changes: 99 additions & 83 deletions src/pocostuff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,15 @@
#include <Poco/Base32Encoder.h>
#include <Poco/Base64Decoder.h>
#include <Poco/Base64Encoder.h>
#include <Poco/Dynamic/Var.h>
#include <Poco/Debugger.h>
#include <Poco/Environment.h>
#include <Poco/Format.h>
#include <Poco/Glob.h>
#include <Poco/HexBinaryDecoder.h>
#include <Poco/HexBinaryEncoder.h>
#include <Poco/JSON/Array.h>
#include <Poco/JSON/Object.h>
#include <Poco/JSON/Parser.h>
#include <Poco/JSON/Query.h>
#include <Poco/Path.h>
#include <Poco/RefCountedObject.h>
#include <Poco/RegularExpression.h>
#include <Poco/SharedPtr.h>
#include <Poco/String.h>
Expand All @@ -49,15 +45,6 @@
#include "pocostuff.h"
using namespace Poco;

// I wonder if this is gonna be one of those things I'll look back at in a year and lament the fact that I couldn't have found a better way before spending the same amount of time then that I'm about to spend now finding a better way to wrap Poco's shared pointers in a simplistic enough way for real use. These SharedPtr's literally have reference counting that we can't use because the pointers' reference counter is private, and I'm not sure I can use Angelscri'ts composition features directly either because raw access to the underlying pointer is also private! Maybe I can specify my own reference counter when constructing these pointers (not sure I could count on that for all pointers being received), I could mess with Angelscript scoped reference types (not sure how the user could choose between assigning a new reference or value to a variable), and I could try using angelscripts opHndlAssign thing but based on the docs I would need to do more research to answer questions about that as well! So we're left with this, a duplicate reference counter and a duplicate copy of any raw pointer assigned just so that the user gets to type @my_var=handle vs. my_var=variable to deepcopy.
template <class T>
class poco_shared : public RefCountedObject {
public:
SharedPtr<T> shared;
T* ptr;
poco_shared(SharedPtr<T> shared) : shared(shared), ptr(shared.get()) {}
};

// Various string related functions.
std::string string_to_hex(const std::string& str) {
std::ostringstream ostr;
Expand Down Expand Up @@ -334,74 +321,97 @@ poco_shared<Dynamic::Var>* json_parse_datastream(datastream* input) {
return new poco_shared<Dynamic::Var>(new Dynamic::Var(parser.parse(*istr)));
}
// We make a custom json_object class for a couple reasons, mostly so that we can build in poco's json querying as well as other more easily wrapped functions. We'll do very similar for arrays below, I'm not even remotely good enough at this c++ templating thing to avoid this duplicated code and I don't want to meld it all into one class.
class poco_json_object : public poco_shared<JSON::Object> {
public:
poco_json_object(JSON::Object::Ptr o) : poco_shared<JSON::Object>(std::move(o)) {}
poco_shared<Dynamic::Var>* get(const std::string& key) {
return new poco_shared<Dynamic::Var>(SharedPtr<Dynamic::Var>(new Dynamic::Var(ptr->get(key)))); // Oof, more duplication that I like probably?
}
poco_shared<Dynamic::Var>* query(const std::string& path) {
JSON::Query q(shared); // I tried to cache the query object in the class variable above, and spent an hour or 2 figuring out that this causes a memory access violation in Poco::Dynamic::Var::Var.
return new poco_shared<Dynamic::Var>(SharedPtr<Dynamic::Var>(new Dynamic::Var(q.find(path))));
}
void set(const std::string& key, poco_shared<Dynamic::Var>* v) {
ptr->set(key, *v->ptr);
}
bool is_array(const std::string& key) {
return ptr->isArray(key);
}
bool is_null(const std::string& key) {
return ptr->isNull(key);
}
bool is_object(const std::string& key) {
return ptr->isObject(key);
}
std::string stringify(unsigned int indent = 0, int step = -1) {
std::ostringstream ostr;
ptr->stringify(ostr, indent, step);
return ostr.str();
}
CScriptArray* get_keys() {
asIScriptContext* ctx = asGetActiveContext();
asIScriptEngine* engine = ctx->GetEngine();
asITypeInfo* arrayType = engine->GetTypeInfoByDecl("array<string>");
CScriptArray* array = CScriptArray::Create(arrayType, ptr->size());
int c = 0;
for (JSON::Object::ConstIterator i = ptr->begin(); i != ptr->end(); i++, c++)((std::string*)(array->At(c)))->assign(i->first);
return array;
}
};
class poco_json_array : public poco_shared<JSON::Array> {
public:
poco_json_array(JSON::Array::Ptr a) : poco_shared<JSON::Array>(std::move(a)) {}
poco_shared<Dynamic::Var>* get(unsigned int index) {
return new poco_shared<Dynamic::Var>(SharedPtr<Dynamic::Var>(new Dynamic::Var(ptr->get(index))));
}
poco_shared<Dynamic::Var>* query(const std::string& path) {
JSON::Query q(shared);
return new poco_shared<Dynamic::Var>(SharedPtr<Dynamic::Var>(new Dynamic::Var(q.find(path))));
}
void set(unsigned int index, poco_shared<Dynamic::Var>* v) {
ptr->set(index, *v->ptr);
}
void add(poco_shared<Dynamic::Var>* v) {
ptr->add(*v->ptr);
}
bool is_array(unsigned int index) {
return ptr->isArray(index);
}
bool is_null(unsigned int index) {
return ptr->isNull(index);
}
bool is_object(unsigned int index) {
return ptr->isObject(index);
}
std::string stringify(unsigned int indent = 0, int step = -1) {
std::ostringstream ostr;
ptr->stringify(ostr, indent, step);
return ostr.str();
}
};
poco_json_object::poco_json_object(JSON::Object::Ptr o) : poco_shared<JSON::Object>(std::move(o)) {}
poco_shared<Dynamic::Var>* poco_json_object::get(const std::string& key) const {
return new poco_shared<Dynamic::Var>(SharedPtr<Dynamic::Var>(new Dynamic::Var(ptr->get(key)))); // Oof, more duplication that I like probably?
}
poco_shared<Dynamic::Var>* poco_json_object::query(const std::string& path) const {
JSON::Query q(shared); // I tried to cache the query object in the class variable above, and spent an hour or 2 figuring out that this causes a memory access violation in Poco::Dynamic::Var::Var.
return new poco_shared<Dynamic::Var>(SharedPtr<Dynamic::Var>(new Dynamic::Var(q.find(path))));
}
poco_json_array* poco_json_object::get_array(const std::string& key) const {
JSON::Array::Ptr obj = ptr->getArray(key);
if (!obj) return nullptr;
return new poco_json_array(obj);
}
poco_json_object* poco_json_object::get_object(const std::string& key) const {
JSON::Object::Ptr obj = ptr->getObject(key);
if (!obj) return nullptr;
return new poco_json_object(obj);
}
void poco_json_object::set(const std::string& key, poco_shared<Dynamic::Var>* v) {
ptr->set(key, *v->ptr);
}
bool poco_json_object::is_array(const std::string& key) const {
return ptr->isArray(key);
}
bool poco_json_object::is_null(const std::string& key) const {
return ptr->isNull(key);
}
bool poco_json_object::is_object(const std::string& key) const {
return ptr->isObject(key);
}
std::string poco_json_object::stringify(unsigned int indent, int step) const {
std::ostringstream ostr;
ptr->stringify(ostr, indent, step);
return ostr.str();
}
void poco_json_object::stringify(datastream* ds, unsigned int indent, int step) const {
if (!ds || !ds->get_ostr()) throw InvalidArgumentException("stream not opened for writing");
ptr->stringify(*ds->get_ostr(), indent, step);
}
CScriptArray* poco_json_object::get_keys() const {
asIScriptContext* ctx = asGetActiveContext();
asIScriptEngine* engine = ctx->GetEngine();
asITypeInfo* arrayType = engine->GetTypeInfoByDecl("array<string>");
CScriptArray* array = CScriptArray::Create(arrayType, ptr->size());
int c = 0;
for (JSON::Object::ConstIterator i = ptr->begin(); i != ptr->end(); i++, c++)((std::string*)(array->At(c)))->assign(i->first);
return array;
}

poco_json_array::poco_json_array(JSON::Array::Ptr a) : poco_shared<JSON::Array>(std::move(a)) {}
poco_shared<Dynamic::Var>* poco_json_array::get(unsigned int index) const {
return new poco_shared<Dynamic::Var>(SharedPtr<Dynamic::Var>(new Dynamic::Var(ptr->get(index))));
}
poco_shared<Dynamic::Var>* poco_json_array::query(const std::string& path) const {
JSON::Query q(shared);
return new poco_shared<Dynamic::Var>(SharedPtr<Dynamic::Var>(new Dynamic::Var(q.find(path))));
}
poco_json_array* poco_json_array::get_array(unsigned int index) const {
JSON::Array::Ptr obj = ptr->getArray(index);
if (!obj) return nullptr;
return new poco_json_array(obj);
}
poco_json_object* poco_json_array::get_object(unsigned int index) const {
JSON::Object::Ptr obj = ptr->getObject(index);
if (!obj) return nullptr;
return new poco_json_object(obj);
}
void poco_json_array::set(unsigned int index, poco_shared<Dynamic::Var>* v) {
ptr->set(index, *v->ptr);
}
void poco_json_array::add(poco_shared<Dynamic::Var>* v) {
ptr->add(*v->ptr);
}
bool poco_json_array::is_array(unsigned int index) const {
return ptr->isArray(index);
}
bool poco_json_array::is_null(unsigned int index) const {
return ptr->isNull(index);
}
bool poco_json_array::is_object(unsigned int index) const {
return ptr->isObject(index);
}
std::string poco_json_array::stringify(unsigned int indent, int step) const {
std::ostringstream ostr;
ptr->stringify(ostr, indent, step);
return ostr.str();
}
void poco_json_array::stringify(datastream* ds, unsigned int indent, int step) const {
if (!ds || !ds->get_ostr()) throw InvalidArgumentException("stream not opened for writing");
ptr->stringify(*ds->get_ostr(), indent, step);
}

// Some functions needed to wrap poco regular expression.
static asITypeInfo* StringArrayType = NULL;
Expand Down Expand Up @@ -575,7 +585,10 @@ void RegisterPocostuff(asIScriptEngine* engine) {
engine->RegisterObjectMethod("json_object", "void set_opIndex(const string&in, const var&in) property", asMETHOD(poco_json_object, set), asCALL_THISCALL);
engine->RegisterObjectMethod("json_object", "void set(const string&in, const var&in)", asMETHOD(poco_json_object, set), asCALL_THISCALL);
engine->RegisterObjectMethod("json_object", "var@ opCall(const string&in) const", asMETHOD(poco_json_object, query), asCALL_THISCALL);
engine->RegisterObjectMethod("json_object", "string stringify(int indent = 0, int step = -1) const", asMETHOD(poco_json_object, stringify), asCALL_THISCALL);
engine->RegisterObjectMethod("json_object", "json_array@ get_array(const string&in) const", asMETHOD(poco_json_object, get_array), asCALL_THISCALL);
engine->RegisterObjectMethod("json_object", "json_object@ get_object(const string&in) const", asMETHOD(poco_json_object, get_object), asCALL_THISCALL);
engine->RegisterObjectMethod("json_object", "string stringify(uint indent = 0, int step = -1) const", asMETHODPR(poco_json_object, stringify, (unsigned int, int) const, std::string), asCALL_THISCALL);
engine->RegisterObjectMethod("json_object", "void stringify(datastream@, uint indent = 0, int step = -1) const", asMETHODPR(poco_json_object, stringify, (datastream*, unsigned int, int) const, void), asCALL_THISCALL);
engine->RegisterObjectMethod("json_object", "uint size() const", asMETHOD(JSON::Object, size), asCALL_THISCALL, 0, asOFFSET(poco_json_object, ptr), true);
engine->RegisterObjectMethod("json_object", "bool get_escape_unicode() const property", asMETHOD(JSON::Object, getEscapeUnicode), asCALL_THISCALL, 0, asOFFSET(poco_json_object, ptr), true);
engine->RegisterObjectMethod("json_object", "void set_escape_unicode(bool) property", asMETHOD(JSON::Object, setEscapeUnicode), asCALL_THISCALL, 0, asOFFSET(poco_json_object, ptr), true);
Expand All @@ -593,7 +606,10 @@ void RegisterPocostuff(asIScriptEngine* engine) {
engine->RegisterObjectMethod("json_array", "void set_opIndex(uint, const var&in) property", asMETHOD(poco_json_array, set), asCALL_THISCALL);
engine->RegisterObjectMethod("json_array", "void add(var@)", asMETHOD(poco_json_array, add), asCALL_THISCALL);
engine->RegisterObjectMethod("json_array", "var@ opCall(const string&in) const", asMETHOD(poco_json_array, query), asCALL_THISCALL);
engine->RegisterObjectMethod("json_array", "string stringify(int indent = 0, int step = -1)", asMETHOD(poco_json_array, stringify), asCALL_THISCALL);
engine->RegisterObjectMethod("json_array", "json_array@ get_array(uint) const", asMETHOD(poco_json_array, get_array), asCALL_THISCALL);
engine->RegisterObjectMethod("json_array", "json_object@ get_object(uint) const", asMETHOD(poco_json_array, get_object), asCALL_THISCALL);
engine->RegisterObjectMethod("json_array", "string stringify(uint indent = 0, int step = -1)", asMETHODPR(poco_json_array, stringify, (unsigned int, int) const, std::string), asCALL_THISCALL);
engine->RegisterObjectMethod("json_array", "void stringify(datastream@, uint indent = 0, int step = -1)", asMETHODPR(poco_json_array, stringify, (datastream*, unsigned int, int) const, void), asCALL_THISCALL);
engine->RegisterObjectMethod("json_array", "uint length()", asMETHOD(JSON::Array, size), asCALL_THISCALL, 0, asOFFSET(poco_json_array, ptr), true);
engine->RegisterObjectMethod("json_array", "uint size()", asMETHOD(JSON::Array, size), asCALL_THISCALL, 0, asOFFSET(poco_json_array, ptr), true);
engine->RegisterObjectMethod("json_array", "bool get_escape_unicode() property", asMETHOD(JSON::Array, getEscapeUnicode), asCALL_THISCALL, 0, asOFFSET(poco_json_array, ptr), true);
Expand Down
45 changes: 45 additions & 0 deletions src/pocostuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,51 @@

#include <angelscript.h>
#include <Poco/Format.h>
#include <Poco/RefCountedObject.h>
#include <Poco/Dynamic/Var.h>
#include <Poco/JSON/Array.h>
#include <Poco/JSON/Object.h>

// I wonder if this is gonna be one of those things I'll look back at in a year and lament the fact that I couldn't have found a better way before spending the same amount of time then that I'm about to spend now finding a better way to wrap Poco's shared pointers in a simplistic enough way for real use. These SharedPtr's literally have reference counting that we can't use because the pointers' reference counter is private, and I'm not sure I can use Angelscript's composition features directly either because raw access to the underlying pointer is also private! Maybe I can specify my own reference counter when constructing these pointers (not sure I could count on that for all pointers being received), I could mess with Angelscript scoped reference types (not sure how the user could choose between assigning a new reference or value to a variable), and I could try using angelscripts opHndlAssign thing but based on the docs I would need to do more research to answer questions about that as well! So we're left with this, a duplicate reference counter and a duplicate copy of any raw pointer assigned just so that the user gets to type @my_var=handle vs. my_var=variable to deepcopy.
template <class T> class poco_shared : public Poco::RefCountedObject {
public:
Poco::SharedPtr<T> shared;
T* ptr;
poco_shared(Poco::SharedPtr<T> shared) : shared(shared), ptr(shared.get()) {}
};

class datastream;
class poco_json_array;
class poco_json_object : public poco_shared<Poco::JSON::Object> {
public:
poco_json_object(Poco::JSON::Object::Ptr o);
poco_shared<Poco::Dynamic::Var>* get(const std::string& key) const;
poco_shared<Poco::Dynamic::Var>* query(const std::string& path) const;
poco_json_array* get_array(const std::string& key) const;
poco_json_object* get_object(const std::string& key) const;
void set(const std::string& key, poco_shared<Poco::Dynamic::Var>* v);
bool is_array(const std::string& key) const;
bool is_null(const std::string& key) const;
bool is_object(const std::string& key) const;
std::string stringify(unsigned int indent = 0, int step = -1) const;
void stringify(datastream* ds, unsigned int indent = 0, int step = -1) const;
CScriptArray* get_keys() const;
};
class poco_json_array : public poco_shared<Poco::JSON::Array> {
public:
poco_json_array(Poco::JSON::Array::Ptr a);
poco_shared<Poco::Dynamic::Var>* get(unsigned int index) const;
poco_shared<Poco::Dynamic::Var>* query(const std::string& path) const;
poco_json_array* get_array(unsigned int index) const;
poco_json_object* get_object(unsigned int index) const;
void set(unsigned int index, poco_shared<Poco::Dynamic::Var>* v);
void add(poco_shared<Poco::Dynamic::Var>* v);
bool is_array(unsigned int index) const;
bool is_null(unsigned int index) const;
bool is_object(unsigned int index) const;
std::string stringify(unsigned int indent = 0, int step = -1) const;
void stringify(datastream* ds, unsigned int indent = 0, int step = -1) const;
};

// Not sure if this should be in another header, if I use this for more than Poco I may consider it. I'm so tired of trying to decide whether to register angelscript types as value or reference. I'm always turned away from reference types because well, the classes I want to wrap have no reference counter. Look maybe this could have been done with c++ multiple inheritance or something, but my brain is tired of learning new concepts for the moment and so hopefully this is portable enough to make better later. Following are methods for attaching a reference counter to any class for registration with angelscript as a reference type.
struct angelscript_refcounted {
Expand Down

0 comments on commit a3a8c8b

Please sign in to comment.