Allegro.cc - Online Community
Post Reply

Allegro.cc Forums » Game Design & Concepts » Overly complicated

rss feed Print
Overly complicated
DanielH
Member #934
January 2001
avatar

Just looking for advice on what to do.

Years ago, I wrote a library with an XML parser that would take a file and create the application as it was parsed. It would create the display, event queue, timers, etc. I wanted something I could change the conditions without recompiling. It worked very well.

I got the bug again and wanted to continue where I left off. However, with all the changes to C++ since, it doesn't work anymore. I could revert to an older C++, but instead I started over.

I didn't want to rewrite the xml parser again so I was looking for alternatives. XML, YAML, JSON, ???

I went with JSON and found a light-weight parser. Very light-weight.

I already reported a bug with parsing escape characters and I have no way of checking if an object is an array or a set of objects themselves. Even an extra comma in the wrong place crashes the parser with no indication where the issue lies.

Otherwise, the parser is great. I did modify it some. I put it all in its own namespace and removed the RSJ prefix.

What to do? continue, find a different parser, attempt to modify his parser?

I could try to add some functions like is_array or is_object. But his code is a bit messy. I hate messy code.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Dizzy Egg
Member #10,824
March 2009
avatar

For Json I usually use quicktype purely because if you can pump the Json into the left hand window and then have all the needed code generated for you (or just the parser); although I only use it with C# which is a lot more straightforward (IMHO) than implementing it in C++.

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Mark Oates
Member #1,146
March 2001
avatar

I use (and even donate to) https://github.com/nlohmann/json for JSON. It's header-only, no library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. It's insanely well-tested, and is full of features. It's one of my favorite libraries on the internet as an example of what great software design looks like in terms of expected features, error messages, simplicity, test coverage, etc.

To convert or parse your custom object to/from JSON, you would write to_json and from_json functions. Note if the object you are parsing is in a namespace, these functions you create must also be in that namespace.

Here's an example going to and from an ALLEGRO_COLOR:

#SelectExpand
1 2#include <lib/nlohmann/json.hpp> 3#include <allegro5/allegro.h> 4 5 6void to_json(nlohmann::json& j, const ALLEGRO_COLOR& color) 7{ 8 j = nlohmann::json{ 9 {"r", color.r}, 10 {"g", color.g}, 11 {"b", color.b}, 12 {"a", color.a}, 13 }; 14} 15 16 17void from_json(const nlohmann::json& j, ALLEGRO_COLOR& color) 18{ 19 j.at("r").get_to(color.r); 20 j.at("g").get_to(color.g); 21 j.at("b").get_to(color.b); 22 j.at("a").get_to(color.a); 23}

With those functions, you might save/load the data through files like this:

#SelectExpand
1 2#include <fstream> // for std::ofstream, std::ifstream 3#include <iostream> // for std::cout 4 5 6void write_to_file(std::string json_filename, ALLEGRO_COLOR color) 7{ 8 std::ofstream outfile; 9 outfile.open(json_filename, std::ofstream::out); 10 nlohmann::json result; 11 12 result["my_color"] = color; 13 14 outfile << std::setw(3) << result << std::endl; 15 outfile.close(); 16} 17 18 19ALLEGRO_COLOR read_from_file(std::string json_filename) 20{ 21 std::ifstream infile(json_filename); 22 nlohmann::json j; 23 ALLEGRO_COLOR result_color; 24 25 infile >> j; 26 27 if (!j.contains("my_color")) 28 { 29 std::cout << "Expecting \"" << json_filename << "\" to contain an object named \"my_color\" but it does not exist." << std::endl; 30 return ALLEGRO_COLOR{0, 0, 0, 0}; 31 } 32 else 33 { 34 j.at("my_color").get_to(result_color); 35 } 36 37 return result_color; 38} 39 40 41int main(int argc, char **argv) 42{ 43 std::string my_filename = "foobar.json"; 44 ALLEGRO_COLOR my_color = ALLEGRO_COLOR{0.2, 0.6, 0.4, 1.0}; 45 46 write_to_file(my_filename, my_color); 47 ALLEGRO_COLOR color_as_read_from_file = read_from_file(my_filename); 48 49 std::cout << "My color, loaded from a JSON file is:" << std::endl 50 << " r: " << my_color.r << std::endl 51 << " g: " << my_color.g << std::endl 52 << " b: " << my_color.b << std::endl 53 << " a: " << my_color.a << std::endl 54 ; 55 56 return 0; 57}

Running the program, your output might look like this:

My color, loaded from a JSON file is:
  r: 0.2
  g: 0.6
  b: 0.4
  a: 1

and the foobar.json file that was written might look like this:

{
   "my_color": {
      "a": 1.0,
      "b": 0.4000000059604645,
      "g": 0.6000000238418579,
      "r": 0.20000000298023224
   }
}

Peter Hull
Member #1,136
March 2001

I also have used nlohmann json. It's very good, I recommend it. TinyXML2 also works nicely if you want to stick to XML.

DanielH said:

However, with all the changes to C++ since, it doesn't work anymore.

This surprises me; compatibility is one of the things C++ tries very hard to do

DanielH
Member #934
January 2001
avatar

I'll look into nlohmann. the JSON library I'm using doesn't have write capability.
Not fond of the from_json with void return. What if there was an issue grabbing from the file?

This surprises me; compatibility is one of the things C++ tries very hard to do

My library was written ~ 2014 and had a few errors with certain functionality removed. Next chance I get I'll see what they were to give you a better picture of what I mean.

Plus, while I was looking at the code to see if I could fix it, I decided I wasn't happy with it. The library was bulky. I am on the fence on what is deemed "better" practice. I know it's subjective. Do I add bulky object functionality or keep them barebone? I'm leaning towards barebone at the moment.

Plus with all the new functionality, I thought I could make it better/cleaner.

Mark Oates
Member #1,146
March 2001
avatar

DanielH said:

Not fond of the from_json with void return. What if there was an issue grabbing from the file?

to_json and from_json don't grab from a file. The file interfacing is entirely up to you to implement if you wish.

If you want to be certain that the fields you expect exist in the JSON object, you can check with .contains("my_color"). You can then handle it however you want.

If there's an error parsing JSON from an invalid JSON string, then nolhmann will raise a parse error, which you can catch. It has detailed information. For example, if I were to parse a string containing this invalid JSON:

   my_color": {
      "a": 1.0,
      "b": 0.4000000059604645,
      "g": 0.6000000238418579,
      "r": 0.20000000298023224
   }
}

It would throw this error:

terminating with uncaught exception of type nlohmann::detail::parse_error: [json.exception.parse_error.101] parse error at line 2, column 4: syntax error while parsing value - invalid literal; last read: '<U+000A>   m'

DanielH
Member #934
January 2001
avatar

Made the plunge. Took a few hours, but have everything converted over.

There was even a NuGet package for it.

A bit of a learning curve, and I documentation is overwhelming.

Thanks,

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

DanielH
Member #934
January 2001
avatar

No, not quite useful.

Years ago I was messing with android programming. They use a manifest.xml that specifies any addons, app type, etc.

I thought it was a good way to initialize an Allegro program. Easier for the end-user. It parses a file and does all the background work (initializes addons, creates a display, event queue, and any timers). Also load any initial objects (bitmaps, fonts, etc).

I have a series of functions that parses json objects.

ALLEGRO_BITMAP* parse(json data);
// etc

I also have a resource handler that manages those objects. If I need an object, I just grab it. If it doesn't exists then it loads it for me.

//  set 'keep' to true if the handler manages deletion
ALLEGRO_BITMAP *grab(handler *h, const std::string& id, bool keep);

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

DanielH
Member #934
January 2001
avatar

I thought about it, but wasn't sure if nesting is possible. That's why I originally went with xml.

json is simple. just a load of pairs with a key and an object, which could be a set of objects {}, an array [] or a value "".

Here is my manifest.json file. I like the simplicity and cleaner look over xml. The only thing is missing is ability to add comments. I could add a comment object and make sure my program ignores it.

#SelectExpand
1{ 2 "addon": 3 { 4 "audio": false, 5 "audio_codecs": false, 6 "color": true, 7 "font": true, 8 "font_ttf": true, 9 "image": true, 10 "memfile": false, 11 "native": true, 12 "physics_fs": false, 13 "primitives": true, 14 "video_streaming": false 15 }, 16 "application": 17 { 18 "display": 19 { 20 "id": "main", 21 "title": "@text/title", 22 "icon": "@image/icon", 23 "width": "@config/display_width", 24 "height": "@config/display_height", 25 "background": "@color/red", 26 "flags": "windowed|resizable", 27 "options": 28 [ 29 { 30 "option": "single_buffer", 31 "value": 1, 32 "importance": "suggest" 33 } 34 ] 35 }, 36 "timer": 37 [ 38 { 39 "id": "logic", 40 "type": "timer", 41 "speed": 60.0 42 }, 43 { 44 "id": "tick", 45 "type": "timer", 46 "speed": 100.0 47 } 48 ], 49 "eventqueue": 50 { 51 "id": "main", 52 "source": 53 [ 54 { 55 "display": "@display/main" 56 }, 57 { 58 "timer": "@timer/logic" 59 }, 60 { 61 "timer": "@timer/tick" 62 }, 63 { 64 "input": 65 { 66 "keyboard": true, 67 "mouse": true 68 } 69 } 70 ] 71 }, 72 "image": 73 [ 74 "@image/buffer" 75 ], 76 "font": 77 [ 78 "@font/main" 79 ] 80 }, 81 "path": 82 { 83 "color": "data/colors/", 84 "config": "data/config/", 85 "font": "data/fonts/", 86 "image": "data/images/", 87 "map": "data/maps/", 88 "text": "data/text/" 89 }, 90 "filename": 91 { 92 "color": "@path/color/color.json", 93 "config" : "@path/config/config.json", 94 "font": "@path/font/font.json", 95 "map": "@path/map/map.json", 96 "image": "@path/image/image.json", 97 "text": "@path/text/text.json" 98 } 99}

amarillion
Member #940
January 2001
avatar

I'll check out nlohmann's library. I like that it's header only, that's a good call.

So far I've used my own JSON parser, mainly because I didn't want to bother with compiling yet another dependency. Dependencies are such a pain in C++, to the point where you prefer to write your own code rather than deal with that.

My own implementation is tested well enough. So far it works with all the JSON I've thrown at it :)

GullRaDriel
Member #3,861
September 2003
avatar

For JSON in C, I use cJSON, and it's pretty good IMHO. (you can find it here: https://github.com/DaveGamble/cJSON )

I recently discovered that KAFKA's librdkafka is also using it (while building on RedHat 7, because the provided librdkafka is way too old, like from 2018).

That aside, it fits my needs, which are maybe simpler than others. cJSON is just a header and a c file.

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

Post Reply
Go to: