Insertar en unordered_map colgado en tiempo de ejecución

Insertar en unordered_map colgado en tiempo de ejecución

La solución para Insertar en unordered_map colgado en el tiempo de ejecución
se proporciona a continuación:

Me encontré con un problema que solo puedo describir como completamente desconocido para mí, y me vendría bien un poco de ayuda. Como contexto, no sé mucho sobre programación en C++ y estoy tratando de aprender más, por lo que el código en el que estoy trabajando actualmente es más educativo que cualquier otra cosa. Quiero crear un unordered_map que contiene una cadena como clave y un vector como valor asociado de la clave. Tradicionalmente, esto sería trivial. Sin embargo, mi programa está tratando de analizar datos json en este unordered_map estructura de datos y no hay garantías de que se conozca de antemano el tipo de cada vector. Lo único que puedo garantizar es que el tipo de cada vector será uno de los tipos en el siguiente conjunto de tipos:string, int, double . Para intentar lograr un mapa_desordenado que funcione de esta manera, intenté usar variant , pero actualmente la implementación de la variante genera un error de tiempo de espera. Espero obtener algún consejo sobre la causa del tiempo de espera e, idealmente, cómo resolverlo.

El código es el siguiente (ejemplo mínimo que replica este problema):


#include <nlohmann/json.hpp>
#include <unordered_map>
#include <variant>
#include <iostream>
#include <string> 

using json = nlohmann::json;

int main() {
// map stores col name as string and vector with col type
    std::unordered_map<std::string, std::vector<std::variant<double, long, std::string>>> mp;

   // input is type nlohmann::json
   json input = "{ "happy": "yes", "pi": 3.141, "t": 1608008400000 }"_json;

   for(auto& el : input.items()) { 
       if (mp.find(el.key()) == mp.end()) {
           std::cout << "trying insertion for key " << el.key() << std::endl;
           mp.insert({ el.key(), std::vector<std::variant<double, long, std::string>>{ el.value() } });
           std::cout << "inserted " << el.key() << " successfully!" << std::endl;
       }
    }
    return 0;
}

Aquí está mi entrada (tenga en cuenta que mi entrada se pasa a este programa como tipo nlohmann::json):
{"c":127.88,"h":127.9,"l":124.13,"n":867462,"o":124.34,"t":1608008400000,"v":157572262.0,"vw":126.5535},{"c":127.81,"h":128.37,"l":126.56,"n":550012,"o":127.41,"t":1608094800000,"v":95913591.0,"vw":127.5459}

Y aquí está la salida actual:

inserted c successfully!
trying insertion for key h
inserted h successfully!
trying insertion for key l
inserted l successfully!
trying insertion for key n
inserted n successfully!
trying insertion for key o
inserted o successfully!
trying insertion for key t
[1]    40305 killed     ./test

Intenté abordar varios problemas potenciales diferentes con respecto a por qué podría estar sucediendo esto, pero esencialmente confirmé mediante prueba y error que el problema ocurre cuando intento usar el std::variant<std::string, long, double> para mi tipo de vector. Cuando asigno a todos los vectores un tipo uniforme (double , por ejemplo), todas las inserciones funcionan perfectamente. Sin embargo, el problema es uno de extensibilidad. Aunque este ejemplo solo contiene dobles y largos, en el futuro me gustaría poder analizar algunos datos que se vean así:
{"a": "test", "b": 1243.343, "c": 120910394023332}
sin error y que los valores devueltos sean (con los tipos que se muestran para mayor claridad):

a : vector<string>{"test"}, b : vector<double>{1243.343}, c : vector<long>{120910394023332}

Si hay algo que pueda aclarar que ayudaría a responder esto, házmelo saber y lo agregaré.

La siguiente declaración de vector:

std::vector<int> v{4};

Esto crea un vector inicializado por defecto con 4 valores. Este std::vector sobrecargado constructor toma un solo parámetro que da el tamaño inicial del vector.

std::vector<std::variant<double, long, std::string>>{ el.value() } }

Teniendo en cuenta mi introducción, ahora debería ser obvio que esto va a invocar al mismo constructor.

Cuando recorrí el código anterior en el depurador, mi depurador reveló el hecho de que las cosas se despegaron rápidamente cuando 1608008400000 pasó al constructor. Las posibilidades de que mi computadora cree con éxito un vector con un billón, seiscientos ocho mil millones, ocho millones y cuatrocientos mil valores, de cualquier cosa, son muy, muy escasas.

el.value() no devuelve una variante. Devuelve un valor JSON y no existe un mecanismo listo para convertirlo en una variante. Tienes que hacer todo el trabajo tú mismo, algo como:

   auto v=el.value();

   if (v.is_number())
   {
       if (v.is_number_float())
       {
           vv.emplace_back( (double)v);
       }
       else
       {
           vv.emplace_back( (long)v);
       }
   }
   else
   {
       vv.emplace_back( (std::string) v);
   }

   mp.insert({ el.key(), vv});

Logré responder mi propia pregunta (aunque de forma indirecta). Puedo verificar si el tipo es entero o no con el is_number_integer() función en nlohmann::json . A partir de ahí, si es un número entero, puedo obtener el uint64_t de él y luego insértelo en el vector variante. La única modificación que tuve que hacer fue alterar el vector variante para cambiar los tipos disponibles eliminando long y añadiendo uint64_t . Aquí está el nuevo código:

int main() {
// map stores col name as string and vector with col type
    std::unordered_map<std::string, std::vector<std::variant<double, uint64_t, std::string>>> mp;

   // input is type nlohmann::json
   // input.push_back(json::object_t::value_type("t", 1608008400000));
   json input = "{ "happy": true, "pi": 3.141, "t": 1608008400000 }"_json;


   for(auto& el : input.items()) {
       if (mp.find(el.key()) == mp.end()) {
            std::cout << "trying insertion for key " << el.key() << std::endl;
            std::cout << "value is " << el.value() << " with type " << el.value().type_name() << std::endl;
            if (el.value().is_number_integer()) {
                mp.insert({ el.key(), std::vector<std::variant<double, uint64_t, std::string>>{ el.value().get<std::uint64_t>() } });
            }
            else {
                mp.insert({ el.key(), std::vector<std::variant<double, uint64_t, std::string>>{ el.value() } });
            }
            std::cout << "inserted " << el.key() << " successfully!" << std::endl;
       }
    }
    return 0;
}