Skip to content
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

Compiler error when using custom map type and type alias #428

Open
pkerichang opened this issue Jun 20, 2024 · 3 comments
Open

Compiler error when using custom map type and type alias #428

pkerichang opened this issue Jun 20, 2024 · 3 comments

Comments

@pkerichang
Copy link

pkerichang commented Jun 20, 2024

Hello,

so I have a custom map/dictionary data structure, from the cookbook, it seems like it is easy enough to just simply make it a type alias of std::unordered_map like so:

namespace daw::json {

template <class Key, class T> struct json_data_contract<my::sorted_map<Key, T>> {
    using map_type = std::unordered_map<Key, T>;
    using class_type = my::sorted_map<Key, T>;

    struct map_builder {
        constexpr class_type operator()(const map_type &val) const {
            class_type ans;
            ans.reserve(val.size());
            for (const auto &[k, v] : val) {
                ans.emplace(k, v);
            }
            return ans;
        }
    };

    using constructor_t = map_builder;

    using type = json_type_alias<map_type>;
    static inline auto to_json_data(const class_type &val) {
        map_type ans;
        ans.reserve(val.size());
        for (const auto &[k, v] : val) {
            ans.emplace(k, v);
        }
        return ans;
    }
};

} // namespace daw::json

however this fails to compile, as it seems to be trying to use daw::construct_a_t to create my::sorted_map from the unordered map, instead of using the map_builder struct I defined. I have to add the following template specialization to get it to work:

namespace daw {

template <class Key, class T> struct construct_a_t<my::sorted_map<Key, T>> {
    constexpr my::sorted_map<Key, T>
    operator()(const std::unordered_map<Key, T> &val) const {
        my::sorted_map<Key, T> ans;
        ans.reserve(val.size());
        for (const auto &[k, v] : val) {
            ans.emplace(k, v);
        }
        return ans;
    }
};

} // namespace daw

Interestingly, I cannot just simply specialize construct_a_t. If I remove the constructor_t typedef and map_builder, compilation also fails. I need to include both for it to work. This seems like an oversight?

@beached
Copy link
Owner

beached commented Jun 23, 2024

This might be a bug, looking into why it isn't choosing your specified constructor

@beached
Copy link
Owner

beached commented Jun 25, 2024

Just trying to replicate that error. I suspect it is related to the auto deduced mapping and the explicit constructor_t not taking over. I suspect a manual mapping of using type = json_type_alias<json_key_value_no_name<map_type, Key, T, map_builder>>; with a constructor parameter should get you there. However, your map builder will need to take two iterators as param, the type of them is daw::json::json_details::json_parse_kv_class_iterato<...> but it will be easier to take them as a template param e.g.

struct map_builder {
        template<typename Iter>
        constexpr class_type operator()( Iter first, Iter last ) const {
            class_type ans;
            // calling distance here can be costly and incur a parse of the range ans.reserve(val.size());
            // it may be random when the data is out of order from expected but that just preserves the known size


            // alternatively if your map type has an iterators  range ctor return class_type( first, last );
            for( ; first != last; ++first ) {
                auto [k,v] = *first;
                ans.emplace(k, v);
            }
            return ans;
        }
    };

@pkerichang
Copy link
Author

I came up with this simple example to test this issue:

template <class Key, class T> class vector_map {
  private:
    using value_type = std::pair<Key, T>;
    using vector_type = std::vector<value_type>;
    using iterator = typename vector_type::iterator;
    vector_type data_;

  public:
    vector_map() = default;

    template <class... Args> std::pair<iterator, bool> emplace(Args &&...args) {
        data_.emplace_back(std::make_pair(std::forward<Args>(args)...));
        return std::make_pair(data_.end() - 1, true);
    }
};

namespace daw::json {
template <class Key, class T> struct json_data_contract<vector_map<Key, T>> {
    using class_type = vector_map<Key, T>;
    using map_type = std::unordered_map<Key, T>;

    struct map_builder {
        template <typename Iter> constexpr class_type operator()(Iter first, Iter last) const {
            class_type ans;
            for (; first != last; ++first) {
                auto [k, v] = *first;
                ans.emplace(k, v);
            }
            return ans;
        }
    };

    using constructor_t = map_builder;
    using type = json_type_alias<json_key_value_no_name<map_type, Key, T, map_builder>>;
    static inline auto to_json_data(const class_type &val) {
        map_type ans;
        ans.reserve(val.size());
        for (const auto &[k, v] : val) {
            ans.emplace(k, v);
        }
        return ans;
    }
};
} // namespace daw::json

void json_test(const std::string &json_file) {
    std::ifstream t(json_file);
    std::stringstream buffer;
    buffer << t.rdbuf();

    auto data_source = buffer.str();
    std::string_view data_view(data_source.data(), data_source.size());

    auto obj = daw::json::from_json<vector_map<std::string, int>>(data_view);
    auto obj_json = daw::json::to_json(
        obj, daw::json::options::output_flags<daw::json::options::SerializationFormat::Pretty>);

    std::cout << obj_json.c_str() << std::endl;
}

however, I get a compiler error I cannot fully understand. What is the correct way to get this working?

beached added a commit that referenced this issue Jun 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants