{"action":"created","issue":{"url":"https://api.github.com/repos/CleverRaven/Cataclysm-DDA/issues/12791","labels_url":"https://api.github.com/repos/CleverRaven/Cataclysm-DDA/issues/12791/labels{/name}","comments_url":"https://api.github.com/repos/CleverRaven/Cataclysm-DDA/issues/12791/comments","events_url":"https://api.github.com/repos/CleverRaven/Cataclysm-DDA/issues/12791/events","html_url":"https://github.com/CleverRaven/Cataclysm-DDA/pull/12791","id":91438117,"number":12791,"title":"[CR] Better serialization system","user":{"login":"BevapDin","id":5095435,"avatar_url":"https://avatars.githubusercontent.com/u/5095435?v=3","gravatar_id":"","url":"https://api.github.com/users/BevapDin","html_url":"https://github.com/BevapDin","followers_url":"https://api.github.com/users/BevapDin/followers","following_url":"https://api.github.com/users/BevapDin/following{/other_user}","gists_url":"https://api.github.com/users/BevapDin/gists{/gist_id}","starred_url":"https://api.github.com/users/BevapDin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BevapDin/subscriptions","organizations_url":"https://api.github.com/users/BevapDin/orgs","repos_url":"https://api.github.com/users/BevapDin/repos","events_url":"https://api.github.com/users/BevapDin/events{/privacy}","received_events_url":"https://api.github.com/users/BevapDin/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-06-27T09:19:42Z","updated_at":"2015-06-28T08:41:50Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/CleverRaven/Cataclysm-DDA/pulls/12791","html_url":"https://github.com/CleverRaven/Cataclysm-DDA/pull/12791","diff_url":"https://github.com/CleverRaven/Cataclysm-DDA/pull/12791.diff","patch_url":"https://github.com/CleverRaven/Cataclysm-DDA/pull/12791.patch"},"body":"This implements a slightly different serialization system. It can be used parallel to the existing system. Some advantages:\r\n\r\n- No inheritance needed in the serialized classes. Currently several classes that don't actually need virtual functions (`item`, `tripoint`, `point` etc.) have them because they need to inherit them from `JsonDeserializer`/`JsonSerializer`. That needlessly increases the size of those otherwise small classes.\r\n\r\n- One function for both serialization and deserialization. Easier to write and avoids silly mistakes like writing to member \"desc\" and reading from member \"description\".\r\n\r\n- A single statement per member that contains all the required information: the member, the name in JSON, the default value (if any).\r\n\r\n- Without the inheritance, including json.h can be removed from most headers.\r\n\r\nI named the main serialization function `io`, `serialize` would be a better name but that is already used by the existing serialization system.\r\n\r\nCurrently pointers are loaded and stored via two lambda functions, one to transform the pointer into a string (which is stored and loaded) and another function to convert the string back into a pointer.\r\n\r\n----\r\n\r\nIt's currently only implemented for the `item` class, but it can be easily extended to other classes.\r\n\r\nEdit: this is related to #12774 as it allows to define default values during serialization."},"comment":{"url":"https://api.github.com/repos/CleverRaven/Cataclysm-DDA/issues/comments/116227996","html_url":"https://github.com/CleverRaven/Cataclysm-DDA/pull/12791#issuecomment-116227996","issue_url":"https://api.github.com/repos/CleverRaven/Cataclysm-DDA/issues/12791","id":116227996,"user":{"login":"kevingranade","id":860276,"avatar_url":"https://avatars.githubusercontent.com/u/860276?v=3","gravatar_id":"","url":"https://api.github.com/users/kevingranade","html_url":"https://github.com/kevingranade","followers_url":"https://api.github.com/users/kevingranade/followers","following_url":"https://api.github.com/users/kevingranade/following{/other_user}","gists_url":"https://api.github.com/users/kevingranade/gists{/gist_id}","starred_url":"https://api.github.com/users/kevingranade/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kevingranade/subscriptions","organizations_url":"https://api.github.com/users/kevingranade/orgs","repos_url":"https://api.github.com/users/kevingranade/repos","events_url":"https://api.github.com/users/kevingranade/events{/privacy}","received_events_url":"https://api.github.com/users/kevingranade/received_events","type":"User","site_admin":false},"created_at":"2015-06-28T08:41:50Z","updated_at":"2015-06-28T08:41:50Z","body":"Very excellent, nice way to cut down on clutter.\r\nThis got me thinking we may be able to have our cake and eat it too by making a declarative system like this for serialization that uses the streaming JSON api, so it only iterates over the input data once.\r\nThe streaming API normally works like this, writing is simple:\r\n```\r\nJsonOut json(fout);\r\njson.start_object();\r\njson.member(\"member1\", member1);\r\njson.member(\"member2\", member2);\r\njson.member(\"member3\", member3);\r\njson.end_object();\r\n```\r\nDeserialization is a little more complex and requires dispatching to the correct code since the input order can be arbitrary (which we want to maintain since it also gives us some degree of forward and backward compatibility.)\r\n```\r\nJsonIn jsin(fin);\r\njsin.start_object();\r\nwhile( !jsin.end_object() ) {\r\n    std::string member_name = jsin.get_member_name();\r\n    if( member_name == \"member1\" ) {\r\n        jsin.read(member1);\r\n    } else if( member_name == \"member1\" ) {\r\n        jsin.read(member2);\r\n    } else if( member_name == \"member1\" ) {\r\n        jsin.read(member3);\r\n    }\r\n```\r\nSo, how do we make this declarative?  In short, load the handlers into a map, like we do already at the top level, and then it's turtles all the way down.\r\n```\r\ntemplate <class T>\r\nvoid mapper::assign_object(JsonIn jsin, t *target) {\r\n    jsin.start_object();\r\n    while( !jsin.end_object() ) {\r\n        std::string member_name = jsin.get_member_name();\r\n        auto subMapper = T::mappings.find(member_name);\r\n        if( subMapper != T::mappings.end() ) {\r\n            subMapper->second(jsin, target);\r\n        } else { throw something; }\r\n    }\r\n}\r\n```\r\nIn order to implement this, I think we can use a \"pointer to data member\", which I wasn't totally sure existed until just now: http://en.cppreference.com/w/cpp/language/pointer\r\nI believe each declaration will look something like ```mapper.add(\"member1\", &type::member1);``` and mapper::add() will look something like\r\n```\r\ntemplate<class T, class M>\r\nvoid mapper::add(const std::string &member_name, M T::*member ) {\r\n    T::mappings[member_name] = [member](JsonStream j, T *target) {\r\n        j.io(target->*member);\r\n    };\r\n}\r\n```"}}