JSON for Classic C++
github.com
github.com
https://rawgit.com/miloyip/nativejson-benchmark/master/sampl...
The variance in feature set, design and performance is huge across all of them. I ultimately landed on libjson, written in C: https://github.com/vincenthz/libjson
It does a lot for you, but it notably does not build a tree for you and does not try to interpret numbers, which I found perfect for adding to languages with C FFI that have their own collection and number types. It’s also great for partial parsing if you need to do any sort of streaming.
It looks like this one can’t currently do partial parsing, but it looks great if C++ maps/vectors are your target.
It does no dynamic memory allocation, which is a plus in constrained IoT/embedded applications. But it’s really only a tokenizer. For example, if you want to parse fields out of a map, you have to write your own wrappers to iterate over key/value pairs. Since no data is copied out of the original buffer, all the “tokens” are given as byte offsets and lengths, not null-terminated strings, so you can’t just do printf(“%s”).
If you can’t (or don’t want to) malloc, it gets the job done. Not sure I’d recommend it for other applications though.
E.g., take the JSON:
"\"\uD83D\uDCA9\""
There's no (pointer, length) into that that you can then printf(…, ptr, len); you'd get the escapes, raw.Ofc., there might be situations like debugging where that's fine.
#include <stdio.h>
void main()
{
char buff[10] = {'R', 'o', 'b', 'o', 't', 't', 'y', 'p', 'e', 's'};
printf("=>%.*s<=", 4, ((char *)&buff + 3));
}
prints =>otty<= all the “tokens” are given as byte offsets and lengthsFar better than nicklohmann's monster build times.
https://github.com/nwpierce/jsb
My goal was to convert a stream of JSON to/from a binary stream that is easier to traverse and manipulate.
If you feel this is an issue then why don't you move it to an independent submodule that can be compiled independently? That means you can build it in parallel along with the whole project, and in the end you just link the resulting binaries.
If it’s a header, you necessarily can’t. Header gets included every time you want to compile code that depends on a header.
Compilers may offer precompilation etc but if the code you want to change has direct dependency to a large header you need to recompile all of the dependencies.
This is one of the painpoints C++.
C++ modules are here, unfortunely outside VC++ and clang latest, plus MSBuild or CMake/ninja, they are not an option.
The reason why this is taking such a long time is because the entire approach is a rather drastic change to how C++ compilers usually work, and C++ compilers (or even frontends, such as the stuff used by IDEs) are complicated things that aren't trivial to make major changes to.
Wasn't support for submodules first added to Clang and CMake?
Also, I don't think you fully grasp the scope of this change. Some projects are still stuck on C++1 to avoid having to pay for the technical debt of upgrading the compiler. This is a flag flip away.
For modules, you need to rework your whole build system and Eve rearchitect your projects.
Think about that for a second.
VSCode is never going to be as good, you are better of with Clion then.
As win/mac user Visual Studio is my preferred tool, but in MacOS Clion (with vscode for few random workflow things not supported in Clion) is an adequate replacement (but Visual Studio remains king).
VSCode can be used as an industrial editor if one likes to, but if it does not feel right, it’s not a skill issue.
When I was stuck with C++ codebases that forced me to take a mandatory coffee break every time I needed to run a bit of new code, it made me a little bit insane! Never again.
I'm sorry, this makes no sense at all. Why would anyone write a new server just because a small component was taking a minute to build?
For some contributors, they’ll have a day job, a family and other personal commitments. so writing open source code is a luxury they don’t have a lot of time for. I know this because I fall exactly into that camp myself.
If original decision process was indeed “less keystrokes”, then how is that not a constructive criticism?
And if you felt their comment was acceptable then I question how much you’ve contributed to open source yourself. Snarky comments like jarts are all too common and really demotivate people from maintaining popular projects.
But don’t just take my word on it, there’s a plethora of other contributors who’ve talked about this topic as well.
Compile times are a big deal, and 'jart is right about individual vs. collective problems. And unlike most other critics on the Internet, 'jart actually provided a solution along with the criticism. If that kind of behavior "demotivates [some] people from maintaining popular projects", I still feel it's a net win.
I have far, far, FAR stronger words for “people” who don’t respect other people’s time by not caring about compile times. Words that would make Linus blush.
So my point stands.
If you focused your comment purely on the technology rather than making ad hominem attacks to the contributor then we wouldn’t need to be having this conversation to begin with.
At the very least, giving the original developer the benefit of doubt, or assuming their decision made sense under the circumstances they were in at the time, is IMO a better start than just public criticism.
So I guess in a JSON-heavy code base or a code base where nlohmann/json has leaked into common headers, you may end up recompiling the library a few dozen times per build where a few dozen of your C++ source files must be recompiled (e.g due to common header changes)...
(But don't worry, the linker will then spend a bunch of time throwing away almost all of that work so you only get one copy of the library in your binary)
I'm sure the authors are brilliant in many ways, but I suspect there's room for improvement in this area.
The moment you switch branches - it changes.
If you develop for Android - it generates build for with hash name from some CMake/Gradle variables, the moment one of those changes (like AGP version) you get a new build dir and essentially have to compile from scratch.
We, and majority of Android projects, aren’t on Bazel, though.
Bazel comes with its own bag of sharp edges though so it's unfortunately not like you can just adopt it and be on your merry way.
Compiler time is way more than a "developer problem". It's an operational problem that ends up permeating to software architecture and development practices, and ultimately affects how the whole project is delivered and deployed.
A nice interface is agreable, but maybe there are diminishing returns when you pay it with large compile time. I remember pondering about that when working with the Eigen math library, which is very nice but such a resource hog when you compile a project using it.
As an aside, I wonder: what are the ThomPike* set of macros actually doing in jart's implem ?
Also, a speed comparison of this vs the other one would be very welcome: conformance and simplicity are certainly important criteria when picking a JSON parser, but speed is rather crucial.
It seems to me though that if you're encountering the edges of json where nlohmann or simple parsing doesn't work properly, a binary format might be better. And if you're trying to serialize so much data that speed actually becomes an issue, then again, binary format might be what you really want.
The killer feature of nlohmann are the the NLOHMANN_DEFINE_TYPE_INTRUSIVE or NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE macros that handle all of the ??? -> json -> ??? steps for you. That alone make it my default go to unless the above reasons force me to go another direction.
Nlohmann is the slowest out of the popular libraries, AFAIK, and not particularly more usable than rapidjson, in my experience. So "better than nlohmann" is not very novel.
I loved the interface and its exactly how I would've designed a json library with modern c++.
Just maybe turn off the implicit conversion option, that can get a bit messy ;)
The Makefile could need some work:
json_test.cpp:360:23: warning: missing terminating '"' character [-Winvalid-pp-token]
{ Json::success, R"({
^
fatal error: too many errors emitted, stopping now [-ferror-limit=]
9 warnings and 20 errors generated.
make: *** [json_test.o] Error 1
% c++ --version
Apple clang version 15.0.0 (clang-1500.1.0.2.5)
Target: arm64-apple-darwin22.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
Compiling direclty with c++ --std=c++11 -c json.cpp
works fine, though.The era of so-called “modern” C++ started with C++11, which was a radical reworking of the language. All prior versions of C++ are “legacy” or “classic”. Idiomatic code in “modern” and “classic” dialects almost look like different languages.
C++20 arguably marks a new dialect break but it doesn’t have a colloquial label to distinguish it from “legacy” and “modern” AFAIK. Idiomatic C++20 looks pretty foreign from a C++11 perspective (but is unambiguously an improvement).
I believe it does require C++11, due to std::nullptr_t and r-value references (&&), but that might be it. It's not a show stopper though since everyone should have a c++11 compiler now (even Ubuntu 14.04 LTS, which still has paid support I believe).
> One thing I like about the C++11 compilers like GCC 4.9 is they build code magnificently faster than recent editions
Kind of reminds me of gcc 2.95 which people kept around for the compiler speed. They would use gcc 3.x for the warning support and then compile with gcc 2.95 after fixing the warnings :).
I think the point of pointing out it's C++11 is that it's not "classic C++" as it's using "modern C++" features. Thus it's a mystery why it would be referred to as classic C++.
Actually, it does. I mean, does it compile when you pass -std=c++98?
> This library was originally written in C.
Doesn't matter. If it uses C++11 features, it's C++11.
> I feel perfectly comfortable calling C++11 "classic" or even "baroque" compared to what people are doing with C++ in 2024.
Irrelevant. You can go the Humpty Dumpty way as far as you want to go and call anything any way. It doesn't matter. If you use C++11 features, it's C++11. If it's C++11 then you're discussing modern C++. You don't need to use all bells and whistles to quality.
And don't forget that C++03 TR1 also added a bunch of very useful stuff - most notably, std::shared_ptr and std::function. And, of course, Boost has been a thing long before C++11, filling many gaps for "modern C++" projects of the time.
"classic C++" from that perspective is C++ written more or less Java-style.
Some of the key differences are use of standard library and its containers, smart pointers, and other language features that look less like C. In this specific library, this refers to some of the techniques like bit manipulation, manual memory management and string parsing, and using things like enums to improve speed and reduce complexity.
An example of a more robust (but still "classic") library would be something like https://github.com/Tencent/rapidjson.
Overloading from_json to modularize parsing is really useful, I think that should be a part of every templated C++ json parser library.
That said, I have seen these ThomPike* macros in cosmopolitan.h before, I wonder what the origin is.
The response doesn't tell you the location of the problem in the input.
We are not living in 90s anymore..