Loki-Astari / ThorsMongo

C++ MongoDB API and BSON/JSON Serialization library
GNU General Public License v3.0
316 stars 72 forks source link

Maximum number of levels/depth? #62

Closed ripe909 closed 4 years ago

ripe909 commented 4 years ago

Is there maximum number of levels that can be encoded in the JSON output?

I am creating a JSON tree structure of vectors and pointers that goes down 9 levels. On the ninth level I have a member std::array. As soon as I jsonExport at that level, the export hangs. If I move the std::array one level up, it works as expected.

class level8_cont
{
public:
    std::array<int, 3> array9 = { 1, 2, 3 };
};
class level7
{
public:
    level7()
    {
        levels8.emplace_back();
    }
    std::vector<level8_cont> levels8;
};
class level6_cont
{
public:
    level6_cont()
    {
        nextLevel7 = new level7();
    }
    level7 *nextLevel7;
};
class level5
{
public:
    level5()
    {
        levels6.emplace_back();
    }
    std::vector<level6_cont> levels6;
};
class level4_cont
{
public:
    level4_cont()
    {
        nextLevel5 = new level5();
    }
    level5 *nextLevel5;
};
class level3
{
public:
    level3()
    {
        levels4.emplace_back();
    }
    std::vector<level4_cont> levels4;
};
class level2_cont
{
public:
    level2_cont()
    {
        nextLevel3 = new level3();
    }
    level3 *nextLevel3;
};
class level1
{
public:
    level1()
    {
        levels2.emplace_back();
    }

    std::vector<level2_cont> levels2;
};

ThorsAnvil_MakeTrait(level1, levels2);
ThorsAnvil_MakeTrait(level2_cont, nextLevel3);
ThorsAnvil_MakeTrait(level3, levels4);
ThorsAnvil_MakeTrait(level4_cont, nextLevel5);
ThorsAnvil_MakeTrait(level5, levels6);
ThorsAnvil_MakeTrait(level6_cont, nextLevel7);
ThorsAnvil_MakeTrait(level7, levels8);
ThorsAnvil_MakeTrait(level8_cont, array9);

using ThorsAnvil::Serialize::jsonExport;
using ThorsAnvil::Serialize::jsonImport;

// Use the export function to serialize
level1 levelTest = level1();
std::cout << jsonExport(levelTest) << "\n";

this gives the (stalled) output:

    { 
        "levels2": [ 
            { 
                "nextLevel3": 
                { 
                    "levels4": [ 
                        { 
                            "nextLevel5": 
                            { 
                                "levels6": [ 
                                    { 
                                        "nextLevel7": 
                                        { 
                                            "levels8": [ 
                                                { 

moving the array9 up a level gives correct output:

class level8_cont
{
public:

};
class level7
{
public:
    level7()
    {
        levels8.emplace_back();
    }
    std::vector<level8_cont> levels8;
    std::array<int, 3> array9 = { 1, 2, 3 };
};
class level6_cont
{
public:
    level6_cont()
    {
        nextLevel7 = new level7();
    }
    level7 *nextLevel7;
};
class level5
{
public:
    level5()
    {
        levels6.emplace_back();
    }
    std::vector<level6_cont> levels6;
};
class level4_cont
{
public:
    level4_cont()
    {
        nextLevel5 = new level5();
    }
    level5 *nextLevel5;
};
class level3
{
public:
    level3()
    {
        levels4.emplace_back();
    }
    std::vector<level4_cont> levels4;
};
class level2_cont
{
public:
    level2_cont()
    {
        nextLevel3 = new level3();
    }
    level3 *nextLevel3;
};
class level1
{
public:
    level1()
    {
        levels2.emplace_back();
    }

    std::vector<level2_cont> levels2;
};

ThorsAnvil_MakeTrait(level1, levels2);
ThorsAnvil_MakeTrait(level2_cont, nextLevel3);
ThorsAnvil_MakeTrait(level3, levels4);
ThorsAnvil_MakeTrait(level4_cont, nextLevel5);
ThorsAnvil_MakeTrait(level5, levels6);
ThorsAnvil_MakeTrait(level6_cont, nextLevel7);
ThorsAnvil_MakeTrait(level7, levels8, array9);
ThorsAnvil_MakeTrait(level8_cont);

using ThorsAnvil::Serialize::jsonExport;
using ThorsAnvil::Serialize::jsonImport;

level1 levelTest = level1();
std::cout << jsonExport(levelTest) << "\n";

which gives this (correct) output:

{ 
        "levels2": [ 
            { 
                "nextLevel3": 
                { 
                    "levels4": [ 
                        { 
                            "nextLevel5": 
                            { 
                                "levels6": [ 
                                    { 
                                        "nextLevel7": 
                                        { 
                                            "levels8": [ 
                                                {
                                                }], 
                                            "array9": [ 1, 2, 3]
                                        }
                                    }]
                            }
                        }]
                }
            }]
    }
Loki-Astari commented 4 years ago

There is no limit on the number of levels.

I built the following:

#include <ThorSerialize/SerUtil.h>
#include <ThorSerialize/JsonThor.h>

class level8_cont
{
    public:
        std::array<int, 3> array9 = { 1, 2, 3 };
};
class level7
{
    public:
        level7()
        {
            levels8.emplace_back();
        }
        std::vector<level8_cont> levels8;
};
class level6_cont
{
    public:
        level6_cont()
        {
            nextLevel7 = new level7();
        }
        level7 *nextLevel7;
};
class level5
{
    public:
        level5()
        {
            levels6.emplace_back();
        }
        std::vector<level6_cont> levels6;
};
class level4_cont
{
    public:
        level4_cont()
        {
            nextLevel5 = new level5();
        }
        level5 *nextLevel5;
};
class level3
{
    public:
        level3()
        {
            levels4.emplace_back();
        }
        std::vector<level4_cont> levels4;
};
class level2_cont
{
    public:
        level2_cont()
        {
            nextLevel3 = new level3();
        }
        level3 *nextLevel3;
};
class level1
{
    public:
        level1()
        {
            levels2.emplace_back();
        }

        std::vector<level2_cont> levels2;
};

ThorsAnvil_MakeTrait(level1, levels2);
ThorsAnvil_MakeTrait(level2_cont, nextLevel3);
ThorsAnvil_MakeTrait(level3, levels4);
ThorsAnvil_MakeTrait(level4_cont, nextLevel5);
ThorsAnvil_MakeTrait(level5, levels6);
ThorsAnvil_MakeTrait(level6_cont, nextLevel7);
ThorsAnvil_MakeTrait(level7, levels8);
ThorsAnvil_MakeTrait(level8_cont, array9);

using ThorsAnvil::Serialize::jsonExport;
using ThorsAnvil::Serialize::jsonImport;

// Use the export function to serialize
int main()
{
    level1 levelTest = level1();
    std::cout << jsonExport(levelTest) << "\n";
}

The I built and run like this:

> brew install thors-serializer
Updating Homebrew...
No stash entries found.
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/core).
==> Updated Formulae
helmfile

==> Installing dependencies for thors-serializer: libyaml
==> Installing thors-serializer dependency: libyaml
==> Downloading https://homebrew.bintray.com/bottles/libyaml-0.2.4.catalina.bottle.tar.gz
######################################################################## 100.0%
==> Pouring libyaml-0.2.4.catalina.bottle.tar.gz
🍺  /usr/local/Cellar/libyaml/0.2.4: 10 files, 322.6KB
==> Installing thors-serializer
==> Downloading https://homebrew.bintray.com/bottles/thors-serializer-1.15.1.catalina.bottle.tar.gz
######################################################################## 100.0%
==> Pouring thors-serializer-1.15.1.catalina.bottle.tar.gz
🍺  /usr/local/Cellar/thors-serializer/1.15.1: 58 files, 919KB
> g++ -O3 -std=c++17 pl.cpp -lThorSerialize17
> ./a.out

    {
        "levels2": [
            {
                "nextLevel3":
                {
                    "levels4": [
                        {
                            "nextLevel5":
                            {
                                "levels6": [
                                    {
                                        "nextLevel7":
                                        {
                                            "levels8": [
                                                {
                                                    "array9": [ 1, 2, 3]
                                                }]
                                        }
                                    }]
                            }
                        }]
                }
            }]
    }
ripe909 commented 4 years ago

OK, I was able to get it working on my build. I am running from an embedded system, so I had to give it some more stack space.

Thanks!

Loki-Astari commented 4 years ago

No problem.
If I may ask are you running the version with pre-built libraries or the header only version? The header only version may result in the compiler being able to optimize out function calls (reduce stack space) at the cost of a slightly larger executable.

Please let me know how the project goes.

Loki

ripe909 commented 4 years ago

Currently I am using the header only version. I had to hack up a few things to get it working on ARM with arm-none-eabi. Specifically, removing the exception throwing, and setup of the config manually.

I am still in debug mode, so I haven't tried any compiler optimizations yet. I had enough RAM to increase my stack space for now.

Loki-Astari commented 4 years ago

Note: there is a header only version available for checkout.

git clone --single-branch --branch header-only https://github.com/Loki-Astari/ThorsSerializer.git

You should not need to modify the config.

Let me know if there might be something I can help fix.