TextureMind Framework – Progress #1 – Serialization and log

I continued to program the TextureMind Framework and I'm pretty happy with the result. I wish that this framework will give me the chance to increment the production of my software and to save most of the time (because I don't have it). People told me many times to use already existing frameworks to produce my works, and I tried. Most of them are not suitable for what I want to do, or maybe they have issues with the licenses or simply I don't like them. I want to make something new and innovative, and I feel like I'm about to do it.

- Serialization

Let me say that the serialization is a master piece. You can program directly in C++ new classes with a very easy pattern, save and load all the data into four formats: raw (*.raw), interchangable binary (*.tmd), human readable xml (*.xml) and json (*.json).

The most difficult part is to maintain the data hierarchy during the serialization. This is an example of the output produced by a test object in xml:

<example name="test" index="0">
    <mat4x4>
        1 0 0 0 
        0 1 0 0 
        0 0 1 0 
        0 0 0 1 
    </mat4x4>
    <position x="10" y="20" z="30" />
    <axis>
        <item x="0.546535313" y="0.435838997" z="0.698445022" />
        <item x="0.546535313" y="0.435838997" z="0.698445022" />
        <item x="0.546535313" y="0.435838997" z="0.698445022" />
    </axis>
    <node class="Example" name="test2" index="0">
        <vectors>
            <item x="0.546535313" y="0.435838997" z="0.698445022" />
            <item x="0.546535313" y="0.435838997" z="0.698445022" />
            <item x="0.546535313" y="0.435838997" z="0.698445022" />
        </vectors>
    </node>
    <map class="MapOfObjects[String]">
        <objects>
            <item class="Example" key="Key1" name="test3" index="0">
                <mat4x4>
                1 0 0 0 
                0 1 0 0 
                0 0 1 0 
                0 0 0 1 
                </mat4x4>
            </item>
            <item class="Example" key="Key2" name="test2" index="0">
                <position x="10" y="20" z="30" />
            </item>
            <item class="Example" key="Key3" name="test2" index="0">
                <vectors>
                    <item x="0.546535313" y="0.435838997" z="0.698445022" />
                    <item x="0.546535313" y="0.435838997" z="0.698445022" />
                    <item x="0.546535313" y="0.435838997" z="0.698445022" />
                </vectors>
            </item>
        </objects>
    </map>
</example>

As you can see, the object saved many members of different kind, like arrays, vectors of numbers or vectors, lists and maps of other objects that can recursively contain other objects with the same complexity. And this is totally interchangable. For instance, if you saved an old version of the format with a class name that is different than the latest one, the framework will still try to load the file, avoiding what is not recognized and reporting the mismatch as a warning. This is guaranteed even in the binary format, while the raw format that is studied to be faster may end in unexpected behavior.

- Log

I added also a brand new log with different level of severity that can be saved during the application execution. Everything from the framework core to the program implementation will be logged to improve the chance to help users when they report that the application crashed. You have no idea how this is useful when somebody writes an email like "Hey, I tried your software and it crashed. You are scam". You can calm down and answer that you can help him or her if he or she will give you the log produced by the application in the moment of the crash, maybe activating the debug level. The output is something like this:

L(INFO) {P:16700, T:16784} S(44466.253) D(TMD) F(wmain) M(384): atom value = 1
L(INFO) {P:16700, T:16784} S(44466.253) D(TMD) F(wmain) M(384): atom value = 57
L(INFO) {P:16700, T:16784} S(44466.253) D(TMD) F(wmain) M(384): atom value = 38
L(INFO) {P:16700, T:16784} S(44466.253) D(TMD) F(wmain) M(384): atom value = 95
L(INFO) {P:16700, T:16784} S(44466.266) D(TMD) F(wmain) M(16264708): Total amount of allocated memory: 16264708
L(INFO) {P:16700, T:16784} S(44467.300) D(TMD) F(wmain) M(28267048): Total amount of allocated memory: 28267048, memory leak: 12002340
L(INFO) {P:16700, T:16784} S(44467.305) D(TMD) F(wmain) M(12264988): Elapsed time for reading = 1050.809001 ms
L(INFO) {P:16700, T:16784} S(44473.422) D(TMD) F(wmain) M(146482612): Elapsed time for writing = 6109.939768 ms
L(INFO) {P:16700, T:16784} S(44473.425) D(TMD) F(wmain) M(134480272): wmain

This test simply play with atomic counters, load a file allocating an amount of memory, then it doesn't deallocate memory to create a memory leak on purpose, then it reads and writes other resources, allocating even more memory. You can observe all the details in the log file, where L is the level, P the process id, T the thread id, S the time in seconds, D the domain, F the function that called the log instruction, M the memory allocated by the process using custom malloc functions (so it can be different than the overall memory used by the process but it can give a good idea how the program is allocating memory). Maybe I'm crazy, but I find stuff like that pretty satisfactory. This is just an example, but the final product will print ERROR, WARNING, INFO and DEBUG messages, with all the information to understand what happened during the program execution. Even better, the process can capture exceptions and print a special message to report what caused the crash and all the acquired information before exiting.

Leave a Reply