{"action":"created","issue":{"url":"https://api.github.com/repos/nlohmann/json/issues/1092","repository_url":"https://api.github.com/repos/nlohmann/json","labels_url":"https://api.github.com/repos/nlohmann/json/issues/1092/labels{/name}","comments_url":"https://api.github.com/repos/nlohmann/json/issues/1092/comments","events_url":"https://api.github.com/repos/nlohmann/json/issues/1092/events","html_url":"https://github.com/nlohmann/json/issues/1092","id":323812440,"number":1092,"title":"Q: few questions about json construction","user":{"login":"crusader-mike","id":22357016,"avatar_url":"https://avatars2.githubusercontent.com/u/22357016?v=4","gravatar_id":"","url":"https://api.github.com/users/crusader-mike","html_url":"https://github.com/crusader-mike","followers_url":"https://api.github.com/users/crusader-mike/followers","following_url":"https://api.github.com/users/crusader-mike/following{/other_user}","gists_url":"https://api.github.com/users/crusader-mike/gists{/gist_id}","starred_url":"https://api.github.com/users/crusader-mike/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/crusader-mike/subscriptions","organizations_url":"https://api.github.com/users/crusader-mike/orgs","repos_url":"https://api.github.com/users/crusader-mike/repos","events_url":"https://api.github.com/users/crusader-mike/events{/privacy}","received_events_url":"https://api.github.com/users/crusader-mike/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":6,"created_at":"2018-05-16T22:39:29Z","updated_at":"2018-05-17T00:47:16Z","closed_at":"2018-05-16T23:58:59Z","author_association":"NONE","body":"I was digging in json.hpp (v2.1.0) in order to understand how stuff gets created and check for unnecessary copies/allocations in simple cases like:\r\n```\r\njson x = { {\"val1\", 1}, {\"val2\", true} };\r\n```\r\n\r\nAs I see it:\r\n\r\n- `basic_json` has expensive copy semantics as evidenced by it's cctor and op=:\r\n```\r\n    basic_json(const basic_json& other)\r\n        : m_type(other.m_type)\r\n    {\r\n        ...\r\n        switch (m_type)\r\n        {\r\n            case value_t::object:\r\n            {\r\n                m_value = *other.m_value.object;   <-- mem alloc + cctor call\r\n                break;\r\n            }\r\n        ...\r\n\r\n    reference& operator=(basic_json other) ...    <-- note lack of const&\r\n    {\r\n        ...\r\n        using std::swap;\r\n        swap(m_type, other.m_type);\r\n        swap(m_value, other.m_value);\r\n```\r\n\r\n- which means in my example we will end up copying the same data many times (depending how deep in hierarchy given piece of data is)\r\n\r\n- `{\"val1\", 1}` will be used to create a `basic_json` of type array that contains two `basic_json` objects of types `string` and `whatever` respectively (underlying vector will be allocated on heap, same for that string)\r\n\r\n- then it will be used to populate an `std::map` entry in `basic_json` object of type _object_ here:\r\n```\r\n    basic_json(std::initializer_list<basic_json> init,\r\n               bool type_deduction = true,\r\n               value_t manual_type = value_t::array)\r\n    {\r\n        ...\r\n            std::for_each(init.begin(), init.end(), [this](const basic_json & element)\r\n            {\r\n                m_value.object->emplace(*(element[0].m_value.string), element[1]);\r\n            });\r\n```\r\nwhere `m_value.object` is of type `std::map<string, basic_json>` and `emplace` will end up passing a `string&` to related constructor (which will have to make another copy of a string)\r\n\r\n- and at some point `initializer_list` instances will be destroyed causing destruction of related `basic_json` objects and aforementioned `vector` and `string` objects will be deallocated\r\n\r\nPlease, correct me if this is incorrect or I missed smth important. \r\n\r\nNow, as I see it there is a quick and easy way to avoid few copies:\r\n```\r\nm_value.object->emplace(std::move(*(element[0].m_value.string)), std::move(element[1]));\r\n```\r\n\r\nQ1: do you think it is ok? if not -- why?\r\n\r\nQ2: this process has a lot of extra allocations (basic_json copies, underlying vectors and strings) -- is there an approach to construct my json object and avoid them?\r\n\r\nQ3: pushing it further a bit -- inserting new sub-element into object means `std::map` lookup, can I avoid it somehow? For example by providing sub-elements in proper order and forcing related `std::map::emplace/insert` to use `end()` as a hint.\r\n"},"comment":{"url":"https://api.github.com/repos/nlohmann/json/issues/comments/389709588","html_url":"https://github.com/nlohmann/json/issues/1092#issuecomment-389709588","issue_url":"https://api.github.com/repos/nlohmann/json/issues/1092","id":389709588,"user":{"login":"crusader-mike","id":22357016,"avatar_url":"https://avatars2.githubusercontent.com/u/22357016?v=4","gravatar_id":"","url":"https://api.github.com/users/crusader-mike","html_url":"https://github.com/crusader-mike","followers_url":"https://api.github.com/users/crusader-mike/followers","following_url":"https://api.github.com/users/crusader-mike/following{/other_user}","gists_url":"https://api.github.com/users/crusader-mike/gists{/gist_id}","starred_url":"https://api.github.com/users/crusader-mike/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/crusader-mike/subscriptions","organizations_url":"https://api.github.com/users/crusader-mike/orgs","repos_url":"https://api.github.com/users/crusader-mike/repos","events_url":"https://api.github.com/users/crusader-mike/events{/privacy}","received_events_url":"https://api.github.com/users/crusader-mike/received_events","type":"User","site_admin":false},"created_at":"2018-05-17T00:47:16Z","updated_at":"2018-05-17T00:47:16Z","author_association":"NONE","body":"It is certainly legal to do if you can prove that given reference points to a modifiable object in this particular case. That SO link doesn't tell the whole picture and there is a good reason why standard say \"may\" instead of \"shall\".\r\n\r\nIn case with initializer_list -- yes, you can argue that extremely smart compiler can potentially figure out details and put your list of very trivial basic_json objects into read-only segment. But in practice it will never happen -- not until you change your basic_json class to be smth like POD.\r\n"}}
{"actor":{"display_login":"crusader-mike"}}