{"action":"created","issue":{"url":"https://api.github.com/repos/billyquith/ponder/issues/32","repository_url":"https://api.github.com/repos/billyquith/ponder","labels_url":"https://api.github.com/repos/billyquith/ponder/issues/32/labels{/name}","comments_url":"https://api.github.com/repos/billyquith/ponder/issues/32/comments","events_url":"https://api.github.com/repos/billyquith/ponder/issues/32/events","html_url":"https://github.com/billyquith/ponder/issues/32","id":160440790,"number":32,"title":"Support for array_view/span-like arrays?","user":{"login":"Weeena","id":19182442,"avatar_url":"https://avatars.githubusercontent.com/u/19182442?v=3","gravatar_id":"","url":"https://api.github.com/users/Weeena","html_url":"https://github.com/Weeena","followers_url":"https://api.github.com/users/Weeena/followers","following_url":"https://api.github.com/users/Weeena/following{/other_user}","gists_url":"https://api.github.com/users/Weeena/gists{/gist_id}","starred_url":"https://api.github.com/users/Weeena/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Weeena/subscriptions","organizations_url":"https://api.github.com/users/Weeena/orgs","repos_url":"https://api.github.com/users/Weeena/repos","events_url":"https://api.github.com/users/Weeena/events{/privacy}","received_events_url":"https://api.github.com/users/Weeena/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/billyquith/ponder/labels/question","name":"question","color":"cc317c"}],"state":"closed","locked":false,"assignee":{"login":"billyquith","id":1754471,"avatar_url":"https://avatars.githubusercontent.com/u/1754471?v=3","gravatar_id":"","url":"https://api.github.com/users/billyquith","html_url":"https://github.com/billyquith","followers_url":"https://api.github.com/users/billyquith/followers","following_url":"https://api.github.com/users/billyquith/following{/other_user}","gists_url":"https://api.github.com/users/billyquith/gists{/gist_id}","starred_url":"https://api.github.com/users/billyquith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/billyquith/subscriptions","organizations_url":"https://api.github.com/users/billyquith/orgs","repos_url":"https://api.github.com/users/billyquith/repos","events_url":"https://api.github.com/users/billyquith/events{/privacy}","received_events_url":"https://api.github.com/users/billyquith/received_events","type":"User","site_admin":false},"assignees":[{"login":"billyquith","id":1754471,"avatar_url":"https://avatars.githubusercontent.com/u/1754471?v=3","gravatar_id":"","url":"https://api.github.com/users/billyquith","html_url":"https://github.com/billyquith","followers_url":"https://api.github.com/users/billyquith/followers","following_url":"https://api.github.com/users/billyquith/following{/other_user}","gists_url":"https://api.github.com/users/billyquith/gists{/gist_id}","starred_url":"https://api.github.com/users/billyquith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/billyquith/subscriptions","organizations_url":"https://api.github.com/users/billyquith/orgs","repos_url":"https://api.github.com/users/billyquith/repos","events_url":"https://api.github.com/users/billyquith/events{/privacy}","received_events_url":"https://api.github.com/users/billyquith/received_events","type":"User","site_admin":false}],"milestone":null,"comments":4,"created_at":"2016-06-15T14:46:04Z","updated_at":"2016-08-24T14:31:02Z","closed_at":"2016-07-03T12:26:18Z","body":"If I have a class that holds an array represented as a pointer and its length like e.g.\r\n```C++\r\ntemplate <typename T>\r\nstruct PodArray\r\n{\r\n    T* parray_;\r\n    std::size_t size_;\r\n};\r\n\r\nPONDER_TYPE(PodArray<uint8_t>)\r\n```\r\nthen I would like to be able to declare an array property for this class e.g. in the following way\r\n```C++\r\n    ponder::Class::declare<PodArray<uint8_t>>()\r\n        .property(\"Array\", &PodArray::parray_, &PodArray::size_);\r\n```\r\nIf I get it right then the resulting array property would (in the first place) have to be non-dynamic since in your current implementation `ArrayProperty::setSize()` does an element-wise `insertElement()` to grow the array.\r\n\r\nI made first attempts to implement such functionality, but since I am no C++ template expert this is hard for me because of all the compile time distinction of cases with `PropertyFactory[n]`, `Accessor[n]` and the like.\r\n\r\nWhen I think of an array represented as a pointer and its length I think of it in terms of an `array_view<T>` or a `span<T>`. Maybe a possible implementation for this issue could be based on a specialization of `ArrayMapper` for an `array_view<T>`. Unfortunately, a standardized class `array_view` is currently not yet available. Nevertheless, there are current implementations, like this one on GitHub: [array_view](https://github.com/rhysd/array_view).\r\n___\r\nSide note for future extension: Since our domain is image processing, I would of course be interested in an `ArrayProperty` for arrays with more than one dimension (for image processing, particularly, a support for two-dimensional arrays would be welcome). To illustrate this point, I give a code example:\r\n```C++\r\ntemplate <typename T>\r\nstruct Image\r\n{\r\n    T* pdata_;\r\n    std::size_t width_;\r\n    std::size_t height_;\r\n};\r\n\r\nPONDER_TYPE(Image<uint8_t>)\r\n...\r\n    ponder::Class::declare<Image<uint8_t>>()\r\n        .property(\"Image\", &PodArray::parray_, &PodArray::width_, &PodArray::height_);\r\n...\r\n   arrayProperty.get(obj, 2, 5); // get image value at position [2][5]\r\n```\r\nThe class `span<T>` would be suited for multi-dimensional arrays (there, the number of dimensions is called `rank`). `span<T>` is part of the GSL (Guidelines Support Library Guidelines) and maintained by Microsoft (you find it here on GitHub: [GSL](https://github.com/Microsoft/GSL)). Alas, I could imagine that to support an `ArrayProperty` for more than one dimension the effort would be high (and maybe in confict with the concepts so far). Also, I do not know in which state the implementation of `span<T>` is (its quite huge, more than 2000 LOC) and how platform independent it is.\r\n___\r\nAs always, I would be interested in your opinion and further plans. If you prefer I could try to continue with my attempts to implement such functionality (for the one-dimensional case).\r\n"},"comment":{"url":"https://api.github.com/repos/billyquith/ponder/issues/comments/242085366","html_url":"https://github.com/billyquith/ponder/issues/32#issuecomment-242085366","issue_url":"https://api.github.com/repos/billyquith/ponder/issues/32","id":242085366,"user":{"login":"Weeena","id":19182442,"avatar_url":"https://avatars.githubusercontent.com/u/19182442?v=3","gravatar_id":"","url":"https://api.github.com/users/Weeena","html_url":"https://github.com/Weeena","followers_url":"https://api.github.com/users/Weeena/followers","following_url":"https://api.github.com/users/Weeena/following{/other_user}","gists_url":"https://api.github.com/users/Weeena/gists{/gist_id}","starred_url":"https://api.github.com/users/Weeena/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Weeena/subscriptions","organizations_url":"https://api.github.com/users/Weeena/orgs","repos_url":"https://api.github.com/users/Weeena/repos","events_url":"https://api.github.com/users/Weeena/events{/privacy}","received_events_url":"https://api.github.com/users/Weeena/received_events","type":"User","site_admin":false},"created_at":"2016-08-24T14:31:01Z","updated_at":"2016-08-24T14:31:01Z","body":"This comment might get lengthy. Fortunately, it has a happy ending :-).\r\n___\r\nTo clarify what I wanted to achieve with my issues regarding arrays (this issue #32 and additionally issue #31): I wanted to implement an (as much as possible) generic serialization and deserialization for pondered objects. I'd like to do this much the same as in your XML example code (found in `.\\include\\ponder-xml`). For arrays, however, I'd like something more efficient than accessing the values individually. Rather, I'd like to get access to the array data with a reference or a pointer and treat the data as a buffer.\r\n\r\nSo, I want to show some of my attempts. Hopefully, someone finds this helpful. One obvious was this:\r\n```C++\r\ntemplate <typename T>\r\nstruct PodArray\r\n{\r\n    T* parray_;\r\n    std::size_t size_;\r\n    \r\n    void setSize(std::size_t newSize);\r\n    const T* getDataByPtr() { return parray_; };\r\n    const T& getDataByRef() { return *parray_; };\r\n};\r\n\r\ntypedef PodArray<uint8_t> Array8u;\r\n\r\nPONDER_TYPE(Array8u);\r\n\r\nponder::Class::declare<Array8u>()\r\n        .constructor()\r\n        .property(\"Size\", &Array8u::size_, &Array8u::setSize)  // get/set\r\n        .property(\"Data\", &Array8u::parray_); //<== compiler error\r\n```\r\nThis leads to compiler errors for the property `\"Data\"`. Equally:\r\n```C++\r\n        .property(\"DataByPtr\", &T::getDataByPtr); //<== compiler error\r\n```\r\nAgain compiler errors for property `\"DataByPtr\"`. Next try:\r\n```C++\r\n        .property(\"DataByRef\", &T::getDataByRef);\r\n```\r\nNo compiler error. But if you try to access this property\r\n```C++\r\n        const Array8u& dataByRef = obj.get(\"DataByRef\");\r\n```\r\nyou get a runtime error. It seems that, since the return value is passed with a `ponder::Value`, it is not possible to pass a reference to a POD with `ponder::Value`.\r\n\r\nOk, we still have additional ideas. Let's try it such:\r\n```C++\r\nstruct VectorWrapper\r\n{\r\n    std::vector<uint8_t> vector_;\r\n    const std::vector<uint8_t>* getDataByPtr() const { return &vector_; };\r\n    const std::vector<uint8_t>& getDataByRef() const { return vector_;  };\r\n};\r\n\r\nPONDER_TYPE(std::vector<uint8_t>)\r\nPONDER_TYPE(VectorWrapper)\r\n...\r\nponder::Class::declare<std::vector<uint8_t>>()\r\n        .constructor()\r\n        .constructor<std::size_t>();\r\nponder::Class::declare<VectorWrapper>()\r\n        .constructor()\r\n        .property(\"DataByPtr\", &VectorWrapper::getDataByPtr) //<== compiler errors with ponder::ArrayPropertyImpl\r\n        .property(\"DataByRef\", &VectorWrapper::getDataByRef) //<== compiler errors with ponder::ArrayPropertyImpl\r\n        .property(\"Data\", &VectorWrapper::vector_);\r\n```\r\nThe difference to the `PodArray` example is now that we declared `std::vector<uint8_t>` to ponder in the hope that it could be treated like a `ponder::UserObject`. Alas, this is not the case. The properties `\"DataByPtr\"` and `\"DataByRef\"` lead to compiler errors with `ponder::ArrayPropertyImpl`, and the `\"Data\"` property leads to compiler errors once the property is tried to accessed:\r\n```C++\r\n            const std::vector<uint8_t>& data = obj.get(\"Data\"); //<== compiler errors with ponder::ValueImpl\r\n```\r\nMy conclusion: I cannot use array types like `std::vector` in the same manner as a `ponder::UserObject` because `std::vector` and the like are treated in ponder in a special way, namely as arrays for its `ponder::ArrayProperty`.\r\n___\r\nFinally, I think I have found a satisfactory solution for my purpose: I can use a `gsl::span`. This is a wrapper for contiguous arrays. Its implementation can be found here: https://github.com/Microsoft/GSL/tree/master/gsl.\r\n\r\nTo show how it could work, I give some example code:\r\n```C++\r\n#include \"span.h\"\r\n\r\nPONDER_TYPE(gsl::span<uint8_t>)\r\n\r\nponder::Class::declare<gsl::span<uint8_t>>();\r\n\r\nstruct VectorWrapper\r\n{\r\n    std::vector<uint8_t> vector_;\r\n    gsl::span<uint8_t> getDataBySpan() { return gsl::span<uint8_t>(vector_); };\r\n};\r\n\r\nPONDER_TYPE(VectorWrapper)\r\n\r\nponder::Class::declare<VectorWrapper>()\r\n        .constructor()\r\n        .property(\"DataBySpan\", &VectorWrapper::getDataBySpan);\r\n```\r\nThen, I can gain access to the array data by asking for a span\r\n```C++\r\n        gsl::span<uint8_t> dataBySpan = obj.get(\"DataBySpan\");\r\n```\r\nand the using the access function `gsl::span::data()` which returns a pointer to the array data.\r\n\r\nOne more note: In my initial comment, I wrote that our domain is image processing, hence I am interested in arrays with more than one dimension (for image processing in particular I need two-dimensional arrays). The good news: `gsl::span` supports multiple dimensions. Futhermore: Often in image processing, not the entire image shall be accessed, rather only a \"region of interest\" (some rectangular sub-image). Another good news: `gsl::span` also provides a `gsl::strided_span` which can be used for this purpose."}}
{"actor":{"display_login":"Weeena"}}