-
-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
std::unorderd_map cannot be used as ObjectType #164
Comments
I've been looking at this for a bit, and a 1st run solution isn't that bad. Just doing SFINAE/enable_if to switch based on unordered_map vs map is pretty easy using is_same or is_base_of. However, I'm still trying to figure out a way to not break existing code that specializes the ObjectType parameter to a non-std::map container (such as a custom map that has object_t compatible template args). The difference between map and unordered_map is 2 fold: Optimally, the object_t would be defined entirely based on the ObjectType signature, though I'm currently having a tough time coming up with anything. |
I think one can check via |
Alright, sounds acceptable. I'm not a HUGE fan of doing it by anything but signature, since the result is overly dependent on the std::map vs std::unordered_map types, but I'll put a pull request together for sdoing it that way in a few hours. Thanks! |
I've made an attempt that is ALMOST working. I differentiate between the two types via SFINAE based on key_comparer and hasher, which makes object_t do exactly what we need it to. See the commit here: https://github.com/erichkeane/json/commit/90ab92ade1df8d32ea52e7ad302b41c47b783dce One might think that type-test-validity would work the same way I'm doing it without having to dive into the member types, however unordered_map will go through resolution successfully with only the 4 template arguments, so it would otherwise be ambiguous. The calls to void_converter are done with ObjectType<char,char>::... since the Value type is incomplete at the time. HOWEVER, you'll note if you attempt to build this that this won't compile with an unordered_map due a number of lines requiring object_t::key_type/value_type, which requires the unordered_map to be fully fleshed out, yet basic_json is still incomplete. The only solution to the problem that I could find is to convert all object_t::key_type to StringType and value_type to std::pair<StringType,basic_json>. I'll do a pull request, but I wanted a better explanation here. Also, if there is a nicer solution to the second commit, I'd REALLY love to see it. |
An alternative would be to let the user provide partially defined template types for More specifically, the object type should respect the AssociativeContainer requirements, or at the very least the subset you need.
template <typename K, typename V>
using defaut_object_type = std::map<K, V>;
template <typename V>
using default_array_type = std::vector<V>;
template <
template<typename U, typename V> class ObjectType = defaut_object_type,
template<typename U> class ArrayType = default_array_type,
class StringType = std::string,
class BooleanType = bool,
class NumberIntegerType = int64_t,
class NumberFloatType = double
>
class basic_json {
}; I removed the allocator for now, as template <typename T>
class my_custom_static_allocator;
using custom_string_type = std::basic_string<
char, std::char_traits<char>, my_custom_static_allocator<char>>;
template<typename K, typename V>
using custom_object_type = std::unordered_map<K, V, std::hash<K>,
std::equal_to<K>, my_custom_static_allocator<std::pair<const K, V>>>;
template<typename V>
using custom_array_type = std::vector<V, my_custom_static_allocator<V>>;
using my_json = basic_json<custom_object_type, custom_array_type, custom_string_type>; Right now for stateless allocators, the |
Hi @erichkeane, thanks for your effort! What do you think of @palacaze 's proposal? It may be more verbose, but it could help to approach issue #161 as well. |
I really like his proposal, it would definitely save on a whole bunch of future specializations, and make sure that we are properly abstracted. My concern was solely that since this is a 1.0.0 product, that changing APIs at all would be prohibitive. I also like the allocation work that he wishes to do, it doesn't seem like it would be that bad! My question for @palacaze: How do you see the two working together? If we no longer make basic_json use an allocator parameter (here), how do we get #161 to work as well? Would we want to make the custom object type be something like:
? Or is the idea that the custom array/object types would be configured with their OWN allocators that didn't match the rest of the basic_json type? |
This is an attempt to make it work with nlohmann#164. The thing is that the current code attempts to define containers with incomplete types, this is not supported by the standard and does not work unordered_map. std::vector and std::map work but we might be in the realm of undefined behavior.
Ok so I looked into it and I won't be that easy. I will mention both #161 and this bug here, first in reply to @erichkeane how do I make I think a judgement call is needed flexibility-wise, it is difficult to get the best of both worlds. First of all, we need to consider the potential uses of an allocator. It may be used for The use of the user-provided allocator for internal nodes is a no-brainer, this is what is expected and easy to do by supplying the reference to she unique_ptr constructor.
So how would I make this work in practise? The first two ideas are quite similar API-wise and mostly differ by their behaviour. Well we provide constructors with an optional allocator parameter. Unfortunately, I got also some bad news. The array_t and object_t types as defined currently use incomplete template parameter types, and this is not really supported by the STL. In fact using unordered_map does not even compile. @erichkeane met the same problem and got around it by avoiding the use of The obvious fix would be to store pointers in the map and vector, but it implies a big overhaul of the code. Another would be to introduce your own containers allowing incomplete types. The Boost libraries have them. |
Reading about all the problems, I am also getting unsure whether it is a good idea to make the class allocator-aware. It was not a design goal in the first place, but (in my limited understanding - which is also wrong as we see in #161) at some point it seems to be an easy addition. That said, I have limited understanding of the values of a user-provided allocator, so I cannot really judge whether the effort is actually adding value in the end. |
The fix for this issue is in #209, and the issue with undefined behavior due to incomplete types in |
The following code does not compile:
The reason for this seems to be that the definition of
object_t
seems to rely on specifics ofstd::map
:The text was updated successfully, but these errors were encountered: