Basic "meta type compiler" for streaming, streaming fixes and test.

This commit is contained in:
Andrzej Kapolka 2013-12-31 15:30:15 -08:00
parent f9043c3cb9
commit effd00a405
9 changed files with 241 additions and 6 deletions

View file

@ -8,6 +8,7 @@
#include <PacketHeaders.h>
#include <MetavoxelMessages.h>
#include <MetavoxelUtil.h>
#include "MetavoxelServer.h"
@ -88,7 +89,9 @@ void MetavoxelSession::sendData(const QByteArray& data) {
}
void MetavoxelSession::readPacket(Bitstream& in) {
qDebug("got packet from client!\n");
QVariant msg;
in >> msg;
qDebug() << msg.value<ClientPositionMessage>().test << "\n";
Bitstream& out = _sequencer.startPacket();
_sequencer.endPacket();

View file

@ -0,0 +1,20 @@
macro(AUTO_MTC TARGET ROOT_DIR)
if (NOT TARGET mtc)
add_subdirectory(${ROOT_DIR}/tools/mtc ${ROOT_DIR}/tools/mtc)
endif (NOT TARGET mtc)
file(GLOB INCLUDE_FILES src/*.h)
add_custom_command(OUTPUT ${TARGET}_automtc.cpp COMMAND mtc -o ${TARGET}_automtc.cpp ${INCLUDE_FILES} DEPENDS mtc)
find_package(Qt5Core REQUIRED)
add_library(${TARGET}_automtc STATIC ${TARGET}_automtc.cpp)
qt5_use_modules(${TARGET}_automtc Core)
target_link_libraries(${TARGET} ${TARGET}_automtc)
endmacro()

View file

@ -12,6 +12,7 @@
#include <SharedUtil.h>
#include <MetavoxelMessages.h>
#include <MetavoxelUtil.h>
#include "Application.h"
@ -193,6 +194,8 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) :
void MetavoxelClient::simulate(float deltaTime) {
Bitstream& out = _sequencer.startPacket();
ClientPositionMessage msg = { 55 };
out << QVariant::fromValue(msg);
_sequencer.endPacket();
}

View file

@ -13,6 +13,9 @@ find_package(Qt5Widgets REQUIRED)
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME})
include(${MACRO_DIR}/AutoMTC.cmake)
auto_mtc(${TARGET_NAME} ${ROOT_DIR})
qt5_use_modules(${TARGET_NAME} Widgets Script)
include(${MACRO_DIR}/IncludeGLM.cmake)

View file

@ -40,6 +40,7 @@ IDStreamer& IDStreamer::operator<<(int value) {
}
IDStreamer& IDStreamer::operator>>(int& value) {
value = 0;
_stream.read(&value, _bits);
if (value == (1 << _bits) - 1) {
_bits++;
@ -93,7 +94,8 @@ Bitstream& Bitstream::read(void* data, int bits, int offset) {
_underlying >> _byte;
}
int bitsToRead = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits));
*dest |= ((_byte >> _position) & ((1 << bitsToRead) - 1)) << offset;
int mask = ((1 << bitsToRead) - 1) << offset;
*dest = (*dest & ~mask) | (((_byte >> _position) << offset) & mask);
_position = (_position + bitsToRead) & LAST_BIT_POSITION;
if ((offset += bitsToRead) == BITS_IN_BYTE) {
dest++;
@ -166,7 +168,7 @@ Bitstream& Bitstream::operator<<(int value) {
}
Bitstream& Bitstream::operator>>(int& value) {
qint32 sizedValue;
qint32 sizedValue = 0;
read(&sizedValue, 32);
value = sizedValue;
return *this;
@ -180,6 +182,7 @@ Bitstream& Bitstream::operator<<(const QByteArray& string) {
Bitstream& Bitstream::operator>>(QByteArray& string) {
int size;
*this >> size;
string.resize(size);
return read(string.data(), size * BITS_IN_BYTE);
}
@ -191,6 +194,7 @@ Bitstream& Bitstream::operator<<(const QString& string) {
Bitstream& Bitstream::operator>>(QString& string) {
int size;
*this >> size;
string.resize(size);
return read(string.data(), size * sizeof(QChar) * BITS_IN_BYTE);
}

View file

@ -13,6 +13,7 @@
#include <QMetaType>
#include <QSharedPointer>
#include <QVariant>
#include <QtDebug>
class QByteArray;
class QDataStream;
@ -278,10 +279,12 @@ public:
#define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \
Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer<x>());
/// Declares the metatype and the streaming operators.
/// Declares the metatype and the streaming operators. The last line
/// ensures that the generated file will be included in the link phase.
#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \
Bitstream& operator<<(Bitstream& out, const X& obj); \
Bitstream& operator>>(Bitstream& in, X& obj);
Bitstream& operator>>(Bitstream& in, X& obj); \
static const int* _TypePtr##X = &X::Type;
/// Registers a streamable type and its streamer.
template<class T> int registerStreamableMetaType() {
@ -290,4 +293,10 @@ template<class T> int registerStreamableMetaType() {
return type;
}
/// Flags a class as streamable (use as you would Q_OBJECT).
#define STREAMABLE public: static const int Type; private:
/// Flags a field or base class as streaming.
#define STREAM
#endif /* defined(__interface__Bitstream__) */

View file

@ -13,9 +13,11 @@
/// A message containing the position of a client.
class ClientPositionMessage {
STREAMABLE
public:
int test;
STREAM int test;
};
DECLARE_STREAMABLE_METATYPE(ClientPositionMessage)

11
tools/mtc/CMakeLists.txt Normal file
View file

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 2.8)
set(TARGET_NAME mtc)
set(ROOT_DIR ../..)
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
include(${MACRO_DIR}/SetupHifiProject.cmake)
setup_hifi_project(${TARGET_NAME} TRUE)

180
tools/mtc/src/main.cpp Normal file
View file

@ -0,0 +1,180 @@
//
// main.cpp
// mtc
//
// Created by Andrzej Kapolka on 12/31/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <iostream>
#include <QFile>
#include <QList>
#include <QRegExp>
#include <QString>
#include <QStringList>
#include <QTextStream>
#include <QtDebug>
using namespace std;
class Class {
public:
QString name;
QStringList bases;
};
class Streamable {
public:
Class clazz;
QStringList fields;
};
void processInput(QTextStream& in, QList<Streamable>* streamables) {
Class clazz;
Streamable currentStreamable;
QRegExp exp(
"(/\\*.*\\*/)|" // multi-line comments
"(//.*\n)|" // single-line comments
"(\\s*#.*\n)|" // preprocessor definitions
"(\\s*STREAMABLE\\s+)|" // STREAMABLE tag for classes
"(\\s*STREAM\\s+.*;)|" // STREAM tag for fields
"(\\s*class\\s+[^;]+\\{)" // class definition
);
exp.setMinimal(true);
QRegExp classExp("class (\\w+) ?:?([^:]*)\\{");
// read in the entire input and look for matches with our expression
QString all = in.readAll();
for (int off = 0; (off = exp.indexIn(all, off)) != -1; off += exp.matchedLength()) {
QString match = exp.cap().simplified();
if (match.startsWith("/*") || match.startsWith("//") || match.startsWith('#')) {
continue; // comment, preprocessor definition
}
if (match.startsWith("STREAMABLE")) {
if (clazz.name.isEmpty()) {
cerr << "Found STREAMABLE marker before class definition." << endl;
continue;
}
if (!currentStreamable.clazz.name.isEmpty()) {
streamables->append(currentStreamable);
}
currentStreamable.clazz = clazz;
currentStreamable.fields.clear();
} else if (match.startsWith("STREAM")) {
match.chop(1); // get rid of the semicolon
match = match.trimmed(); // and any space before it
currentStreamable.fields.append(match.mid(match.lastIndexOf(' ') + 1));
} else { // match.startsWith("class")
classExp.exactMatch(match);
clazz.name = classExp.cap(1);
clazz.bases.clear();
foreach (const QString& bstr, classExp.cap(2).split(',')) {
QString base = bstr.trimmed();
if (!base.isEmpty() && base.startsWith("STREAM")) {
clazz.bases.append(base.mid(base.lastIndexOf(' ') + 1));
}
}
}
}
if (!currentStreamable.clazz.name.isEmpty()) {
streamables->append(currentStreamable);
}
}
void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
foreach (const Streamable& str, streamables) {
const QString& name = str.clazz.name;
out << "Bitstream& operator<< (Bitstream& out, const " << name << "& obj) {\n";
foreach (const QString& base, str.clazz.bases) {
out << " out << static_cast<const " << base << "&>(obj);\n";
}
foreach (const QString& field, str.fields) {
out << " out << obj." << field << ";\n";
}
out << " return out;\n";
out << "}\n";
out << "Bitstream& operator>> (Bitstream& in, " << name << "& obj) {\n";
foreach (const QString& base, str.clazz.bases) {
out << " in >> static_cast<" << base << "&>(obj);\n";
}
foreach (const QString& field, str.fields) {
out << " in >> obj." << field << ";\n";
}
out << " return in;\n";
out << "}\n";
out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n\n";
}
}
int main (int argc, char** argv) {
// process the command line arguments
QStringList inputs;
QString output;
for (int ii = 1; ii < argc; ii++) {
QString arg(argv[ii]);
if (!arg.startsWith('-')) {
inputs.append(arg);
continue;
}
QStringRef name = arg.midRef(1);
if (name == "o") {
if (++ii == argc) {
cerr << "Missing file name argument for -o" << endl;
return 1;
}
output = argv[ii];
} else {
cerr << "Unknown option " << arg.toStdString() << endl;
return 1;
}
}
if (inputs.isEmpty()) {
cerr << "Usage: mtc [OPTION]... input files" << endl;
cerr << "Where options include:" << endl;
cerr << " -o filename: Send output to filename rather than standard output." << endl;
return 0;
}
QList<Streamable> streamables;
foreach (const QString& input, inputs) {
QFile ifile(input);
if (!ifile.open(QIODevice::ReadOnly | QIODevice::Text)) {
cerr << ("Couldn't open " + input + ": " + ifile.errorString()).toStdString() << endl;
continue;
}
QTextStream istream(&ifile);
int oldSize = streamables.size();
processInput(istream, &streamables);
if (streamables.size() == oldSize) {
// no streamables; remove from list
inputs.removeOne(input);
}
}
QFile ofile(output);
if (output.isNull()) {
ofile.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
} else if (!ofile.open(QIODevice::WriteOnly | QIODevice::Text)) {
cerr << ("Couldn't open " + output + ": " + ofile.errorString()).toStdString() << endl;
return 1;
}
QTextStream ostream(&ofile);
ostream << "// generated by mtc\n";
foreach (const QString& input, inputs) {
ostream << "#include \"" << input << "\"\n";
}
generateOutput(ostream, streamables);
return 0;
}