mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 23:49:55 +02:00
Working on tests for datagram sequencing, added autogenerated ==/!= operators
to mtc.
This commit is contained in:
parent
d5b8d1c0d6
commit
524ceb4ed2
10 changed files with 414 additions and 3 deletions
|
@ -39,4 +39,5 @@ add_subdirectory(assignment-client)
|
||||||
add_subdirectory(domain-server)
|
add_subdirectory(domain-server)
|
||||||
add_subdirectory(interface)
|
add_subdirectory(interface)
|
||||||
add_subdirectory(pairing-server)
|
add_subdirectory(pairing-server)
|
||||||
add_subdirectory(voxel-edit)
|
add_subdirectory(tests)
|
||||||
|
add_subdirectory(voxel-edit)
|
||||||
|
|
|
@ -82,6 +82,14 @@ bool AttributeValue::operator==(void* other) const {
|
||||||
return _attribute && _attribute->equal(_value, other);
|
return _attribute && _attribute->equal(_value, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AttributeValue::operator!=(const AttributeValue& other) const {
|
||||||
|
return _attribute != other._attribute || (_attribute && !_attribute->equal(_value, other._value));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AttributeValue::operator!=(void* other) const {
|
||||||
|
return !_attribute || !_attribute->equal(_value, other);
|
||||||
|
}
|
||||||
|
|
||||||
OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) :
|
OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) :
|
||||||
AttributeValue(attribute, value) {
|
AttributeValue(attribute, value) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,9 @@ public:
|
||||||
bool operator==(const AttributeValue& other) const;
|
bool operator==(const AttributeValue& other) const;
|
||||||
bool operator==(void* other) const;
|
bool operator==(void* other) const;
|
||||||
|
|
||||||
|
bool operator!=(const AttributeValue& other) const;
|
||||||
|
bool operator!=(void* other) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
AttributePointer _attribute;
|
AttributePointer _attribute;
|
||||||
|
|
|
@ -415,12 +415,16 @@ public:
|
||||||
#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \
|
#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \
|
||||||
Bitstream& operator<<(Bitstream& out, const X& obj); \
|
Bitstream& operator<<(Bitstream& out, const X& obj); \
|
||||||
Bitstream& operator>>(Bitstream& in, X& obj); \
|
Bitstream& operator>>(Bitstream& in, X& obj); \
|
||||||
|
bool operator==(const X& first, const X& second); \
|
||||||
|
bool operator!=(const X& first, const X& second); \
|
||||||
static const int* _TypePtr##X = &X::Type;
|
static const int* _TypePtr##X = &X::Type;
|
||||||
#else
|
#else
|
||||||
#define STRINGIFY(x) #x
|
#define STRINGIFY(x) #x
|
||||||
#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \
|
#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \
|
||||||
Bitstream& operator<<(Bitstream& out, const X& obj); \
|
Bitstream& operator<<(Bitstream& out, const X& obj); \
|
||||||
Bitstream& operator>>(Bitstream& in, X& obj); \
|
Bitstream& operator>>(Bitstream& in, X& obj); \
|
||||||
|
bool operator==(const X& first, const X& second); \
|
||||||
|
bool operator!=(const X& first, const X& second); \
|
||||||
static const int* _TypePtr##X = &X::Type; \
|
static const int* _TypePtr##X = &X::Type; \
|
||||||
_Pragma(STRINGIFY(unused(_TypePtr##X)))
|
_Pragma(STRINGIFY(unused(_TypePtr##X)))
|
||||||
#endif
|
#endif
|
||||||
|
|
10
tests/CMakeLists.txt
Normal file
10
tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
|
# add the test directories
|
||||||
|
file(GLOB TEST_SUBDIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)
|
||||||
|
foreach(DIR ${TEST_SUBDIRS})
|
||||||
|
if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${DIR})
|
||||||
|
add_subdirectory(${DIR})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
32
tests/metavoxels/CMakeLists.txt
Normal file
32
tests/metavoxels/CMakeLists.txt
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
|
set(TARGET_NAME metavoxel-tests)
|
||||||
|
|
||||||
|
set(ROOT_DIR ../..)
|
||||||
|
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
||||||
|
|
||||||
|
# setup for find modules
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
|
||||||
|
|
||||||
|
find_package(Qt5Network REQUIRED)
|
||||||
|
find_package(Qt5Script REQUIRED)
|
||||||
|
find_package(Qt5Widgets REQUIRED)
|
||||||
|
|
||||||
|
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
||||||
|
setup_hifi_project(${TARGET_NAME} TRUE)
|
||||||
|
|
||||||
|
include(${MACRO_DIR}/AutoMTC.cmake)
|
||||||
|
auto_mtc(${TARGET_NAME} ${ROOT_DIR})
|
||||||
|
|
||||||
|
qt5_use_modules(${TARGET_NAME} Network Script Widgets)
|
||||||
|
|
||||||
|
#include glm
|
||||||
|
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||||
|
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||||
|
|
||||||
|
# link in the shared libraries
|
||||||
|
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||||
|
link_hifi_library(metavoxels ${TARGET_NAME} ${ROOT_DIR})
|
||||||
|
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||||
|
|
||||||
|
|
178
tests/metavoxels/src/MetavoxelTests.cpp
Normal file
178
tests/metavoxels/src/MetavoxelTests.cpp
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
//
|
||||||
|
// MetavoxelTests.cpp
|
||||||
|
// metavoxel-tests
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 2/7/14.
|
||||||
|
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <DatagramSequencer.h>
|
||||||
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
|
#include "MetavoxelTests.h"
|
||||||
|
|
||||||
|
MetavoxelTests::MetavoxelTests(int& argc, char** argv) :
|
||||||
|
QCoreApplication(argc, argv) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static int highPriorityMessagesSent = 0;
|
||||||
|
static int unreliableMessagesSent = 0;
|
||||||
|
static int unreliableMessagesReceived = 0;
|
||||||
|
|
||||||
|
bool MetavoxelTests::run() {
|
||||||
|
|
||||||
|
qDebug() << "Running metavoxel tests...";
|
||||||
|
|
||||||
|
// seed the random number generator so that our tests are reproducible
|
||||||
|
srand(0xBAAAAABE);
|
||||||
|
|
||||||
|
// create two endpoints with the same header
|
||||||
|
QByteArray datagramHeader("testheader");
|
||||||
|
Endpoint alice(datagramHeader), bob(datagramHeader);
|
||||||
|
|
||||||
|
alice.setOther(&bob);
|
||||||
|
bob.setOther(&alice);
|
||||||
|
|
||||||
|
// perform a large number of simulation iterations
|
||||||
|
const int SIMULATION_ITERATIONS = 100000;
|
||||||
|
for (int i = 0; i < SIMULATION_ITERATIONS; i++) {
|
||||||
|
if (alice.simulate(i) || bob.simulate(i)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Sent " << highPriorityMessagesSent << " high priority messages";
|
||||||
|
|
||||||
|
qDebug() << "Sent " << unreliableMessagesSent << " unreliable messages, received " << unreliableMessagesReceived;
|
||||||
|
|
||||||
|
qDebug() << "All tests passed!";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Endpoint::Endpoint(const QByteArray& datagramHeader) :
|
||||||
|
_sequencer(new DatagramSequencer(datagramHeader)),
|
||||||
|
_highPriorityMessagesToSend(0.0f) {
|
||||||
|
|
||||||
|
connect(_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendDatagram(const QByteArray&)));
|
||||||
|
connect(_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readMessage(Bitstream&)));
|
||||||
|
connect(_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)),
|
||||||
|
SLOT(handleHighPriorityMessage(const QVariant&)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QByteArray createRandomBytes() {
|
||||||
|
const int MIN_BYTES = 4;
|
||||||
|
const int MAX_BYTES = 16;
|
||||||
|
QByteArray bytes(randIntInRange(MIN_BYTES, MAX_BYTES), 0);
|
||||||
|
for (int i = 0; i < bytes.size(); i++) {
|
||||||
|
bytes[i] = rand();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QVariant createRandomMessage() {
|
||||||
|
switch (randIntInRange(0, 2)) {
|
||||||
|
case 0: {
|
||||||
|
TestMessageA message = { randomBoolean(), rand(), randFloat() };
|
||||||
|
return QVariant::fromValue(message);
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
TestMessageB message = { createRandomBytes() };
|
||||||
|
return QVariant::fromValue(message);
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
default: {
|
||||||
|
TestMessageC message;
|
||||||
|
message.foo = randomBoolean();
|
||||||
|
message.bar = rand();
|
||||||
|
message.baz = randFloat();
|
||||||
|
message.bong.foo = createRandomBytes();
|
||||||
|
return QVariant::fromValue(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMessage) {
|
||||||
|
int type = firstMessage.userType();
|
||||||
|
if (secondMessage.userType() != type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (type == TestMessageA::Type) {
|
||||||
|
return firstMessage.value<TestMessageA>() == secondMessage.value<TestMessageA>();
|
||||||
|
} else if (type == TestMessageB::Type) {
|
||||||
|
return firstMessage.value<TestMessageB>() == secondMessage.value<TestMessageB>();
|
||||||
|
} else if (type == TestMessageC::Type) {
|
||||||
|
return firstMessage.value<TestMessageC>() == secondMessage.value<TestMessageC>();
|
||||||
|
} else {
|
||||||
|
return firstMessage == secondMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Endpoint::simulate(int iterationNumber) {
|
||||||
|
// enqueue some number of high priority messages
|
||||||
|
const float MIN_HIGH_PRIORITY_MESSAGES = 0.0f;
|
||||||
|
const float MAX_HIGH_PRIORITY_MESSAGES = 2.0f;
|
||||||
|
_highPriorityMessagesToSend += randFloatInRange(MIN_HIGH_PRIORITY_MESSAGES, MAX_HIGH_PRIORITY_MESSAGES);
|
||||||
|
while (_highPriorityMessagesToSend >= 1.0f) {
|
||||||
|
QVariant message = createRandomMessage();
|
||||||
|
_highPriorityMessagesSent.append(message);
|
||||||
|
_sequencer->sendHighPriorityMessage(message);
|
||||||
|
highPriorityMessagesSent++;
|
||||||
|
_highPriorityMessagesToSend -= 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send a packet
|
||||||
|
try {
|
||||||
|
Bitstream& out = _sequencer->startPacket();
|
||||||
|
SequencedTestMessage message = { iterationNumber, createRandomMessage() };
|
||||||
|
_unreliableMessagesSent.append(message);
|
||||||
|
unreliableMessagesSent++;
|
||||||
|
out << message;
|
||||||
|
_sequencer->endPacket();
|
||||||
|
|
||||||
|
} catch (const QString& message) {
|
||||||
|
qDebug() << message;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Endpoint::sendDatagram(const QByteArray& datagram) {
|
||||||
|
// some datagrams are dropped
|
||||||
|
const float DROP_PROBABILITY = 0.1f;
|
||||||
|
if (randFloat() < DROP_PROBABILITY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_other->_sequencer->receivedDatagram(datagram);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Endpoint::handleHighPriorityMessage(const QVariant& message) {
|
||||||
|
if (_other->_highPriorityMessagesSent.isEmpty()) {
|
||||||
|
throw QString("Received unsent/already sent high priority message.");
|
||||||
|
}
|
||||||
|
QVariant sentMessage = _other->_highPriorityMessagesSent.takeFirst();
|
||||||
|
if (!messagesEqual(message, sentMessage)) {
|
||||||
|
throw QString("Sent/received high priority message mismatch.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Endpoint::readMessage(Bitstream& in) {
|
||||||
|
SequencedTestMessage message;
|
||||||
|
in >> message;
|
||||||
|
|
||||||
|
for (QList<SequencedTestMessage>::iterator it = _other->_unreliableMessagesSent.begin();
|
||||||
|
it != _other->_unreliableMessagesSent.end(); it++) {
|
||||||
|
if (it->sequenceNumber == message.sequenceNumber) {
|
||||||
|
if (!messagesEqual(it->submessage, message.submessage)) {
|
||||||
|
throw QString("Sent/received unreliable message mismatch.");
|
||||||
|
}
|
||||||
|
_other->_unreliableMessagesSent.erase(_other->_unreliableMessagesSent.begin(), it + 1);
|
||||||
|
unreliableMessagesReceived++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw QString("Received unsent/already sent unreliable message.");
|
||||||
|
}
|
109
tests/metavoxels/src/MetavoxelTests.h
Normal file
109
tests/metavoxels/src/MetavoxelTests.h
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
//
|
||||||
|
// MetavoxelTests.h
|
||||||
|
// metavoxel-tests
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 2/7/14.
|
||||||
|
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __interface__MetavoxelTests__
|
||||||
|
#define __interface__MetavoxelTests__
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QVariantList>
|
||||||
|
|
||||||
|
#include <Bitstream.h>
|
||||||
|
|
||||||
|
class DatagramSequencer;
|
||||||
|
class SequencedTestMessage;
|
||||||
|
|
||||||
|
/// Tests various aspects of the metavoxel library.
|
||||||
|
class MetavoxelTests : public QCoreApplication {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
MetavoxelTests(int& argc, char** argv);
|
||||||
|
|
||||||
|
/// Performs our various tests.
|
||||||
|
/// \return true if any of the tests failed.
|
||||||
|
bool run();
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Represents a simulated endpoint.
|
||||||
|
class Endpoint : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Endpoint(const QByteArray& datagramHeader);
|
||||||
|
|
||||||
|
void setOther(Endpoint* other) { _other = other; }
|
||||||
|
|
||||||
|
/// Perform a simulation step.
|
||||||
|
/// \return true if failure was detected
|
||||||
|
bool simulate(int iterationNumber);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void sendDatagram(const QByteArray& datagram);
|
||||||
|
void handleHighPriorityMessage(const QVariant& message);
|
||||||
|
void readMessage(Bitstream& in);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
DatagramSequencer* _sequencer;
|
||||||
|
Endpoint* _other;
|
||||||
|
float _highPriorityMessagesToSend;
|
||||||
|
QVariantList _highPriorityMessagesSent;
|
||||||
|
QList<SequencedTestMessage> _unreliableMessagesSent;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A simple test message.
|
||||||
|
class TestMessageA {
|
||||||
|
STREAMABLE
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
STREAM bool foo;
|
||||||
|
STREAM int bar;
|
||||||
|
STREAM float baz;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_STREAMABLE_METATYPE(TestMessageA)
|
||||||
|
|
||||||
|
// Another simple test message.
|
||||||
|
class TestMessageB {
|
||||||
|
STREAMABLE
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
STREAM QByteArray foo;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_STREAMABLE_METATYPE(TestMessageB)
|
||||||
|
|
||||||
|
// A test message that demonstrates inheritance and composition.
|
||||||
|
class TestMessageC : public TestMessageA {
|
||||||
|
STREAMABLE
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
STREAM TestMessageB bong;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_STREAMABLE_METATYPE(TestMessageC)
|
||||||
|
|
||||||
|
/// Combines a sequence number with a submessage; used for testing unreliable transport.
|
||||||
|
class SequencedTestMessage {
|
||||||
|
STREAMABLE
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
STREAM int sequenceNumber;
|
||||||
|
STREAM QVariant submessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_STREAMABLE_METATYPE(SequencedTestMessage)
|
||||||
|
|
||||||
|
#endif /* defined(__interface__MetavoxelTests__) */
|
14
tests/metavoxels/src/main.cpp
Normal file
14
tests/metavoxels/src/main.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
//
|
||||||
|
// main.cpp
|
||||||
|
// metavoxel-tests
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 2/7/14.
|
||||||
|
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "MetavoxelTests.h"
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
return MetavoxelTests(argc, argv).run();
|
||||||
|
}
|
|
@ -90,7 +90,7 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
|
||||||
foreach (const Streamable& str, streamables) {
|
foreach (const Streamable& str, streamables) {
|
||||||
const QString& name = str.clazz.name;
|
const QString& name = str.clazz.name;
|
||||||
|
|
||||||
out << "Bitstream& operator<< (Bitstream& out, const " << name << "& obj) {\n";
|
out << "Bitstream& operator<<(Bitstream& out, const " << name << "& obj) {\n";
|
||||||
foreach (const QString& base, str.clazz.bases) {
|
foreach (const QString& base, str.clazz.bases) {
|
||||||
out << " out << static_cast<const " << base << "&>(obj);\n";
|
out << " out << static_cast<const " << base << "&>(obj);\n";
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
|
||||||
out << " return out;\n";
|
out << " return out;\n";
|
||||||
out << "}\n";
|
out << "}\n";
|
||||||
|
|
||||||
out << "Bitstream& operator>> (Bitstream& in, " << name << "& obj) {\n";
|
out << "Bitstream& operator>>(Bitstream& in, " << name << "& obj) {\n";
|
||||||
foreach (const QString& base, str.clazz.bases) {
|
foreach (const QString& base, str.clazz.bases) {
|
||||||
out << " in >> static_cast<" << base << "&>(obj);\n";
|
out << " in >> static_cast<" << base << "&>(obj);\n";
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,58 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
|
||||||
out << " return in;\n";
|
out << " return in;\n";
|
||||||
out << "}\n";
|
out << "}\n";
|
||||||
|
|
||||||
|
out << "bool operator==(const " << name << "& first, const " << name << "& second) {\n";
|
||||||
|
if (str.clazz.bases.isEmpty() && str.fields.isEmpty()) {
|
||||||
|
out << " return true";
|
||||||
|
} else {
|
||||||
|
out << " return ";
|
||||||
|
bool first = true;
|
||||||
|
foreach (const QString& base, str.clazz.bases) {
|
||||||
|
if (!first) {
|
||||||
|
out << " &&\n";
|
||||||
|
out << " ";
|
||||||
|
}
|
||||||
|
out << "static_cast<" << base << "&>(first) == static_cast<" << base << "&>(second)";
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
foreach (const QString& field, str.fields) {
|
||||||
|
if (!first) {
|
||||||
|
out << " &&\n";
|
||||||
|
out << " ";
|
||||||
|
}
|
||||||
|
out << "first." << field << " == second." << field;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out << ";\n";
|
||||||
|
out << "}\n";
|
||||||
|
|
||||||
|
out << "bool operator!=(const " << name << "& first, const " << name << "& second) {\n";
|
||||||
|
if (str.clazz.bases.isEmpty() && str.fields.isEmpty()) {
|
||||||
|
out << " return false";
|
||||||
|
} else {
|
||||||
|
out << " return ";
|
||||||
|
bool first = true;
|
||||||
|
foreach (const QString& base, str.clazz.bases) {
|
||||||
|
if (!first) {
|
||||||
|
out << " ||\n";
|
||||||
|
out << " ";
|
||||||
|
}
|
||||||
|
out << "static_cast<" << base << "&>(first) != static_cast<" << base << "&>(second)";
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
foreach (const QString& field, str.fields) {
|
||||||
|
if (!first) {
|
||||||
|
out << " ||\n";
|
||||||
|
out << " ";
|
||||||
|
}
|
||||||
|
out << "first." << field << " != second." << field;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out << ";\n";
|
||||||
|
out << "}\n";
|
||||||
|
|
||||||
out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n\n";
|
out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue