This document provides an overview for using ATDNA. There is also a DNA record reference to use during development.

Introduction

Athena’s primary purpose as a core library is to facilitate access and modification of individual data fields in a structured data record; streamed or buffered. Whenever a developer uses Athena on its own, each individual field is read or written using explicitly-typed methods like IStreamReader::readUint32() or IStreamWriter::writeVec3f().

For complex data records with recursive hierarchies or random-access parsing, this code can become rather tedious. Furthermore, separate functions must be maintained for reading and writing. Some code-generation assistance would be useful in these cases.

Overview

atdna is a source-to-source clang tool for transforming a specially-formed template syntax on C++ record-declarations (i.e. Structs, Classes, Unions). The results are fully-implemented methods that form the basis of the ordered read/write flow of the streamed data.

Important details like byte-order (endianness) and contiguous subrecord tables are automatically accounted for by atdna’s parser.

Using

atdna [-o <out.cpp>] [-I <include-search-dir>...] <in.hpp>...

Since atdna is built on LLVM/clang, it functions much like the clang tool itself, with -o <out.cpp> to specify output and one or more standalone args to specify input header files <in.hpp>.... If no output file is specified, a.cpp is emitted in the working directory, overwriting the existing file if any!!

Diagnostics

Clang’s built-in diagnostics engine is used to supply error reports for records that aren’t well-formed for Athena (e.g. missing template parameters, namespace conflicts). Additionally, all C++ syntax issues are diagnosed just like clang would as a normal compiler.

#include <athena/DNA.hpp>

struct DemoRecord : public athena::io::DNA<athena::BigEndian>
{
    DECL_DNA
    struct NotDnaRecord
    {
        atUint32 notUsefulToAtdna;
    };
    Value<atUint32> count;
    Vector<NotDnaRecord, DNA_COUNT(count)> invalidTable;
};
[jacko@ghor Desktop]$ atdna -o demo.cpp demo.hpp 
demo.hpp:11:5: error: Athena error: Unable to use type 'struct
      DemoRecord::NotDnaRecord' with Athena
    Vector<NotDnaRecord, DNA_COUNT(count)> invalidTable;
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Sample Input

#include <athena/DNA.hpp>

struct DemoRecord : 
public athena::io::DNA<athena::BigEndian>
{
    DECL_DNA
    Value<atUint32> myInt;
    Value<float> myFloat;

    Value<atUint32> myCount;
    struct DemoSubRecord : 
    public athena::io::DNA<athena::BigEndian>
    {
        DECL_DNA
        Value<atUint16> myShort;
        Value<atUint8> myChar1;
        Value<atUint8> myChar2;
    };
    Vector<DemoSubRecord, DNA_COUNT(myCount)> mySubRecordArray;
};

Sample Output

/* Auto generated atdna implementation */
#include <athena/Global.hpp>
#include <athena/IStreamReader.hpp>
#include <athena/IStreamWriter.hpp>

#include "demo.hpp"

void DemoRecord::read(athena::io::IStreamReader& 
                      __dna_reader)
{
    __dna_reader.setEndian(athena::BigEndian);
    /* myInt */
    myInt = __dna_reader.readUint32();
    /* myFloat */
    myFloat = __dna_reader.readFloat();
    /* myCount */
    myCount = __dna_reader.readUint32();
    /* mySubRecordArray */
    mySubRecordArray.clear();
    mySubRecordArray.reserve(myCount);
    for (size_t i=0 ; i<(myCount) ; ++i)
    {
        mySubRecordArray.emplace_back();
        mySubRecordArray.back().read(__dna_reader);
    }
}

void DemoRecord::write(athena::io::IStreamWriter& 
                       __dna_writer) const
{
    __dna_writer.setEndian(athena::BigEndian);
    /* myInt */
    __dna_writer.writeUint32(myInt);
    /* myFloat */
    __dna_writer.writeFloat(myFloat);
    /* myCount */
    __dna_writer.writeUint32(myCount);
    /* mySubRecordArray */
    for (auto elem : mySubRecordArray)
        elem.write(__dna_writer);
}

void DemoRecord::DemoSubRecord::read(athena::io::IStreamReader& 
                                     __dna_reader)
{
    __dna_reader.setEndian(athena::BigEndian);
    /* myShort */
    myShort = __dna_reader.readUint16();
    /* myChar1 */
    myChar1 = __dna_reader.readUByte();
    /* myChar2 */
    myChar2 = __dna_reader.readUByte();
}

void DemoRecord::DemoSubRecord::write(athena::io::IStreamWriter& 
                                      __dna_writer) const
{
    __dna_writer.setEndian(athena::BigEndian);
    /* myShort */
    __dna_writer.writeUint16(myShort);
    /* myChar1 */
    __dna_writer.writeUByte(myChar1);
    /* myChar2 */
    __dna_writer.writeUByte(myChar2);
}