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

Need help with to_json for derived class, keep getting "cannot use operator" #1931

Closed
PcChip opened this issue Feb 9, 2020 · 5 comments
Closed
Labels
kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation

Comments

@PcChip
Copy link

PcChip commented Feb 9, 2020

Hello,

I have a two part question:

  1. I'm having trouble accessing the base class from inside the to_json for the derived class, even though I have it set to "friend" everywhere. I got it somewhat working by doing j = (static_cast<Weapon>(weapon)); , is that the expected way to do it?

  2. I'm having trouble with to_json from the derived class, every attempt I make throws the error "cannot use operator[]", or "cannot use at()", even though the exact same code works fine outside of the to_json() function!

I have a base class:

class Weapon
{
    public:
    glm::vec3 firingLocation; //updated every frame with new location
    
    friend void to_json(nlohmann::json& j, const Weapon& weapon); //to access private vars
    friend void from_json(const nlohmann::json& j, Weapon& weapon);//to access private vars

    private:
    glm::vec3 defaultFiringLocation; //location of the barrel in the base/bind pose
};  

and a derived class:

class LaserWeapon :private Weapon
{
public:
	float damagePerTick = 0.0f;
	float burstLength = 0.0f;   //in seconds
	float totalDamage = 0.0f; //total damage if burst is fully applied


	friend void to_json(nlohmann::json& j, const Weapon& weapon); //to access private vars
	friend void from_json(const nlohmann::json& j, Weapon& weapon);//to access private vars
	friend void to_json(nlohmann::json& j, const LaserWeapon& weapon); //to access private vars
	friend void from_json(const nlohmann::json& j, LaserWeapon& weapon);//to access private vars
	friend nlohmann::json;
};

I want to write a to_json function that will produce something similar to this output:

{
    "LaserWeapon",
    {
        "WeaponBase",
        {
		"firingLocation",
		{
			"x": 0.0,
			"y": 0.0,
			"z": 0.0
		}
	},
	"LaserInfo",
	{
		"burstLength", 2.0,
		"totalDamage", 10.0		
	}
    }
}

but I keep getting errors when trying j[] , j.at(), j.emplace etc

when inside the to_json function I can't even use j["test"] = 4; or j.at("test") = 4; without it throwing an error (JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));), but that same code works fine inside of main() - what am I doing wrong?

just to show you what I'm trying, I would expect this to work based on documentation, but this is where I keep getting runtime errors:

void to_json(nlohmann::json& j, const LaserWeapon& weapon)
{
	j=(static_cast<Weapon>(weapon)); //only way I could get private vars from base class to work
	
        j["LaserInfo"] = { {"damagePerTick", weapon.damagePerTick},
			 {"burstLength", weapon.burstLength},
			 {"totalDamage", weapon.totalDamage}
	                         };
}  

however I get "cannot use operator[] with a string argument with " + std::string(type_name())

Thanks for any help you can provide!

Edit:
also just to save some time, I should have specified that I've already implemented the glm::vec3 part and confirmed that works:

namespace glm
{
  void from_json(const nlohmann::json& j, glm::vec3& v3)
  {
  	j.at("x").get_to(v3.x);
  	j.at("y").get_to(v3.y);
  	j.at("z").get_to(v3.z);
  }

  void to_json(nlohmann::json& j, const glm::vec3& v3)
  {
  	j = nlohmann::json{ {"x", v3.x}, {"y", v3.y}, {"z", v3.z} };
  }
}
@PcChip
Copy link
Author

PcChip commented Feb 9, 2020

I think I figured out how to write the json file as I wanted it, but I'm still having trouble reading it back in

This is how I'm writing it out:

void to_json(nlohmann::json& j, const LaserWeapon& weapon)
{
	j = nlohmann::json({ "LaserWeapon", 
			{ "WeaponBase", static_cast<Weapon>(weapon) }, 
			{ "LaserInfo", 
			{"totalDamage", weapon.totalDamage},
			{"burstLength", weapon.burstLength},
			} 
		});

}

and it produces this file:

  
[
    "LaserWeapon",
    [
        "WeaponBase",
        [
            "defaultFiringLocation",
            {
                "x": 2.0,
                "y": 0.0,
                "z": 0.0
            }
        ]
    ],
    [
        "LaserInfo",
        [
            "totalDamage",
            0.0
        ],
        [
            "burstLength",
            0.0
        ]
    ]
]

However when I try to read it back in as laserWeapon = json["LaserWeapon"].get<LaserWeapon>();

I get the same error about operator[]

What would be the correct way to read this back in and fill out the LaserWeapon (and base Weapon) classes?

@nickaein
Copy link
Contributor

The code is creating the JSON as arrays instead of nested objects. For instance, the LaserWeapon doesn't have any child object and it's just an element of top-level array.

You can generate nested objects as demonstrated in README: https://github.com/nlohmann/json#json-as-first-class-data-type

Here is a boiled-down example: https://wandbox.org/permlink/LABEaFLHBIUZMjtQ

@nlohmann nlohmann added the solution: proposed fix a fix for the issue has been proposed and waits for confirmation label Feb 15, 2020
@PcChip
Copy link
Author

PcChip commented Feb 16, 2020

thanks for the tips!

This code:


void to_json(nlohmann::json& j, const LaserWeapon& weapon)
{
  j = nlohmann::json{{ "LaserWeapon",
  		                 { "WeaponBase", static_cast<Weapon>(weapon) },
  		                 { "LaserInfo",
  			                {"totalDamage", weapon.totalDamage},
  			                {"burstLength", weapon.burstLength},
  		                 }
                            }
};

produces this:


[
   [
       "LaserWeapon",
       [
           "WeaponBase",
           [
               "defaultFiringLocation",
               {
                   "x": 0.0,
                   "y": 0.0,
                   "z": 0.0
               }
           ]
       ],
       [
           "LaserInfo",
           [
               "totalDamage",
               0.0
           ],
           [
               "burstLength",
               0.0
           ]
       ]
   ]
]

however I'm really struggling to figure out how to read it back into a LaserWeapon, filling out the base class and the derived class's extra variables... I've tried several methods and most give me an exception about using at() or operator[]

@nickaein
Copy link
Contributor

Again, your code is creating nested arrays which would not be easy to handle. For instance, you have to access x with something probably like:

j[0][1][1][1]["x"]

Instead, you should fix the code to construct nested JSON objects.

Here is a demonstration: https://wandbox.org/permlink/mtQMInu3E2FkmocE

@PcChip
Copy link
Author

PcChip commented Feb 17, 2020

your example on wandbox is perfect, and I think it should be added to the readme/examples to help others

thanks so much for your time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: question 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