Search code examples
c++jsondynamic-typing

How to implement Javascript-like dynamic typing in C++


I have to write a very basic JSON serializer/deserializer in C++. The goal is to build something similar to this https://github.com/nlohmann/json. Unfortunately I can't use this awesome library because my compiler does not fully support the C++ 11 standard. Switching the compiler is not an option.

I would like to go with an approach close to the following examples. I am writing my idea in some sort of pseudo code, based on the TypeScript Syntax.

I'm imaging a class representing a JSON object, with functions to access it's keys and get some dynamic values from them.

type JSONValue = int | double | string | list | JSONObject | ...;

class JSONObject {
  private map: Map<string, JSONValue>;

  public set(key: string, value: JSONValue) { ... };
  public get(key: string): JSONValue { ... };
  public stringify(): string;
}

I think the general concept should be clear. Parsing a JSON string is not the problem. Creating a JSON string is trivial too. But creating a type like JSONValue in C++ is something I can't wrap my head around.

Probably the way to go are templates, but I need some guidance on how to implement such a "dynamic" type. I would like to get some example on how to approach this problem.

Edit: The code has to work in RAD Studio 10.2.3 with the Clang enhanced compiler. It's hard to tell what this compiler supports so I will test every suggestion and add the limitations to this question as they arise.

Edit 2: Running bcc32x --version yields the following results:

Embarcadero C++ 7.30 for Win32 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
Embarcadero Technologies Inc. bcc32x version 3.3.1 (36355.e71d049.f8c4cf9) (based on LLVM 3.3.1)
Target: i686-pc-win32-omf
Thread model: posix

Solution

  • In your case, you don't actually need fully dynamic typing, as JSONValue can't have any type, just some amount of different types. This is what you would use a union for:

    union JSONValue {
        long long integer;
        double floating;
        std::string string;
        ...
    }
    

    Unions of non-trivially destructible types are however quite unsafe, because you need to make sure that the destructor of string gets called, if string is the active member.

    If you were using C++17, you could use std::variant, which is essentially a type-safe union. Before C++17 (in your case), boost::variant can be used instead.