Re: Typemaps jsoncpp => Python: recursive typemap(outargs)?
Julien Marrec <[email protected]> Wed, 14 Jun 2023 13:57:49 +0200
| Newsgroups | gmane.comp.programming.swig |
|---|---|
| Message-ID | <CAEqKKtxjJ11d6M=nG_h1vu-4EbUP3kN-Unny7NAnkqwcBS6vGw@mail.gmail.com> |
Hello,
Two years later and change I’m revisiting this because I had an increasing
need for this. I have figured out a why to make it work with the fragments,
thanks William. It’s probably not optimized (I think I could figure out a
way to use swig::from or something), and reference counting might be off,
but it appears to work.
In case someone stumbles upon this question, here’s what I came up with:
https://github.com/jmarrec/SwigTestBed/blob/42e56b27452225dc825c55f5ee7ba3c565855154/Model.i#L56-L134
#if defined SWIGPYTHON
%fragment("JsonToDict","header", fragment="SWIG_FromCharPtrAndSize") {
PyObject* SWIG_From_JsonValue(const Json::Value& value) {
if (value.isBool()) {
return value.asBool() ? Py_True : Py_False;
} else if (value.isIntegral()) {
return PyLong_FromLongLong(value.asInt64());
} else if (value.isNumeric()) {
return PyFloat_FromDouble(value.asDouble());
} else if (value.isString()) {
// return PyUnicode_FromString(value.asCString());
const auto str = value.asString();
return SWIG_FromCharPtrAndSize(str.data(), str.size());
} else if (value.isArray()) {
PyObject* result = PyList_New(value.size());
Py_ssize_t idx = 0;
for( const auto& arrayElement : value) {
// TODO: this should do a recursive call to convert n
(which is Json::Value) to a python type...
auto val = SWIG_From_JsonValue(arrayElement);
// PyList_Append(result, val);
PyList_SetItem(result, idx++, val);
}
return result;
} else if (value.isObject()) {
PyObject* result = PyDict_New();
for( const auto& id : value.getMemberNames()) {
// TODO: this should do a recursive call to convert
*$1[id] (which is a Json::Value) to a python type...
auto val = SWIG_From_JsonValue(value[id]);
PyDict_SetItemString(result, id.c_str(), val);
Py_DECREF(val);
}
return result;
}
return PyDict_New();
}
}
%typemap(out, fragment="JsonToDict") Json::Value {
$result = SWIG_From_JsonValue($1);
}#endif
#if defined SWIGRUBY
%fragment("JsonToDict","header", fragment="SWIG_FromCharPtrAndSize") {
VALUE SWIG_From_JsonValue(const Json::Value& value) {
if (value.isBool()) {
return value.asBool() ? Qtrue : Qfalse;
} else if (value.isIntegral()) {
return INT2NUM(value.asInt64());
} else if (value.isNumeric()) {
return DOUBLE2NUM(value.asDouble());
} else if (value.isString()) {
const auto str = value.asString();
return SWIG_FromCharPtrAndSize(str.data(), str.size());
} else if (value.isArray()) {
VALUE result = rb_ary_new2(value.size());
for( const auto& arrayElement : value) {
rb_ary_push(result, SWIG_From_JsonValue(arrayElement));
}
return result;
} else if (value.isObject()) {
VALUE result = rb_hash_new();
for( const auto& id : value.getMemberNames()) {
rb_hash_aset(result, ID2SYM(rb_intern(id.data())),
SWIG_From_JsonValue(value[id]));
}
return result;
}
return rb_hash_new();
}
}
%typemap(out, fragment="JsonToDict") Json::Value {
$result = SWIG_From_JsonValue($1);
}#endif
Best,
Julien
—
Julien Marrec, EBCP, BPI MFBA
Owner at EffiBEM <http://www.effibem.com>
T: +33 6 95 14 42 13
LinkedIn (en <https://www.linkedin.com/in/julienmarrec>) | (fr
<https://fr.linkedin.com/in/julienmarrec/fr>) :
<http://www.linkedin.com/in/julienmarrec>
Le mer. 10 mars 2021 à 00:26, William S Fulton <[email protected]> a
écrit :
> Hi Julien
>
> With regards to recursive calls, I suggest your typemap should be a one
> liner which simply calls into a support function. The support function can
> then recursively call itself. Have a look at fragments in the Typemaps
> chapter, where your fragment would contain the support function and the
> support function would only be generated once no matter how many times the
> typemap is used.
>
> William
>
>
> On Wed, 3 Mar 2021 at 15:02, Julien Marrec <[email protected]>
> wrote:
>
>> Hello,
>>
>> First, has anyone wrapped jsoncpp
>> <https://github.com/open-source-parsers/jsoncpp> in Swig? If I can avoid
>> reinventing the wheel...
>>
>> Otherwise, I basically have a Json::Value toJSON() method in C++.
>>
>> I'd like to return a python dict instead (and a Hash in ruby...). So
>> far, I've done a workaround by creating a std::string toJSONString() {
>> return toJSON().toStyledString(); }, and using %pythoncode (and %init
>> with rb_eval_string in ruby). It works (see at end of email if you're
>> curious), but I'd like to try a typemap(argout) instead. All my attempts
>> so far have miserably failed, probably because:
>>
>> 1. This is pretty much the first time I tried to do a typemap, and,
>> 2. I have no clue how to do a recursive call to the typemap(argout)
>> itself.
>>
>> Here is one of my attempts (I tried with Json::Value instead of Json::Value
>> * too)
>>
>> #if defined SWIGPYTHON
>>
>> %typemap(argout) Json::Value * {
>>
>> if ($1->isIntegral()) {
>> $result = PyLong_FromLongLong($1->asInt64());
>> } else if ($1->isNumeric()) {
>> $result = PyFloat_FromDouble($1->asDouble());
>> } else if ($1->isString()) {
>> $result = PyUnicode_FromString($->.asString());
>> } else if ($1->isArray()) {
>> $result = PyList_New();
>> for( const auto& n : *$1) {
>> // TODO: this should do a recursive call to convert n (which is Json::Value) to a python type...
>> PyList_Append(n);
>> }
>> } else if ($1->isObject()) {
>> $result = PyDict_New();
>> for( const auto& id : $1->getMemberNames()) {
>> // TODO: this should do a recursive call to convert *$1[id] (which is a Json::Value) to a python type...
>> PyDict_SetItemString($result, id.c_str(), *$1[id]);
>> }
>> }
>> }
>> #endif
>>
>> The documentation for Json::Value is at
>> https://open-source-parsers.github.io/jsoncpp-docs/doxygen/class_json_1_1_value.html
>>
>> *If someone could nudge me in the right direction, that'd be much
>> appreciated.*
>>
>> Thank you,
>>
>> Julien
>> ------------------------------
>>
>> For reference my current workaround: in epJSON.i:
>>
>> #if defined SWIGRUBY
>> // This isn't super clean and there might be a better way with typemaps in SWIG rather than using a String in between,
>> // but still helpful I think: we redefine toJSON that will return a native ruby hash. 'json' is part of ruby stdlib since at least ruby 2.5.5
>> %init %{
>> rb_eval_string("OpenStudio::EPJSON.module_eval { define_method(:toJSON) { |arg| json_str = self.toJSONString(arg); require 'json'; JSON.load(json_str); }; module_function(:toJSON) }");
>> %}
>> #endif
>>
>> #if defined SWIGPYTHON
>>
>> // Let's use monkey-patching via unbound functions
>> // Edit: not even needed here
>> %pythoncode %{
>> # Manually added: Lazy-load the json module (python std lib) and return a dict via toJSONString
>> def toJSON(*args) -> dict:
>> import json
>> return json.loads(toJSONString(*args))
>> %}
>> #endif
>>
>>
>> --
>> Julien Marrec, EBCP, BPI MFBA
>> Owner at EffiBEM <http://www.effibem.com>
>> T: +33 6 95 14 42 13
>>
>> LinkedIn (en <https://www.linkedin.com/in/julienmarrec>) *| *(fr
>> <https://fr.linkedin.com/in/julienmarrec/fr>) :
>> <http://www.linkedin.com/in/julienmarrec>
>> _______________________________________________
>> Swig-user mailing list
>> [email protected]
>> https://lists.sourceforge.net/lists/listinfo/swig-user
>>
>
_______________________________________________
Swig-user mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/swig-user