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

Using fifo_map with base class and derived class #2023

Closed
theresa95 opened this issue Apr 1, 2020 · 3 comments
Closed

Using fifo_map with base class and derived class #2023

theresa95 opened this issue Apr 1, 2020 · 3 comments
Labels
kind: question solution: duplicate the issue is a duplicate; refer to the linked issue instead solution: proposed fix a fix for the issue has been proposed and waits for confirmation

Comments

@theresa95
Copy link

theresa95 commented Apr 1, 2020

Hey @nlohmann and @Daniel599 !

I am currently trying to generate JSON with ordered keys and therefore used the workaround method from @Daniel599 . I just copy-pasted your workaround code and it worked as aspected. However, if I try to use it within my base and derived classes, I get an error which I do not really understand. It seems like it fails to call the to_Json methods (because the error appears if I try to map a DerivedClass-instance (test and test2) to my_json.

I have already tried the example without ordered keys (just by using json = nlohmann::json;) and it works completely fine. The keys in the output are sorted alphabetically and looks like this:

{
  "cbor": "cbortest",
  "diagnostic": "diagnose: corona",
  "header": {
    "headerId": 3,
    "timestamp": "2019-12-10T16:04:00.00Z",
    "version": "4.0.0"
  },
  "hex": "00f2",
  "roundtrip": true
}

What I am trying to achieve through using the nlohmann fifo_map is to keep the insertion order and the final output therefore should look like this:

{
  "header": {
    "headerId": 3,
    "timestamp": "2019-12-10T16:04:00.00Z",
    "version": "4.0.0"
  },
  "cbor": "cbortest",
  "hex": "00f2",
  "roundtrip": true,
  "diagnostic": "diagnose: corona"
}

Executing the following code outputs two errors:

Error C2440: 'initializing': cannot convert from 'BaseNamespace::SubNamespace::DerivedClass' to 'nlohmann::basic_json<my_workaround_fifo_map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>'	; in file: main.cpp

image

Please have a look at the following code:

In BaseClass.h:

#ifndef BASECLASS_H
#define BASECLASS_H

#include <stdint.h>
#include <string>
#include "nlohmann/json.hpp"
#include "fifo_map.hpp"

namespace BaseNamespace{

	namespace SubNamespace{

		class BaseClass {
		public:
			BaseClass () {};
			virtual ~BaseClass () {};

			uint32_t getHeaderId() const { return headerId; };
			std::string getTimestamp() const { return timestamp; };
			std::string getVersion() const { return version; };

			void setHeaderId(uint32_t str) { headerId = str; };
			void setTimestamp(std::string str) { timestamp = str; };
			void setVersion(std::string bo) { version = bo; };

			void setHeader(UAgvHeader const& header) {
				setHeaderId(header.getHeaderId());
				setTimestamp(header.getTimestamp());
				setVersion(header.getVersion());
			}

		private:
			uint32_t    headerId;       
			std::string timestamp;     
			std::string version;        
		};

		// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
		using namespace nlohmann;
		template<class K, class V, class dummy_compare, class A>
		using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
		using my_json = basic_json<my_workaround_fifo_map>;

		void to_json(my_json &j, const BaseClass &p)
		{			
			j = my_json{
				{ "headerId", p.getHeaderId() }, 
				{ "timestamp", p.getTimestamp() }, 
				{ "version", p.getVersion() }
			};
		}

		void from_json(const my_json &j, BaseClass &p)
		{
			p.setHeaderId(j.at("headerId").get< std::uint32_t>());
			p.setTimestamp(j.at("timestamp").get< std::string >());
			p.setVersion(j.at("version").get<std::string>());
		}

	} // namespace SubNamespace

} // namespace BaseNamespace

#endif // BASECLASS_H_

In DerivedClass.h:

#ifndef DERIVEDCLASS_H
#define DERIVEDCLASS_H

#include <stdint.h>
#include <string>
#include "nlohmann/json.hpp"
#include <optional>
#include "BaseClass.h"

namespace BaseNamespace{

	namespace SubNamespace{

		class DerivedClass : public BaseClass {
		public:

			std::string getCBor() const { return cbor; };
			std::string getHex() const { return hex; };
			bool getRoundtrip() const { return roundtrip; };
			std::optional<std::string> getDiagnostic() const { return diagnostic; };

			void setCBor(std::string str) { cbor = str; };
			void setHex(std::string str) { hex = str; };
			void setRoundtrip(bool bo) { roundtrip = bo; };
			void setDiagnostic(std::optional<std::string> opt_str) { diagnostic = opt_str; };

		private:
			std::string cbor;
			std::string hex;
			bool        roundtrip;
			std::optional<std::string> diagnostic = std::nullopt;
		};
		
		// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
		using namespace nlohmann;
		template<class K, class V, class dummy_compare, class A>
		using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
		using my_json = basic_json<my_workaround_fifo_map>;

		void to_json(my_json &j, const DerivedClass& p)
		{
			j["header"] = static_cast<BaseClass>(p);
			j["cbor"] = p.getCBor();
			j["hex"] = p.getHex();
			j["roundtrip"] = p.getRoundtrip();

			// assuming you only want a "diagnostic" key if there is an actual value;
			// if not, store a nullptr and adjust the from_json accordingly
			if (p.getDiagnostic() != std::nullopt)
			{
				j["diagnostic"] = p.getDiagnostic().value();
			}
		}

		void from_json(const my_json &j, DerivedClass&p)
		{
			p.setHeader(j.at("header").get<BaseClass>());
			p.setCBor(j.at("cbor").get< std::string >());
			p.setHex(j.at("hex").get< std::string >());
			p.setRoundtrip(j.at("roundtrip").get< bool >());

			// if we also allow "null" values, then we need to add an "is_string()"
			// check
			if (j.count("diagnostic") != 0)
			{
				p.setDiagnostic(j.at("diagnostic").get< std::string >());
			}
		}

	} // namespace SubNamespace

} // namespace BaseNamespace

#endif // DERIVEDCLASS_H

In main.cpp:

#include <iostream>
#include <string>
#include <nlohmann/json.hpp>
#include <iomanip> 
#include <optional>
#include "DerivedClass.h"

using namespace nlohmann;
// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;

int main(int argc, char* argv[]) {

	BaseNamespace::SubNamespace::DerivedClass test;
	test.setHeaderId(3);
	test.setTimestamp("2019-12-10T16:04:00.00Z");
	test.setVersion("4.0.0");
	test.setCBor("cbortest");
	test.setHex("00f2");
	test.setRoundtrip(true);
	test.setDiagnostic("diagnose: corona");
	my_json j = test;                                  // ERROR: no suitable conversion
	std::cout << std::setw(2) << j << std::endl;

	std::string str = R"({"header":
        {   "headerId" : 4711,
            "timestamp" : "1 Uhr",
		"version" : "5.0.0" 
        },
        "cbor" : "+X4A",
        "hex" : "f97e00",
        "roundtrip" : true,
        "diagnostic" : "NaN"
        })";

	my_json j2 = my_json::parse(str);
	BaseNamespace::SubNamespace::DerivedClass test2 = j2;
	my_json k = test2;                                 // ERROR: no suitable conversion

	std::cout << std::setw(2) << k << std::endl;


	return 0;
}
@nlohmann
Copy link
Owner

I looked into this and could reproduce the issue, but I have not understood why the conversion does not work.

@nlohmann nlohmann added the state: help needed the issue needs help to proceed label Apr 11, 2020
@Daniel599
Copy link
Contributor

Isn't it the same issue as in this post?
Seems like deja vu.
The fix is already posted in here
Anyway i`ll re-post it here:

after some investigation I found out the cause of your error is due to multiple "my_json" types: one inside BaseNamespace::SubNamespace::my_json and the other in the global scope.
I don't know if it's the same case in your original code, but I mange to fix it by doing the following:

  • remove using namespace nlohmann; within your namespace (e.g inside BaseClass.h & DerivedClass.h)
  • define my_json in single place at BaseClass.h
    after doing the steps above, your code example compiles on g++ 7.5 with -std=c++17, I hope it compiles on windows as well (if not let me know)
namespace BaseNamespace{
	namespace SubNamespace{
// Other code
		// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
		template<class K, class V, class dummy_compare, class A>
		using my_workaround_fifo_map = nlohmann::fifo_map<K, V, nlohmann::fifo_map_compare<K>, A>;
		using my_json = nlohmann::basic_json<my_workaround_fifo_map>;
	}
}

@nlohmann
Copy link
Owner

@Daniel599 Thanks!

@nlohmann nlohmann added solution: duplicate the issue is a duplicate; refer to the linked issue instead solution: proposed fix a fix for the issue has been proposed and waits for confirmation and removed state: help needed the issue needs help to proceed labels Apr 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: question solution: duplicate the issue is a duplicate; refer to the linked issue instead solution: proposed fix a fix for the issue has been proposed and waits for confirmation
Projects
None yet
Development

No branches or pull requests

3 participants