-
-
Save Dugy/2532c810bb232b8ff1603cfa679bdf28 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//usr/bin/g++ $0 -o ${o=`mktemp`} && exec $o $* | |
#include <iostream> | |
#include <fstream> | |
#include <string> | |
#include <vector> | |
#include <memory> | |
#include <chrono> | |
#include <charconv> | |
#include <cstring> | |
#ifdef USE_ERROR_CODES | |
enum ErrorType { | |
SUCCESS = 0, | |
PARSE_ERROR = 1, | |
EXPECTED_NUMBER = 2, | |
INVALID_RESOURCE_ERROR = 3, | |
INVALID_PACKET = 4, | |
NOT_ENOUGH_DATA = 5 | |
}; | |
#else | |
struct ParseError : std::runtime_error { | |
using std::runtime_error::runtime_error; | |
}; | |
struct InvalidResourceError : std::runtime_error { | |
using std::runtime_error::runtime_error; | |
}; | |
struct NetworkError : std::runtime_error { | |
using std::runtime_error::runtime_error; | |
}; | |
#endif | |
// Mostly test 1 | |
struct Xml { | |
std::string name; | |
std::vector<std::pair<std::string, std::string>> attributes; | |
std::vector<std::shared_ptr<Xml>> children; | |
std::string text; | |
#ifdef USE_ERROR_CODES | |
ErrorType getAttribute(const std::string& attributeName, std::string& out) const { | |
for (auto& it : attributes) | |
if (it.first == attributeName) { | |
out = it.second; | |
return SUCCESS; | |
} | |
return INVALID_RESOURCE_ERROR; | |
} | |
ErrorType getChild(const std::string& childName, std::shared_ptr<Xml>& out) const { | |
for (auto& it : children) | |
if (it->name == childName) { | |
out = it; | |
return SUCCESS; | |
} | |
return INVALID_RESOURCE_ERROR; | |
} | |
#else | |
std::string getAttribute(const std::string& attributeName) const { | |
for (auto& it : attributes) | |
if (it.first == attributeName) { | |
return it.second; | |
} | |
#ifdef __cpp_exceptions | |
throw InvalidResourceError("Missing attribute " + name); | |
#else | |
std::cerr << "Missing attribute " << attributeName << std::endl; | |
exit(4); | |
#endif | |
} | |
std::shared_ptr<Xml> getChild(const std::string& childName) const { | |
for (auto& it : children) | |
if (it->name == childName) { | |
return it; | |
} | |
#ifdef __cpp_exceptions | |
throw InvalidResourceError("Missing child " + name); | |
#else | |
std::cerr << "Missing child " << childName << std::endl; | |
exit(4); | |
#endif | |
} | |
#endif | |
void write(std::ostream& stream, int depth = 0) const { | |
auto indent = [depth, &stream] { | |
for (int i = 0; i < depth; i++) | |
stream << '\t'; | |
}; | |
indent(); | |
stream << '<' << name; | |
for (auto& it : attributes) { | |
stream << ' ' << it.first << "=\"" << it.second << '\"'; | |
} | |
if (children.empty() && text.empty()) { | |
stream << "/>\n"; | |
} else { | |
stream << ">\n"; | |
if (!text.empty()) { | |
stream << text << '\n'; | |
} | |
if (!children.empty()) { | |
for (auto& it : children) { | |
it->write(stream, depth + 1); | |
} | |
} | |
indent(); | |
stream << "</" << name << ">\n"; | |
} | |
} | |
friend std::ostream& operator<<(std::ostream& stream, const Xml& self) { | |
self.write(stream, 0); | |
return stream; | |
} | |
}; | |
#ifndef __cpp_exceptions | |
void fail(const std::string& problem) { | |
// No idea why, but the performance is better if this function is defined but never used | |
std::cerr << problem << std::endl; | |
exit(3); | |
} | |
#endif | |
#ifdef USE_ERROR_CODES | |
ErrorType parseXml(const char*& source, std::shared_ptr<Xml>& result) { | |
#else | |
std::shared_ptr<Xml> parseXml(const char*& source) { | |
#endif | |
auto absorbWhitespace = [&source] { | |
while (*source == ' ' || *source == '\n' || *source == '\t' || *source == '\r') | |
source++; | |
}; | |
#ifdef USE_ERROR_CODES | |
#define REPORT_PARSE_ERROR return PARSE_ERROR | |
#define READ_STRING(TARGET, FORBIDDEN) if (readString(TARGET, FORBIDDEN)) return PARSE_ERROR | |
#else | |
#ifdef __cpp_exceptions | |
#define REPORT_PARSE_ERROR throw ParseError("Failed to parse") | |
#else | |
#define REPORT_PARSE_ERROR { std::cerr << "Parse error" << std::endl; exit(3); } | |
#endif | |
#define READ_STRING(TARGET, FORBIDDEN) readString(TARGET, FORBIDDEN) | |
#endif | |
auto readString = [&source] (std::string& made, const char* forbidden) { | |
while (true) { | |
bool stop = false; | |
for (int i = 0; forbidden[i]; i++) | |
if (*source == forbidden[i]) { | |
#ifdef USE_ERROR_CODES | |
return SUCCESS; | |
#else | |
return; | |
#endif | |
} | |
if (*source == '\0') | |
REPORT_PARSE_ERROR; | |
made.push_back(*source); | |
source++; | |
} | |
}; | |
absorbWhitespace(); | |
if (*source == '\0') { | |
#ifdef USE_ERROR_CODES | |
return SUCCESS; | |
#else | |
return nullptr; | |
#endif | |
} | |
#ifdef USE_ERROR_CODES | |
result = std::make_shared<Xml>(); | |
#endif | |
// Tag start | |
if (*source != '<') { | |
REPORT_PARSE_ERROR; | |
} | |
source++; | |
while (*source == '!') { | |
// Comments | |
for (int i = 0; i < 2; i++) { | |
source++; | |
if (*source != '-') { | |
REPORT_PARSE_ERROR; | |
} | |
} | |
do source++; | |
while (*source != '>' || source[-1] != '-' || source[-2] != '-'); | |
source++; | |
absorbWhitespace(); | |
source++; | |
} | |
absorbWhitespace(); | |
// It's a tag | |
#ifndef USE_ERROR_CODES | |
std::shared_ptr<Xml> result = std::make_shared<Xml>(); | |
#endif | |
READ_STRING(result->name, "\n\r\t >/"); | |
absorbWhitespace(); | |
// Parse attributes | |
while (*source != '>' && (*source != '/' || source[1] != '>')) { | |
std::string attributeName; | |
READ_STRING(attributeName, "\n\r\t =/>"); | |
if (*source != '=') | |
REPORT_PARSE_ERROR; | |
source++; | |
if (*source != '\"') | |
REPORT_PARSE_ERROR; | |
source++; | |
std::string attribute; | |
READ_STRING(attribute, "\""); | |
source++; | |
absorbWhitespace(); | |
result->attributes.push_back(std::make_pair(attributeName, attribute)); | |
} | |
if (*source == '/') { | |
// It's a tag without children | |
source += 2; | |
absorbWhitespace(); | |
#ifdef USE_ERROR_CODES | |
return SUCCESS; | |
#else | |
return result; | |
#endif | |
} | |
// The tag has children | |
source++; | |
absorbWhitespace(); | |
while (*source != '<' || source[1] != '/') { | |
if (*source != '<') { | |
result->text.push_back(*source); | |
source++; | |
} else { | |
#ifdef USE_ERROR_CODES | |
std::shared_ptr<Xml> child; | |
if (parseXml(source, child)) | |
return PARSE_ERROR; | |
result->children.push_back(child); | |
#else | |
result->children.push_back(parseXml(source)); | |
#endif | |
} | |
} | |
source += 2; | |
std::string check; | |
READ_STRING(check, "\n\t\r >"); | |
if (check != result->name) | |
REPORT_PARSE_ERROR; | |
source++; | |
absorbWhitespace(); | |
#ifdef USE_ERROR_CODES | |
return SUCCESS; | |
#else | |
return result; | |
#endif | |
} | |
#undef READ_STRING | |
// Mostly test 2 | |
#ifdef USE_ERROR_CODES | |
ErrorType parseXmlDocument(const char* str, std::vector<std::shared_ptr<Xml>>& result) { | |
#else | |
std::vector<std::shared_ptr<Xml>> parseXmlDocument(const char* str) { | |
std::vector<std::shared_ptr<Xml>> result; | |
#endif | |
// Eat the header | |
if (*str == '<' && str[1] == '?') { | |
str += 2; | |
while (*str != '>' || str[-1] != '?') { | |
if (*str == '\0') | |
REPORT_PARSE_ERROR; | |
str++; | |
} | |
str++; | |
} | |
std::shared_ptr<Xml> part; | |
do { | |
#ifdef USE_ERROR_CODES | |
part = nullptr; | |
if (parseXml(str, part)) | |
return PARSE_ERROR; | |
#else | |
part = parseXml(str); | |
#endif | |
if (part) | |
result.push_back(part); | |
} while (part); | |
#ifdef USE_ERROR_CODES | |
return SUCCESS; | |
#else | |
return result; | |
#endif | |
} | |
#undef REPORT_PARSE_ERROR | |
#ifdef USE_ERROR_CODES | |
#define PROPAGATE_ERROR(CODE) { auto problem = CODE; if (problem) return problem; } | |
#endif | |
unsigned int now() { | |
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count(); | |
} | |
#ifdef __cpp_exceptions | |
#define NETWORK_ERROR_OR_ABORT(MESSAGE) throw NetworkError(MESSAGE) | |
#else | |
#define NETWORK_ERROR_OR_ABORT(MESSAGE) { std::cerr << MESSAGE << std::endl; exit(6); } | |
#endif | |
struct TestClass { | |
#ifdef USE_ERROR_CODES | |
static ErrorType parseNumber(const std::string& from, int& to) { | |
auto result = std::from_chars(from.data(), from.data() + from.size(), to); | |
if (result.ptr != from.data()) | |
return SUCCESS; | |
else | |
return EXPECTED_NUMBER; | |
} | |
static ErrorType parseAttribute(const Xml& source, const std::string& name, int& result) { | |
std::string attribute; | |
auto problem = source.getAttribute(name, attribute); | |
if (problem) | |
return problem; | |
problem = parseNumber(attribute, result); | |
return problem; | |
} | |
template <typename Child> | |
static ErrorType setupChild(const Xml& source, const std::string& name, Child& target) { | |
std::shared_ptr<Xml> got; | |
PROPAGATE_ERROR(source.getChild(name, got)); | |
PROPAGATE_ERROR(target.fromXml(*got)); | |
return SUCCESS; | |
} | |
#else | |
static int parseNumber(const std::string& from) { | |
int to; | |
auto result = std::from_chars(from.data(), from.data() + from.size(), to); | |
if (result.ptr != from.data()) | |
return to; | |
else | |
#ifdef __cpp_exceptions | |
throw std::runtime_error("Expected number, got " + from); | |
#else | |
{ | |
std::cerr << "Expected number, got " << from << std::endl; | |
exit(4); | |
} | |
#endif | |
return 0; | |
} | |
static float parseAttribute(const Xml& source, const std::string& attributeName) { | |
return parseNumber(source.getAttribute(attributeName)); | |
} | |
#endif | |
struct Stats { | |
int damage; | |
int life; | |
#ifdef USE_ERROR_CODES | |
ErrorType fromXml(const Xml& source) { | |
PROPAGATE_ERROR(parseAttribute(source, "damage", damage)); | |
PROPAGATE_ERROR(parseAttribute(source, "life", life)); | |
return SUCCESS; | |
} | |
#else | |
Stats(const Xml& source) : | |
damage(parseAttribute(source, "damage")), | |
life(parseAttribute(source, "life")) { | |
} | |
#endif | |
}; | |
struct Appearance { | |
std::string mesh; | |
std::string icon; | |
#ifdef USE_ERROR_CODES | |
ErrorType fromXml(const Xml& source) { | |
PROPAGATE_ERROR(source.getAttribute("mesh", mesh)); | |
PROPAGATE_ERROR(source.getAttribute("icon", icon)); | |
return SUCCESS; | |
} | |
#else | |
Appearance(const Xml& source) : | |
mesh(source.getAttribute("mesh")), | |
icon(source.getAttribute("icon")) { | |
} | |
#endif | |
}; | |
struct Skill { | |
struct Stats { | |
int damage; | |
int speed; | |
int area; | |
std::string type; | |
#ifdef USE_ERROR_CODES | |
ErrorType fromXml(const Xml& source) { | |
PROPAGATE_ERROR(parseAttribute(source, "damage", damage)); | |
PROPAGATE_ERROR(parseAttribute(source, "speed", speed)); | |
PROPAGATE_ERROR(parseAttribute(source, "area", area)); | |
PROPAGATE_ERROR(source.getAttribute("type", type)); | |
return SUCCESS; | |
} | |
#else | |
Stats(const Xml& source) : | |
damage(parseAttribute(source, "damage")), | |
speed(parseAttribute(source, "speed")), | |
area(parseAttribute(source, "area")), | |
type(source.getAttribute("type")) { | |
} | |
#endif | |
}; | |
struct Animation { | |
int speed; | |
std::string name; | |
#ifdef USE_ERROR_CODES | |
ErrorType fromXml(const Xml& source) { | |
PROPAGATE_ERROR(parseAttribute(source, "speed", speed)); | |
PROPAGATE_ERROR(source.getAttribute("name", name)); | |
return SUCCESS; | |
} | |
#else | |
Animation(const Xml& source) : | |
speed(parseAttribute(source, "speed")), | |
name(source.getAttribute("name")) { | |
} | |
#endif | |
}; | |
struct Effect { | |
std::string particle; | |
std::string colour; | |
#ifdef USE_ERROR_CODES | |
ErrorType fromXml(const Xml& source) { | |
PROPAGATE_ERROR(source.getAttribute("particle", particle)); | |
PROPAGATE_ERROR(source.getAttribute("colour", colour)); | |
return SUCCESS; | |
} | |
#else | |
Effect(const Xml& source) : | |
particle(source.getAttribute("particle")), | |
colour(source.getAttribute("colour")) { | |
} | |
#endif | |
}; | |
std::string name; | |
Stats stats; | |
Animation animation; | |
Effect effect; | |
#ifdef USE_ERROR_CODES | |
ErrorType fromXml(const Xml& source) { | |
PROPAGATE_ERROR(source.getAttribute("name", name)); | |
PROPAGATE_ERROR(setupChild(source, "stats", stats)); | |
PROPAGATE_ERROR(setupChild(source, "animation", animation)); | |
PROPAGATE_ERROR(setupChild(source, "effect", effect)); | |
return SUCCESS; | |
} | |
#else | |
Skill(const Xml& source) : | |
name(source.getAttribute("name")), | |
stats(*source.getChild("stats")), | |
animation(*source.getChild("animation")), | |
effect(*source.getChild("effect")) { | |
} | |
#endif | |
}; | |
std::string id; | |
std::string description; | |
Stats stats; | |
Appearance appearance; | |
std::vector<Skill> skills; | |
int x = rand() % 128 - 64; | |
int y = rand() % 128 - 64; | |
int life; | |
unsigned int lastAttack = now(); | |
#ifdef USE_ERROR_CODES | |
ErrorType fromXml(const Xml& source) { | |
PROPAGATE_ERROR(source.getAttribute("id", id)); | |
PROPAGATE_ERROR(setupChild(source, "stats", stats)); | |
PROPAGATE_ERROR(setupChild(source, "appearance", appearance)); | |
for (auto& it : source.children) | |
if (it->name == "skill") { | |
skills.emplace_back(); | |
PROPAGATE_ERROR(skills.back().fromXml(*it)); | |
} | |
life = stats.life; | |
return SUCCESS; | |
} | |
#else | |
TestClass(const Xml& source) : | |
id(source.getAttribute("id")), | |
description(source.getChild("description")->text), | |
stats(*source.getChild("stats")), | |
appearance(*source.getChild("appearance")), | |
life(stats.life) { | |
for (auto& it : source.children) | |
if (it->name == "skill") | |
skills.emplace_back(std::move(*it)); | |
} | |
#endif | |
struct LocationUpdate { | |
std::array<char, 16> name; | |
int x; | |
int y; | |
}; | |
struct LifeUpdate { | |
std::array<char, 16> name; | |
int newLife; | |
}; | |
struct LastAttackUpdate { | |
std::array<char, 16> name; | |
unsigned int lastAttackTime; | |
}; | |
int difference(int first, int second) { | |
if (first > second) | |
return first - second; | |
else | |
return second - first; | |
} | |
#ifdef USE_ERROR_CODES | |
ErrorType updateLocation(const LocationUpdate& update) { | |
if (difference(x, update.x) > 256 || difference(y, update.y) > 256) | |
return INVALID_PACKET; | |
x = update.x; | |
y = update.y; | |
return SUCCESS; | |
} | |
ErrorType updateLife(const LifeUpdate& update) { | |
if (update.newLife < 0 || update.newLife > 2048) | |
return INVALID_PACKET; | |
life = update.newLife; | |
return SUCCESS; | |
} | |
ErrorType updateLastAttack(const LastAttackUpdate& update) { | |
if (update.lastAttackTime > now()) | |
return INVALID_PACKET; | |
lastAttack = update.lastAttackTime; | |
return SUCCESS; | |
} | |
#else | |
void updateLocation(const LocationUpdate& update) { | |
if (difference(x, update.x) > 256 || difference(y, update.y) > 256) | |
NETWORK_ERROR_OR_ABORT("Invalid location update"); | |
x = update.x; | |
y = update.y; | |
} | |
void updateLife(const LifeUpdate& update) { | |
if (update.newLife < 0 || update.newLife > 2048) | |
NETWORK_ERROR_OR_ABORT("Invalid life update"); | |
life = update.newLife; | |
} | |
void updateLastAttack(const LastAttackUpdate& update) { | |
if (update.lastAttackTime > now()) | |
NETWORK_ERROR_OR_ABORT("Invalid last attack update"); | |
lastAttack = update.lastAttackTime; | |
} | |
#endif | |
}; | |
// The rest of test 3 | |
template <auto identifier, typename MessageType, typename Reaction> | |
#ifdef USE_ERROR_CODES | |
ErrorType parseMessage(const char*& data, int& dataLeft, const Reaction& reaction) { | |
#else | |
bool parseMessage(const char*& data, int& dataLeft, const Reaction& reaction) { | |
#endif | |
if (*data == identifier) { | |
if (dataLeft < sizeof(MessageType) + 1) { | |
#ifdef USE_ERROR_CODES | |
return NOT_ENOUGH_DATA; | |
#else | |
return true; | |
#endif | |
} | |
auto moveData = [&] (int amount) { | |
data += amount; | |
dataLeft -= amount; | |
}; | |
moveData(1); | |
MessageType message; | |
memcpy(reinterpret_cast<char*>(&message), data, sizeof(MessageType)); | |
moveData(sizeof(MessageType)); | |
#ifdef USE_ERROR_CODES | |
return reaction(message); | |
#else | |
reaction(message); | |
#endif | |
} | |
#ifdef USE_ERROR_CODES | |
return SUCCESS; | |
#else | |
return false; | |
#endif | |
} | |
#ifdef USE_ERROR_CODES | |
ErrorType testUpdate(std::vector<TestClass>& state, const char*& data, int& dataLeft) { | |
#else | |
bool testUpdate(std::vector<TestClass>& state, const char*& data, int& dataLeft) { | |
#endif | |
if (dataLeft == 0) { | |
#ifdef USE_ERROR_CODES | |
return NOT_ENOUGH_DATA; | |
#else | |
return true; | |
#endif | |
} | |
enum MessageIdentifier : char { | |
LOCATION_UPDATE = 1, | |
LIFE_UPDATE = 2, | |
LAST_ATTACK_UPDATE = 3 | |
}; | |
auto match = [&] (auto call) { | |
return [call, &state] (auto& message) { | |
for (auto& it : state) | |
if (!strcmp(it.id.c_str(), message.name.data())) { | |
#ifdef USE_ERROR_CODES | |
return (it.*call)(message); | |
#else | |
(it.*call)(message); | |
return; | |
#endif | |
} | |
#ifdef USE_ERROR_CODES | |
return INVALID_PACKET; | |
#else | |
NETWORK_ERROR_OR_ABORT("Referring to nonexistent entity"); | |
#endif | |
}; | |
}; | |
auto problem = parseMessage<LOCATION_UPDATE, TestClass::LocationUpdate>(data, dataLeft, match(&TestClass::updateLocation)); | |
if (problem) return problem; | |
problem = parseMessage<LIFE_UPDATE, TestClass::LifeUpdate>(data, dataLeft, match(&TestClass::updateLife)); | |
if (problem) return problem; | |
problem = parseMessage<LAST_ATTACK_UPDATE, TestClass::LastAttackUpdate>(data, dataLeft, match(&TestClass::updateLastAttack)); | |
if (problem) return problem; | |
#ifdef USE_ERROR_CODES | |
return SUCCESS; | |
#else | |
return false; | |
#endif | |
} | |
// Overall testing code | |
int main(int argc, char** argv) | |
{ | |
if (argc != 3) { | |
std::cout << "Use " << argv[0] << " XML_FILE_NAME BINARY_FILE_NAME" << std::endl; | |
return 1; | |
} | |
std::ifstream file(argv[1]); | |
if (!file.good()) { | |
#ifdef USE_ERROR_CODES | |
std::cerr << "File not readable" << std::endl; | |
return 2; | |
#else | |
#ifdef __cpp_exceptions | |
throw std::runtime_error("Could not read XML file"); | |
#else | |
{ std::cout << "Could not read XML file" << std::endl; exit(3); } | |
#endif | |
#endif | |
} | |
std::string fileString((std::istreambuf_iterator<char>(file)), | |
std::istreambuf_iterator<char>()); | |
constexpr int parsingRepeats = 1000; | |
std::vector<std::shared_ptr<Xml>> parsed; | |
auto start = std::chrono::high_resolution_clock::now(); | |
for (int i = 0; i < parsingRepeats; i++) { | |
const char* str = fileString.c_str(); | |
parsed.clear(); | |
#ifdef USE_ERROR_CODES | |
auto result = parseXmlDocument(str, parsed); | |
// for (auto& it : parsed) { | |
// std::cout << *it; | |
// } | |
#else | |
#ifdef __cpp_exceptions | |
try { | |
#endif | |
parsed = parseXmlDocument(str); | |
// for (auto& it : parsed) { | |
// std::cout << *it; | |
// } | |
#ifdef __cpp_exceptions | |
} catch (std::exception& e) { | |
std::cerr << "Parsing failed: " << e.what() << std::endl; | |
} | |
#endif | |
#endif | |
} | |
auto end = std::chrono::high_resolution_clock::now(); | |
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); | |
std::cout << "Parsing took on average " << (duration / parsingRepeats) << " us" << std::endl; | |
constexpr int fillingRepeats = 10000; | |
std::vector<TestClass> filled; | |
start = std::chrono::high_resolution_clock::now(); | |
for (int i = 0; i < fillingRepeats; i++) { | |
filled.clear(); | |
#ifdef USE_ERROR_CODES | |
ErrorType problem; | |
for (auto& it : parsed) { | |
filled.emplace_back(); | |
problem = filled.back().fromXml(*it); | |
if (problem) { | |
// std::cout << "Invalid XML" << std::endl; | |
break; | |
} | |
} | |
#else | |
#ifdef __cpp_exceptions | |
try { | |
#endif | |
for (auto& it : parsed) { | |
filled.emplace_back(std::move(*it)); | |
} | |
#ifdef __cpp_exceptions | |
} catch (std::exception& e) { | |
std::cerr << "Filling failed: " << e.what() << std::endl; | |
} | |
#endif | |
#endif | |
} | |
end = std::chrono::high_resolution_clock::now(); | |
duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); | |
std::cout << "Filling took on average " << (float(duration) / fillingRepeats) << " us" << std::endl; | |
constexpr int updates = 1000; | |
std::vector<TestClass> updated; | |
std::ifstream updateStream(argv[2], std::ios::binary); | |
std::vector<char> fileStream((std::istreambuf_iterator<char>(updateStream)), std::istreambuf_iterator<char>()); | |
start = std::chrono::high_resolution_clock::now(); | |
for (int i = 0; i < updates; i++) { | |
updated = filled; | |
const char* data = fileStream.data(); | |
int dataLeft = fileStream.size(); | |
#ifdef USE_ERROR_CODES | |
ErrorType problem; | |
while (!(problem = testUpdate(updated, data, dataLeft))) { } | |
if (problem != NOT_ENOUGH_DATA) { | |
// std::cerr << "Error " << problem << std::endl; | |
} | |
#else | |
#ifdef __cpp_exceptions | |
try { | |
#endif | |
while (!testUpdate(updated, data, dataLeft)) { } | |
#ifdef __cpp_exceptions | |
} catch (std::exception& e) { | |
std::cerr << "Updating failed: " << e.what() << std::endl; | |
} | |
#endif | |
#endif | |
} | |
end = std::chrono::high_resolution_clock::now(); | |
duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); | |
std::cout << "Updating took on average " << (float(duration) / updates) << " us" << std::endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment