ATDNA
This document provides an overview for using ATDNA. There is also a DNA record reference to use during development.
Contents |
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);
}