]> code.bitgloo.com Git - clyne/entities.git/commitdiff
reorginization
authorClyne Sullivan <tullivan99@gmail.com>
Sun, 26 Feb 2017 23:58:25 +0000 (18:58 -0500)
committerClyne Sullivan <tullivan99@gmail.com>
Sun, 26 Feb 2017 23:58:25 +0000 (18:58 -0500)
18 files changed:
EntityXBenchmark.h [deleted file]
Makefile [deleted file]
README.md
bench.cpp [deleted file]
benchpress.hpp [deleted file]
cxxopts.hpp [deleted file]
entitiesBenchmark.h [deleted file]
main.cpp [deleted file]
tests/EntityXBenchmark.h [new file with mode: 0644]
tests/Makefile [new file with mode: 0644]
tests/benchpress.hpp [new file with mode: 0644]
tests/cxxopts.hpp [new file with mode: 0644]
tests/entitiesBenchmark.h [new file with mode: 0644]
tests/entitiesTests [new file with mode: 0755]
tests/entitiesTests.cpp [new file with mode: 0644]
tests/entityXTests [new file with mode: 0755]
tests/entityXTests.cpp [new file with mode: 0644]
xtest.cpp [deleted file]

diff --git a/EntityXBenchmark.h b/EntityXBenchmark.h
deleted file mode 100644 (file)
index defa37c..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-#ifndef ENTITYXBENCHMARK_H_
-#define ENTITYXBENCHMARK_H_
-
-#include <string>
-#include <vector>
-#include <memory>
-#include <random>
-#include <numeric>
-#include <functional>
-
-#include "entityx/entityx.h"
-
-class EntityXBenchmark {
-    public:
-
-    struct PositionComponent {
-        float x = 0.0f;
-        float y = 0.0f;
-    };
-
-    struct DirectionComponent {
-        float x = 0.0f;
-        float y = 0.0f;
-    };
-
-    struct ComflabulationComponent {
-        float thingy = 0.0;
-        int dingy = 0;
-        bool mingy = false;
-        std::string stringy;
-    };
-
-
-    // Convenience types for our entity system.
-    using Entity = entityx::Entity;
-    using EntityManager = entityx::EventManager;
-    using EventManager = entityx::EventManager;
-
-    template <typename C>
-    using Component = entityx::ComponentHandle<C>;
-
-    using TimeDelta = entityx::TimeDelta;
-
-
-
-    template<class S>
-    using System = entityx::System<S>;
-
-
-    class MovementSystem : public System<MovementSystem> {
-        public:
-        MovementSystem() = default;
-
-        void update(entityx::EntityManager &es, entityx::EventManager &events, entityx::TimeDelta dt) override {
-            Component<PositionComponent> position;
-            Component<DirectionComponent> direction;
-
-            for (auto entity : es.entities_with_components(position, direction)) {
-                position->x += direction->x * dt;
-                position->y += direction->y * dt;
-            }
-        }
-    };
-
-    class ComflabSystem : public System<ComflabSystem> {
-        public:
-        ComflabSystem() = default;
-
-        void update(entityx::EntityManager &es, entityx::EventManager &events, entityx::TimeDelta dt) override {
-            Component<ComflabulationComponent> comflab;
-
-            for (auto entity : es.entities_with_components(comflab)) {
-                comflab->thingy *= 1.000001f;
-                comflab->mingy = !comflab->mingy;
-                comflab->dingy++;
-                //comflab.stringy = std::to_string(comflab.dingy);
-            }
-        }
-    };
-
-    #ifdef USE_MORECOMPLEX_SYSTEM
-    class MoreComplexSystem : public System<MoreComplexSystem> {
-        private:
-        int random(int min, int max){
-            // Seed with a real random value, if available
-            static std::random_device r;
-        
-            // Choose a random mean between min and max
-            static std::default_random_engine e1(r());
-
-            std::uniform_int_distribution<int> uniform_dist(min, max);
-
-            return uniform_dist(e1);
-        }
-
-        public:
-        MoreComplexSystem() = default;
-
-        void update(entityx::EntityManager &es, entityx::EventManager &events, entityx::TimeDelta dt) override {
-            Component<PositionComponent> position;
-            Component<DirectionComponent> direction;
-            Component<ComflabulationComponent> comflab;
-
-            for (auto entity : es.entities_with_components(comflab, direction, comflab)) {
-                if(comflab) {
-                    std::vector<double> vec;
-                    for(size_t i = 0;i < comflab->dingy && i < 100;i++){
-                        vec.push_back(i * comflab->thingy);
-                    }
-
-                    int sum = std::accumulate(std::begin(vec), std::end(vec), 0.0);
-                    int product = std::accumulate(std::begin(vec), std::end(vec), 1, std::multiplies<double>());
-
-                    comflab->stringy = std::to_string(comflab->dingy);
-
-                    if(position && direction && comflab->dingy % 10000 == 0) {
-                        if(position->x > position->y) {
-                            direction->x = random(0, 5);
-                            direction->y = random(0, 10);
-                        } else {
-                            direction->x = random(0, 10);
-                            direction->y = random(0, 5);
-                        }
-                    }
-                }
-            }
-        }
-    };
-    #endif
-
-    class Application : public entityx::EntityX {
-        public:
-        Application() {
-            this->systems.add<MovementSystem>();
-            this->systems.add<ComflabSystem>();
-            #ifdef USE_MORECOMPLEX_SYSTEM
-            this->systems.add<MoreComplexSystem>();
-            #endif
-
-            this->systems.configure();
-        }
-
-        void update(TimeDelta dt) {
-            this->systems.update<MovementSystem>(dt);
-            this->systems.update<ComflabSystem>(dt);
-            #ifdef USE_MORECOMPLEX_SYSTEM
-            this->systems.update<MoreComplexSystem>(dt);
-            #endif
-        }
-    };
-
-    static constexpr double fakeDeltaTime = 1.0 / 60;
-};
-
-#endif // ENTITYXBENCHMARK_H_
diff --git a/Makefile b/Makefile
deleted file mode 100644 (file)
index b1a2703..0000000
--- a/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-all:
-       g++ -std=c++17 -ggdb -pedantic -Wall -Wextra main.cpp -o main -lentityx
-
-test:
-       g++ -std=c++17 -Wall -Wextra bench.cpp -o test -O1 
-       g++ -std=c++17 -Wall -Wextra xtest.cpp -o xtest -O1 -lentityx
-       
index 5ee95027ad8f297449ebace4a449fee24c0ddddc..02651810d56f89160ef880924938695266d16348 100644 (file)
--- a/README.md
+++ b/README.md
@@ -7,43 +7,43 @@ The file that actually contains the library is entities.hpp.
 ## compared to EntityX
 entities:
 ```
-entityx create destroy entity with components     5000000         254 ns/op
-      [25] entityx       25 entities component systems update     1000000        1572 ns/op
-      [50] entityx       50 entities component systems update      500000        3009 ns/op
-     [100] entityx      100 entities component systems update      200000        5995 ns/op
-     [200] entityx      200 entities component systems update      100000       11848 ns/op
-     [400] entityx      400 entities component systems update       50000       23631 ns/op
-     [800] entityx      800 entities component systems update       20000       51669 ns/op
-    [1600] entityx     1600 entities component systems update       10000      112446 ns/op
-    [3200] entityx     3200 entities component systems update        5000      225504 ns/op
-    [5000] entityx     5000 entities component systems update        5000      368282 ns/op
-   [10000] entityx    10000 entities component systems update        1000     1240502 ns/op
-   [30000] entityx    30000 entities component systems update         200     5196731 ns/op
-  [100000] entityx   100000 entities component systems update         100    18331731 ns/op
-  [500000] entityx   500000 entities component systems update          20    93182044 ns/op
- [1000000] entityx  1000000 entities component systems update          10   191315282 ns/op
- [2000000] entityx  2000000 entities component systems update           5   299790721 ns/op
-./test 43.175s
+entities create destroy entity with components     5000000         279 ns/op
+      [25] entities       25 entities component systems update     1000000        1610 ns/op
+      [50] entities       50 entities component systems update      500000        3098 ns/op
+     [100] entities      100 entities component systems update      200000        6247 ns/op
+     [200] entities      200 entities component systems update      100000       12274 ns/op
+     [400] entities      400 entities component systems update       50000       25029 ns/op
+     [800] entities      800 entities component systems update       20000       55717 ns/op
+    [1600] entities     1600 entities component systems update       10000      116305 ns/op
+    [3200] entities     3200 entities component systems update        5000      244461 ns/op
+    [5000] entities     5000 entities component systems update        5000      463835 ns/op
+   [10000] entities    10000 entities component systems update        1000     1647042 ns/op
+   [30000] entities    30000 entities component systems update         200     5784773 ns/op
+  [100000] entities   100000 entities component systems update          50    20711117 ns/op
+  [500000] entities   500000 entities component systems update          20   103582654 ns/op
+ [1000000] entities  1000000 entities component systems update          10   208073004 ns/op
+ [2000000] entities  2000000 entities component systems update           5   341977621 ns/op
+./entitiesTests 45.328s
 ```
 
 EntityX:
 ```
-entityx create destroy entity with components     5000000         200 ns/op
-      [25] entityx       25 entities component systems update      500000        2903 ns/op
-      [50] entityx       50 entities component systems update      200000        5721 ns/op
-     [100] entityx      100 entities component systems update      100000       11338 ns/op
-     [200] entityx      200 entities component systems update       50000       22622 ns/op
-     [400] entityx      400 entities component systems update       50000       45560 ns/op
-     [800] entityx      800 entities component systems update       20000       92718 ns/op
-    [1600] entityx     1600 entities component systems update       10000      180159 ns/op
-    [3200] entityx     3200 entities component systems update        5000      360127 ns/op
-    [5000] entityx     5000 entities component systems update        2000      563316 ns/op
-   [10000] entityx    10000 entities component systems update        1000     1130659 ns/op
-   [30000] entityx    30000 entities component systems update         500     3431964 ns/op
-  [100000] entityx   100000 entities component systems update         100    11680312 ns/op
-  [500000] entityx   500000 entities component systems update          20    59996331 ns/op
- [1000000] entityx  1000000 entities component systems update          10   128663563 ns/op
- [2000000] entityx  2000000 entities component systems update           5   271582063 ns/op
-./xtest 41.912s
+entityx create destroy entity with components     5000000         212 ns/op
+      [25] entityx       25 entities component systems update      500000        3025 ns/op
+      [50] entityx       50 entities component systems update      200000        6274 ns/op
+     [100] entityx      100 entities component systems update      100000       12101 ns/op
+     [200] entityx      200 entities component systems update       50000       24227 ns/op
+     [400] entityx      400 entities component systems update       50000       48100 ns/op
+     [800] entityx      800 entities component systems update       20000       95872 ns/op
+    [1600] entityx     1600 entities component systems update       10000      196251 ns/op
+    [3200] entityx     3200 entities component systems update        5000      397806 ns/op
+    [5000] entityx     5000 entities component systems update        2000      623035 ns/op
+   [10000] entityx    10000 entities component systems update        1000     1279807 ns/op
+   [30000] entityx    30000 entities component systems update         500     3791401 ns/op
+  [100000] entityx   100000 entities component systems update         100    13074271 ns/op
+  [500000] entityx   500000 entities component systems update          20    65787539 ns/op
+ [1000000] entityx  1000000 entities component systems update          10   137753731 ns/op
+ [2000000] entityx  2000000 entities component systems update           5   290476228 ns/op
+./entityXTests 45.232s
 ```
 You can find EntityX [here](https://github.com/alecthomas/entityx).
diff --git a/bench.cpp b/bench.cpp
deleted file mode 100644 (file)
index 99824ac..0000000
--- a/bench.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-#include <string>
-#include <vector>
-#include <thread>
-#include <memory>
-
-#define BENCHPRESS_CONFIG_MAIN
-#include "benchpress.hpp"
-
-#include "entities.hpp"
-
-#include "entitiesBenchmark.h"
-
-inline void init_entities(EntityManager& entities, size_t nentities){
-    for (size_t i = 0; i < nentities; i++) {
-               auto entity = entities.create();
-
-               entity.assign<EntitiesBenchmark::PositionComponent>();
-               entity.assign<EntitiesBenchmark::VelocityComponent>();
-
-               if (i % 2) {
-                       entity.assign<EntitiesBenchmark::ComflabulationComponent>();
-               }
-       }
-}
-
-inline void runEntitiesSystemsEntityXBenchmark(benchpress::context* ctx, size_t nentities) {
-    EntitiesBenchmark::Application app;
-    auto& entities = app.em;
-
-    init_entities(entities, nentities);
-
-    ctx->reset_timer();
-    for (size_t i = 0; i < ctx->num_iterations(); ++i) {
-        app.update(EntitiesBenchmark::fakeDeltaTime);
-    }
-}
-
-
-
-
-BENCHMARK("entityx create destroy entity with components", [](benchpress::context* ctx) {
-    EntityManager entities;
-
-    ctx->reset_timer();
-    for (size_t i = 0; i < ctx->num_iterations(); ++i) {
-        auto entity = entities.create();
-
-        entity.assign<EntitiesBenchmark::PositionComponent>();
-               entity.assign<EntitiesBenchmark::VelocityComponent>();
-               entity.assign<EntitiesBenchmark::ComflabulationComponent>();
-
-        entities.kill(entity);
-    }
-})
-
-
-
-
-
-class BenchmarksEntityX {
-    public:
-    static const std::vector<int> ENTITIES;
-
-    static inline void makeBenchmarks(std::string name) {
-        makeBenchmarks(name, ENTITIES);
-    }
-    
-    static void makeBenchmarks(std::string name, const std::vector<int>& entities) {
-        for(int nentities : entities) {
-            std::string tag = "[" + std::to_string(nentities) + "]";
-
-            std::stringstream ss;
-            ss << std::right << std::setw(10) << tag << ' ';
-            ss << name << ' ';
-            ss << std::right << std::setw(8) << nentities;
-            ss << " entities component systems update";
-
-            std::string benchmark_name = ss.str();
-            BENCHMARK(benchmark_name, [nentities](benchpress::context* ctx) {
-                runEntitiesSystemsEntityXBenchmark(ctx, nentities);
-            })
-        }
-    }
-
-    BenchmarksEntityX(std::string name){
-        makeBenchmarks(name);
-    }
-};
-const std::vector<int> BenchmarksEntityX::ENTITIES = {
-    25, 50, 
-    100, 200, 400, 800, 
-    1600, 3200, 5000, 
-    10'000, 30'000, 
-    100'000, 500'000, 
-    1'000'000, 2'000'000
-};
-
-BenchmarksEntityX entityxbenchmarks ("entityx");
-
-
-
-
-
-/*
-BENCHMARK("[25]     entityx      25 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 25);
-})
-
-BENCHMARK("[50]     entityx      50 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 50);
-})
-
-BENCHMARK("[100]    entityx     100 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 100);
-})
-
-BENCHMARK("[200]    entityx     200 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 200);
-})
-
-
-BENCHMARK("[400]    entityx     400 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 400);
-})
-
-
-BENCHMARK("[800]    entityx     800 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 800);
-})
-
-
-BENCHMARK("[1600]    entityx   1600 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 1600);
-})
-
-
-
-BENCHMARK("[3200]    entityx   3200 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 3200);
-})
-
-
-BENCHMARK("[5000]    entityx   5000 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 5000);
-})
-
-
-BENCHMARK("[10000]   entityx  10000 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 10'000);
-})
-
-BENCHMARK("[30000]   entityx  30000 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 30'000);
-})
-
-
-BENCHMARK("[100000]  entityx 100000 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 100'000L);
-})
-
-
-BENCHMARK("[500000]  entityx 500000 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 500'000L);
-})
-
-BENCHMARK("[1000000] entityx     1M entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 1'000'000L);
-})
-
-BENCHMARK("[2000000] entityx     2M entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 2'000'000L);
-})
-*/
diff --git a/benchpress.hpp b/benchpress.hpp
deleted file mode 100644 (file)
index cb1bff4..0000000
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
-* Copyright (C) 2015 Christopher Gilbert.
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy
-* of this software and associated documentation files (the "Software"), to deal
-* in the Software without restriction, including without limitation the rights
-* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-* copies of the Software, and to permit persons to whom the Software is
-* furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all
-* copies or substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-* SOFTWARE.
-*/
-#ifndef BENCHPRESS_HPP
-#define BENCHPRESS_HPP
-
-#include <algorithm>   // max, min
-#include <atomic>      // atomic_intmax_t
-#include <chrono>      // high_resolution_timer, duration
-#include <functional>  // function
-#include <iomanip>     // setw
-#include <iostream>    // cout
-#include <regex>       // regex, regex_match
-#include <sstream>     // stringstream
-#include <string>      // string
-#include <thread>      // thread
-#include <vector>      // vector
-
-namespace benchpress {
-
-/*
- * The options class encapsulates all options for running benchmarks.
- *
- * When including benchpress, a main function can be emitted which includes a command-line parser for building an
- * options object. However from time-to-time it may be necessary for the developer to have to build their own main
- * stub and construct the options object manually.
- *
- * options opts;
- * opts
- *     .bench(".*")
- *     .benchtime(1)
- *     .cpu(4);
- */
-class options {
-    std::string d_bench;
-    size_t      d_benchtime;
-    size_t      d_cpu;
-public:
-    options()
-        : d_bench(".*")
-        , d_benchtime(1)
-        , d_cpu(std::thread::hardware_concurrency())
-    {}
-    options& bench(const std::string& bench) {
-        d_bench = bench;
-        return *this;
-    }
-    options& benchtime(size_t benchtime) {
-        d_benchtime = benchtime;
-        return *this;
-    }
-    options& cpu(size_t cpu) {
-        d_cpu = cpu;
-        return *this;
-    }
-    std::string get_bench() const {
-        return d_bench;
-    }
-    size_t get_benchtime() const {
-        return d_benchtime;
-    }
-    size_t get_cpu() const {
-        return d_cpu;
-    }
-};
-
-class context;
-
-/*
- * The benchmark_info class is used to store a function name / pointer pair.
- *
- * benchmark_info bi("example", [](benchpress::context* b) {
- *     // benchmark function
- * });
- */
-class benchmark_info {
-    std::string                   d_name;
-    std::function<void(context*)> d_func;
-
-public:
-    benchmark_info(std::string name, std::function<void(context*)> func)
-        : d_name(name)
-        , d_func(func)
-    {}
-
-    std::string                   get_name() const { return d_name; }
-    std::function<void(context*)> get_func() const { return d_func; }
-};
-
-/*
- * The registration class is responsible for providing a single global point of reference for registering
- * benchmark functions.
- *
- * registration::get_ptr()->register_benchmark(info);
- */
-class registration {
-    static registration*        d_this;
-    std::vector<benchmark_info> d_benchmarks;
-
-public:
-    static registration* get_ptr() {
-        if (nullptr == d_this) {
-            d_this = new registration();
-        }
-        return d_this;
-    }
-
-    void register_benchmark(benchmark_info& info) {
-        d_benchmarks.push_back(info);
-    }
-
-    std::vector<benchmark_info> get_benchmarks() { return d_benchmarks; }
-};
-
-/*
- * The auto_register class is a helper used to register benchmarks.
- */
-class auto_register {
-public:
-    auto_register(const std::string& name, std::function<void(context*)> func) {
-        benchmark_info info(name, func);
-        registration::get_ptr()->register_benchmark(info);
-    }
-};
-
-#define CONCAT(x, y) x ## y
-#define CONCAT2(x, y) CONCAT(x, y)
-
-// The BENCHMARK macro is a helper for creating benchmark functions and automatically registering them with the
-// registration class.
-#define BENCHMARK(x, f) benchpress::auto_register CONCAT2(register_, __LINE__)((x), (f));
-
-/*
- * This function can be used to keep variables on the stack that would normally be optimised away
- * by the compiler, without introducing any additional instructions or changing the behaviour of
- * the program.
- * 
- * This function uses the Extended Asm syntax of GCC. The volatile keyword indicates that the 
- * following instructions have some unknowable side-effect, and ensures that the code will neither 
- * be moved, nor optimised away.
- *
- * AssemblerTemplate: No operands.
- *
- * OutputOperands: None.
- *
- * InputOperands: The "g" is a wildcard constraint which tells the compiler that it may choose what 
- * to use for p (eg. a register OR a memory reference).
- *
- * Clobbers: The "memory" clobber tells the compiler that the assembly code performs reads or writes
- * to the memory pointed to by one of the input parameters.
- *
- * Example usage:
- *  std::vector<int> v;
- *  v.reserve(10);
- *  escape(v.data());
- */
-void escape(void *p) {
-    asm volatile("" : : "g"(p) : "memory");
-}
-
-/*
- * This function can be used to disable the optimiser. It has the effect of creating a read / write
- * memory barrier for the compiler, meaning it does not assume that any values read from memory before
- * the asm remain unchanged after that asm; it reloads them as needed.
- *
- * Example usage:
- *  std::vector<int> v;
- *  v.reserve(10);
- *  escape(v.data());
- *  v.push_back(42);
- *  clobber(); // Ensure the integer pushed is read
- */
-void clobber() {
-    asm volatile("" : : : "memory");
-}
-
-/*
- * The result class is responsible for producing a printable string representation of a benchmark run.
- */
-class result {
-    size_t                   d_num_iterations;
-    std::chrono::nanoseconds d_duration;
-    size_t                   d_num_bytes;
-
-public:
-    result(size_t num_iterations, std::chrono::nanoseconds duration, size_t num_bytes)
-        : d_num_iterations(num_iterations)
-        , d_duration(duration)
-        , d_num_bytes(num_bytes)
-    {}
-
-    size_t get_ns_per_op() const {
-        if (d_num_iterations <= 0) {
-            return 0;
-        }
-        return d_duration.count() / d_num_iterations;
-    }
-
-    double get_mb_per_s() const {
-        if (d_num_iterations <= 0 || d_duration.count() <= 0 || d_num_bytes <= 0) {
-            return 0;
-        }
-        return ((double(d_num_bytes) * double(d_num_iterations) / double(1e6)) /
-                double(std::chrono::duration_cast<std::chrono::seconds>(d_duration).count()));
-    }
-
-    std::string to_string() const {
-        std::stringstream tmp;
-        tmp << std::setw(12) << std::right << d_num_iterations;
-        size_t npo = get_ns_per_op();
-        tmp << std::setw(12) << std::right << npo << std::setw(0) << " ns/op";
-        double mbs = get_mb_per_s();
-        if (mbs > 0.0) {
-            tmp << std::setw(12) << std::right << mbs << std::setw(0) << " MB/s";
-        }
-        return std::string(tmp.str());
-    }
-};
-
-/*
- * The parallel_context class is responsible for providing a thread-safe context for parallel benchmark code.
- */
-class parallel_context {
-    std::atomic_intmax_t d_num_iterations;
-public:
-    parallel_context(size_t num_iterations)
-        : d_num_iterations(num_iterations)
-    {}
-
-    bool next() {
-        return (d_num_iterations.fetch_sub(1) > 0);
-    }
-};
-
-/*
- * The context class is responsible for providing an interface for capturing benchmark metrics to benchmark functions.
- */
-class context {
-    bool                                           d_timer_on;
-    std::chrono::high_resolution_clock::time_point d_start;
-    std::chrono::nanoseconds                       d_duration;
-    std::chrono::seconds                           d_benchtime;
-    size_t                                         d_num_iterations;
-    size_t                                         d_num_threads;
-    size_t                                         d_num_bytes;
-    benchmark_info                                 d_benchmark;
-
-public:
-    context(const benchmark_info& info, const options& opts)
-        : d_timer_on(false)
-        , d_start()
-        , d_duration()
-        , d_benchtime(std::chrono::seconds(opts.get_benchtime()))
-        , d_num_iterations(1)
-        , d_num_threads(opts.get_cpu())
-        , d_num_bytes(0)
-        , d_benchmark(info)
-    {}
-
-    size_t num_iterations() const { return d_num_iterations; }
-
-    void set_num_threads(size_t n) { d_num_threads = n; }
-    size_t num_threads() const { return d_num_threads; }
-
-    void start_timer() {
-        if (!d_timer_on) {
-            d_start = std::chrono::high_resolution_clock::now();
-            d_timer_on = true;
-        }
-    }
-    void stop_timer() {
-        if (d_timer_on) {
-            d_duration += std::chrono::high_resolution_clock::now() - d_start;
-            d_timer_on = false;
-        }
-    }
-    void reset_timer() {
-        if (d_timer_on) {
-            d_start = std::chrono::high_resolution_clock::now();
-        }
-        d_duration = std::chrono::nanoseconds::zero();
-    }
-
-    void set_bytes(int64_t bytes) { d_num_bytes = bytes; }
-
-    size_t get_ns_per_op() {
-        if (d_num_iterations <= 0) {
-            return 0;
-        }
-        return d_duration.count() / d_num_iterations;
-    }
-
-    void run_n(size_t n) {
-        d_num_iterations = n;
-        reset_timer();
-        start_timer();
-        d_benchmark.get_func()(this);
-        stop_timer();
-    }
-
-    void run_parallel(std::function<void(parallel_context*)> f) {
-        parallel_context pc(d_num_iterations);
-        std::vector<std::thread> threads;
-        for (size_t i = 0; i < d_num_threads; ++i) {
-            threads.push_back(std::thread([&pc,&f]() -> void {
-                f(&pc);
-            }));
-        }
-        for(auto& thread : threads){
-            thread.join();
-        }
-    }
-
-    result run() {
-        size_t n = 1;
-        run_n(n);
-        while (d_duration < d_benchtime && n < 1e9) {
-            size_t last = n;
-            if (get_ns_per_op() == 0) {
-                n = 1e9;
-            } else {
-                n = d_duration.count() / get_ns_per_op();
-            }
-            n = std::max(std::min(n+n/2, 100*last), last+1);
-            n = round_up(n);
-            run_n(n);
-        }
-        return result(n, d_duration, d_num_bytes);
-    }
-
-private:
-    template<typename T>
-    T round_down_10(T n) {
-        int tens = 0;
-        while (n > 10) {
-            n /= 10;
-            tens++;
-        }
-        int result = 1;
-        for (int i = 0; i < tens; ++i) {
-            result *= 10;
-        }
-        return result;
-    }
-
-    template<typename T>
-    T round_up(T n) {
-        T base = round_down_10(n);
-        if (n < (2 * base)) {
-            return 2 * base;
-        }
-        if (n < (5 * base)) {
-            return 5 * base;
-        }
-        return 10 * base;
-    }
-};
-
-/*
- * The run_benchmarks function will run the registered benchmarks.
- */
-void run_benchmarks(const options& opts) {
-    std::regex match_r(opts.get_bench());
-    auto benchmarks = registration::get_ptr()->get_benchmarks();
-    for (auto& info : benchmarks) {
-        if (std::regex_match(info.get_name(), match_r)) {
-            context c(info, opts);
-            auto r = c.run();
-            std::cout << std::setw(35) << std::left << info.get_name() << r.to_string() << std::endl;
-        }
-    }
-}
-
-} // namespace benchpress
-
-/*
- * If BENCHPRESS_CONFIG_MAIN is defined when the file is included then a main function will be emitted which provides a
- * command-line parser and then executes run_benchmarks.
- */
-#ifdef BENCHPRESS_CONFIG_MAIN
-#include "cxxopts.hpp"
-benchpress::registration* benchpress::registration::d_this;
-int main(int argc, char** argv) {
-    std::chrono::high_resolution_clock::time_point bp_start = std::chrono::high_resolution_clock::now();
-    benchpress::options bench_opts;
-    try {
-        cxxopts::Options cmd_opts(argv[0], " - command line options");
-        cmd_opts.add_options()
-            ("bench", "run benchmarks matching the regular expression", cxxopts::value<std::string>()
-                ->default_value(".*"))
-            ("benchtime", "run enough iterations of each benchmark to take t seconds", cxxopts::value<size_t>()
-                ->default_value("1"))
-            ("cpu", "specify the number of threads to use for parallel benchmarks", cxxopts::value<size_t>()
-                ->default_value(std::to_string(std::thread::hardware_concurrency())))
-            ("list", "list all available benchmarks")
-            ("help", "print help")
-        ;
-        cmd_opts.parse(argc, argv);
-        if (cmd_opts.count("help")) {
-            std::cout << cmd_opts.help({""}) << std::endl;
-            exit(0);
-        }
-        if (cmd_opts.count("bench")) {
-            bench_opts.bench(cmd_opts["bench"].as<std::string>());
-        }
-        if (cmd_opts.count("benchtime")) {
-            bench_opts.benchtime(cmd_opts["benchtime"].as<size_t>());
-        }
-        if (cmd_opts.count("cpu")) {
-            bench_opts.cpu(cmd_opts["cpu"].as<size_t>());
-        }
-        if (cmd_opts.count("list")) {
-            auto benchmarks = benchpress::registration::get_ptr()->get_benchmarks();
-            for (auto& info : benchmarks) {
-                std::cout << info.get_name() << std::endl;
-            }
-            exit(EXIT_SUCCESS);
-        }
-    } catch (const cxxopts::OptionException& e) {
-        std::cout << "error parsing options: " << e.what() << std::endl;
-        exit(1);
-    }
-    benchpress::run_benchmarks(bench_opts);
-    float duration = std::chrono::duration_cast<std::chrono::milliseconds>(
-            std::chrono::high_resolution_clock::now() - bp_start
-    ).count() / 1000.f;
-    std::cout << argv[0] << " " << duration << "s" << std::endl;
-    return 0;
-}
-#endif
-
-#endif // BENCHPRESS_HPP
diff --git a/cxxopts.hpp b/cxxopts.hpp
deleted file mode 100644 (file)
index 047190e..0000000
+++ /dev/null
@@ -1,1312 +0,0 @@
-/*
-
-Copyright (c) 2014 Jarryd Beck
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-*/
-
-#ifndef CXX_OPTS_HPP
-#define CXX_OPTS_HPP
-
-#if defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
-#endif
-
-#include <exception>
-#include <iostream>
-#include <map>
-#include <memory>
-#include <regex>
-#include <sstream>
-#include <string>
-#include <vector>
-
-//when we ask cxxopts to use Unicode, help strings are processed using ICU,
-//which results in the correct lengths being computed for strings when they
-//are formatted for the help output
-//it is necessary to make sure that <unicode/unistr.h> can be found by the
-//compiler, and that icu-uc is linked in to the binary.
-
-#ifdef CXXOPTS_USE_UNICODE
-#include <unicode/unistr.h>
-
-namespace cxxopts
-{
-  typedef icu::UnicodeString String;
-
-  inline
-  String
-  toLocalString(std::string s)
-  {
-    return icu::UnicodeString::fromUTF8(s);
-  }
-
-  class UnicodeStringIterator : public
-    std::iterator<std::forward_iterator_tag, int32_t>
-  {
-    public:
-
-    UnicodeStringIterator(const icu::UnicodeString* s, int32_t pos)
-    : s(s)
-    , i(pos)
-    {
-    }
-
-    value_type
-    operator*() const
-    {
-      return s->char32At(i);
-    }
-
-    bool
-    operator==(const UnicodeStringIterator& rhs) const
-    {
-      return s == rhs.s && i == rhs.i;
-    }
-
-    bool
-    operator!=(const UnicodeStringIterator& rhs) const
-    {
-      return !(*this == rhs);
-    }
-
-    UnicodeStringIterator&
-    operator++()
-    {
-      ++i;
-      return *this;
-    }
-
-    UnicodeStringIterator
-    operator+(int32_t v)
-    {
-      return UnicodeStringIterator(s, i + v);
-    }
-
-    private:
-    const icu::UnicodeString* s;
-    int32_t i;
-  };
-
-  inline
-  String&
-  stringAppend(String&s, String a)
-  {
-    return s.append(std::move(a));
-  }
-
-  inline
-  String&
-  stringAppend(String& s, int n, UChar32 c)
-  {
-    for (int i = 0; i != n; ++i)
-    {
-      s.append(c);
-    }
-
-    return s;
-  }
-
-  template <typename Iterator>
-  String&
-  stringAppend(String& s, Iterator begin, Iterator end)
-  {
-    while (begin != end)
-    {
-      s.append(*begin);
-      ++begin;
-    }
-
-    return s;
-  }
-
-  inline
-  size_t
-  stringLength(const String& s)
-  {
-    return s.length();
-  }
-
-  inline
-  std::string
-  toUTF8String(const String& s)
-  {
-    std::string result;
-    s.toUTF8String(result);
-
-    return result;
-  }
-}
-
-namespace std
-{
-  cxxopts::UnicodeStringIterator
-  begin(const icu::UnicodeString& s)
-  {
-    return cxxopts::UnicodeStringIterator(&s, 0);
-  }
-
-  cxxopts::UnicodeStringIterator
-  end(const icu::UnicodeString& s)
-  {
-    return cxxopts::UnicodeStringIterator(&s, s.length());
-  }
-}
-
-//ifdef CXXOPTS_USE_UNICODE
-#else
-
-namespace cxxopts
-{
-  typedef std::string String;
-
-  template <typename T>
-  T
-  toLocalString(T&& t)
-  {
-    return t;
-  }
-
-  inline
-  size_t
-  stringLength(const String& s)
-  {
-    return s.length();
-  }
-
-  inline
-  String&
-  stringAppend(String&s, String a)
-  {
-    return s.append(std::move(a));
-  }
-
-  inline
-  String&
-  stringAppend(String& s, size_t n, char c)
-  {
-    return s.append(n, c);
-  }
-
-  template <typename Iterator>
-  String&
-  stringAppend(String& s, Iterator begin, Iterator end)
-  {
-    return s.append(begin, end);
-  }
-
-  template <typename T>
-  std::string
-  toUTF8String(T&& t)
-  {
-    return std::forward<T>(t);
-  }
-
-}
-
-//ifdef CXXOPTS_USE_UNICODE
-#endif
-
-namespace cxxopts
-{
-  class Value : public std::enable_shared_from_this<Value>
-  {
-    public:
-
-    virtual void
-    parse(const std::string& text) const = 0;
-
-    virtual void
-    parse() const = 0;
-
-    virtual bool
-    has_arg() const = 0;
-
-    virtual bool
-    has_default() const = 0;
-
-    virtual bool
-    has_implicit() const = 0;
-
-    virtual std::string
-    get_default_value() const = 0;
-
-    virtual std::string
-    get_implicit_value() const = 0;
-
-    virtual std::shared_ptr<Value>
-    default_value(const std::string& value) = 0;
-
-    virtual std::shared_ptr<Value>
-    implicit_value(const std::string& value) = 0;
-  };
-
-  class OptionException : public std::exception
-  {
-    public:
-    OptionException(const std::string& message)
-    : m_message(message)
-    {
-    }
-
-    virtual const char*
-    what() const noexcept
-    {
-      return m_message.c_str();
-    }
-
-    private:
-    std::string m_message;
-  };
-
-  class OptionSpecException : public OptionException
-  {
-    public:
-
-    OptionSpecException(const std::string& message)
-    : OptionException(message)
-    {
-    }
-  };
-
-  class OptionParseException : public OptionException
-  {
-    public:
-    OptionParseException(const std::string& message)
-    : OptionException(message)
-    {
-    }
-  };
-
-  class option_exists_error : public OptionSpecException
-  {
-    public:
-    option_exists_error(const std::string& option)
-    : OptionSpecException(u8"Option ?" + option + u8"? already exists")
-    {
-    }
-  };
-
-  class invalid_option_format_error : public OptionSpecException
-  {
-    public:
-    invalid_option_format_error(const std::string& format)
-    : OptionSpecException(u8"Invalid option format ?" + format + u8"?")
-    {
-    }
-  };
-
-  class option_not_exists_exception : public OptionParseException
-  {
-    public:
-    option_not_exists_exception(const std::string& option)
-    : OptionParseException(u8"Option ?" + option + u8"? does not exist")
-    {
-    }
-  };
-
-  class missing_argument_exception : public OptionParseException
-  {
-    public:
-    missing_argument_exception(const std::string& option)
-    : OptionParseException(u8"Option ?" + option + u8"? is missing an argument")
-    {
-    }
-  };
-
-  class option_requires_argument_exception : public OptionParseException
-  {
-    public:
-    option_requires_argument_exception(const std::string& option)
-    : OptionParseException(u8"Option ?" + option + u8"? requires an argument")
-    {
-    }
-  };
-
-  class option_not_has_argument_exception : public OptionParseException
-  {
-    public:
-    option_not_has_argument_exception
-    (
-      const std::string& option,
-      const std::string& arg
-    )
-    : OptionParseException(
-        u8"Option ?" + option + u8"? does not take an argument, but argument?"
-        + arg + "? given")
-    {
-    }
-  };
-
-  class option_not_present_exception : public OptionParseException
-  {
-    public:
-    option_not_present_exception(const std::string& option)
-    : OptionParseException(u8"Option ?" + option + u8"? not present")
-    {
-    }
-  };
-
-  class argument_incorrect_type : public OptionParseException
-  {
-    public:
-    argument_incorrect_type
-    (
-      const std::string& arg
-    )
-    : OptionParseException(
-      u8"Argument ?" + arg + u8"? failed to parse"
-    )
-    {
-    }
-  };
-
-  namespace values
-  {
-    template <typename T>
-    void
-    parse_value(const std::string& text, T& value)
-    {
-      std::istringstream is(text);
-      if (!(is >> value))
-      {
-        std::cerr << "cannot parse empty value" << std::endl;
-        throw argument_incorrect_type(text);
-      }
-
-      if (is.rdbuf()->in_avail() != 0)
-      {
-        throw argument_incorrect_type(text);
-      }
-    }
-
-    template <typename T>
-    void
-    parse_value(const std::string& text, std::vector<T>& value)
-    {
-      T v;
-      parse_value(text, v);
-      value.push_back(v);
-    }
-
-    inline
-    void
-    parse_value(const std::string& /*text*/, bool& value)
-    {
-      //TODO recognise on, off, yes, no, enable, disable
-      //so that we can write --long=yes explicitly
-      value = true;
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, std::string& value)
-    {
-      value = text;
-    }
-
-    template <typename T>
-    struct value_has_arg
-    {
-      static constexpr bool value = true;
-    };
-
-    template <>
-    struct value_has_arg<bool>
-    {
-      static constexpr bool value = false;
-    };
-
-    template <typename T>
-    class standard_value : public Value
-    {
-      public:
-      standard_value()
-      : m_result(std::make_shared<T>())
-      , m_store(m_result.get())
-      {
-      }
-
-      standard_value(T* t)
-      : m_store(t)
-      {
-      }
-
-      void
-      parse(const std::string& text) const
-      {
-        if (m_implicit && text.empty())
-        {
-          parse_value(m_implicit_value, *m_store);
-        }
-        else
-        {
-          parse_value(text, *m_store);
-        }
-      }
-
-      void
-      parse() const
-      {
-        parse_value(m_default_value, *m_store);
-      }
-
-      bool
-      has_arg() const
-      {
-        return value_has_arg<T>::value;
-      }
-
-      bool
-      has_default() const
-      {
-        return m_default;
-      }
-
-      bool
-      has_implicit() const
-      {
-        return m_implicit;
-      }
-
-      virtual std::shared_ptr<Value>
-      default_value(const std::string& value){
-        m_default = true;
-        m_default_value = value;
-        return shared_from_this();
-      }
-
-      virtual std::shared_ptr<Value>
-      implicit_value(const std::string& value){
-        m_implicit = true;
-        m_implicit_value = value;
-        return shared_from_this();
-      }
-
-      std::string
-      get_default_value() const
-      {
-        return m_default_value;
-      }
-
-      std::string
-      get_implicit_value() const
-      {
-        return m_implicit_value;
-      }
-
-      const T&
-      get() const
-      {
-        if (m_store == nullptr)
-        {
-          return *m_result;
-        }
-        else
-        {
-          return *m_store;
-        }
-      }
-
-      protected:
-      std::shared_ptr<T> m_result;
-      T* m_store;
-      bool m_default = false;
-      std::string m_default_value;
-      bool m_implicit = false;
-      std::string m_implicit_value;
-    };
-  }
-
-  template <typename T>
-  std::shared_ptr<Value>
-  value()
-  {
-    return std::make_shared<values::standard_value<T>>();
-  }
-
-  template <typename T>
-  std::shared_ptr<Value>
-  value(T& t)
-  {
-    return std::make_shared<values::standard_value<T>>(&t);
-  }
-
-  class OptionAdder;
-
-  class OptionDetails
-  {
-    public:
-    OptionDetails
-    (
-      const String& description,
-      std::shared_ptr<const Value> value
-    )
-    : m_desc(description)
-    , m_value(value)
-    , m_count(0)
-    {
-    }
-
-    const String&
-    description() const
-    {
-      return m_desc;
-    }
-
-    bool
-    has_arg() const
-    {
-      return m_value->has_arg();
-    }
-
-    void
-    parse(const std::string& text)
-    {
-      m_value->parse(text);
-      ++m_count;
-    }
-
-    void
-    parse_default()
-    {
-      m_value->parse();
-      ++m_count;
-    }
-
-    int
-    count() const
-    {
-      return m_count;
-    }
-
-    const Value& value() const {
-        return *m_value;
-    }
-
-    template <typename T>
-    const T&
-    as() const
-    {
-#ifdef CXXOPTS_NO_RTTI
-      return static_cast<const values::standard_value<T>&>(*m_value).get();
-#else
-      return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
-#endif
-    }
-
-    private:
-    String m_desc;
-    std::shared_ptr<const Value> m_value;
-    int m_count;
-  };
-
-  struct HelpOptionDetails
-  {
-    std::string s;
-    std::string l;
-    String desc;
-    bool has_arg;
-    bool has_default;
-    std::string default_value;
-    bool has_implicit;
-    std::string implicit_value;
-    std::string arg_help;
-  };
-
-  struct HelpGroupDetails
-  {
-    std::string name;
-    std::string description;
-    std::vector<HelpOptionDetails> options;
-  };
-
-  class Options
-  {
-    public:
-
-    Options(std::string program, std::string help_string = "")
-    : m_program(std::move(program))
-    , m_help_string(toLocalString(std::move(help_string)))
-    {
-    }
-
-    inline
-    void
-    parse(int& argc, char**& argv);
-
-    inline
-    OptionAdder
-    add_options(std::string group = "");
-
-    inline
-    void
-    add_option
-    (
-      const std::string& group,
-      const std::string& s,
-      const std::string& l,
-      std::string desc,
-      std::shared_ptr<const Value> value,
-      std::string arg_help
-    );
-
-    int
-    count(const std::string& o) const
-    {
-      auto iter = m_options.find(o);
-      if (iter == m_options.end())
-      {
-        return 0;
-      }
-
-      return iter->second->count();
-    }
-
-    const OptionDetails&
-    operator[](const std::string& option) const
-    {
-      auto iter = m_options.find(option);
-
-      if (iter == m_options.end())
-      {
-        throw option_not_present_exception(option);
-      }
-
-      return *iter->second;
-    }
-
-    //parse positional arguments into the given option
-    inline
-    void
-    parse_positional(std::string option);
-
-    inline
-    std::string
-    help(const std::vector<std::string>& groups = {""}) const;
-
-    inline
-    const std::vector<std::string>
-    groups() const;
-
-    inline
-    const HelpGroupDetails&
-    group_help(const std::string& group) const;
-
-    private:
-
-    inline
-    void
-    add_one_option
-    (
-      const std::string& option,
-      std::shared_ptr<OptionDetails> details
-    );
-
-    inline
-    bool
-    consume_positional(std::string a);
-
-    inline
-    void
-    add_to_option(const std::string& option, const std::string& arg);
-
-    inline
-    void
-    parse_option
-    (
-      std::shared_ptr<OptionDetails> value,
-      const std::string& name,
-      const std::string& arg = ""
-    );
-
-    inline
-    void
-    checked_parse_arg
-    (
-      int argc,
-      char* argv[],
-      int& current,
-      std::shared_ptr<OptionDetails> value,
-      const std::string& name
-    );
-
-    inline
-    String
-    help_one_group(const std::string& group) const;
-
-    std::string m_program;
-    String m_help_string;
-
-    std::map<std::string, std::shared_ptr<OptionDetails>> m_options;
-    std::string m_positional;
-
-    //mapping from groups to help options
-    std::map<std::string, HelpGroupDetails> m_help;
-  };
-
-  class OptionAdder
-  {
-    public:
-
-    OptionAdder(Options& options, std::string group)
-    : m_options(options), m_group(std::move(group))
-    {
-    }
-
-    inline
-    OptionAdder&
-    operator()
-    (
-      const std::string& opts,
-      const std::string& desc,
-      std::shared_ptr<const Value> value
-        = ::cxxopts::value<bool>(),
-      std::string arg_help = ""
-    );
-
-    private:
-    Options& m_options;
-    std::string m_group;
-  };
-
-}
-
-namespace cxxopts
-{
-
-  namespace
-  {
-
-    constexpr int OPTION_LONGEST = 30;
-    constexpr int OPTION_DESC_GAP = 2;
-
-    std::basic_regex<char> option_matcher
-      ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([a-zA-Z]+)");
-
-    std::basic_regex<char> option_specifier
-      ("(([a-zA-Z]),)?([a-zA-Z0-9][-_a-zA-Z0-9]+)");
-
-    String
-    format_option
-    (
-      const HelpOptionDetails& o
-    )
-    {
-      auto& s = o.s;
-      auto& l = o.l;
-
-      String result = "  ";
-
-      if (s.size() > 0)
-      {
-        result += "-" + toLocalString(s) + ",";
-      }
-      else
-      {
-        result += "   ";
-      }
-
-      if (l.size() > 0)
-      {
-        result += " --" + toLocalString(l);
-      }
-
-      if (o.has_arg)
-      {
-        auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";
-
-        if (o.has_implicit)
-        {
-          result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
-        }
-        else
-        {
-          result += " " + arg;
-        }
-      }
-
-      return result;
-    }
-
-    String
-    format_description
-    (
-      const HelpOptionDetails& o,
-      size_t start,
-      size_t width
-    )
-    {
-      auto desc = o.desc;
-
-      if (o.has_default)
-      {
-        desc += toLocalString(" (default:" + o.default_value + ")");
-      }
-
-      String result;
-
-      auto current = std::begin(desc);
-      auto startLine = current;
-      auto lastSpace = current;
-
-      auto size = size_t{};
-
-      while (current != std::end(desc))
-      {
-        if (*current == ' ')
-        {
-          lastSpace = current;
-        }
-
-        if (size > width)
-        {
-          if (lastSpace == startLine)
-          {
-            stringAppend(result, startLine, current + 1);
-            stringAppend(result, "\n");
-            stringAppend(result, start, ' ');
-            startLine = current + 1;
-            lastSpace = startLine;
-          }
-          else
-          {
-            stringAppend(result, startLine, lastSpace);
-            stringAppend(result, "\n");
-            stringAppend(result, start, ' ');
-            startLine = lastSpace + 1;
-          }
-          size = 0;
-        }
-        else
-        {
-          ++size;
-        }
-
-        ++current;
-      }
-
-      //append whatever is left
-      stringAppend(result, startLine, current);
-
-      return result;
-    }
-  }
-
-OptionAdder
-Options::add_options(std::string group)
-{
-  return OptionAdder(*this, std::move(group));
-}
-
-OptionAdder&
-OptionAdder::operator()
-(
-  const std::string& opts,
-  const std::string& desc,
-  std::shared_ptr<const Value> value,
-  std::string arg_help
-)
-{
-  std::match_results<const char*> result;
-  std::regex_match(opts.c_str(), result, option_specifier);
-
-  if (result.empty())
-  {
-    throw invalid_option_format_error(opts);
-  }
-
-  const auto& s = result[2];
-  const auto& l = result[3];
-
-  m_options.add_option(m_group, s.str(), l.str(), desc, value,
-    std::move(arg_help));
-
-  return *this;
-}
-
-void
-Options::parse_option
-(
-  std::shared_ptr<OptionDetails> value,
-  const std::string& /*name*/,
-  const std::string& arg
-)
-{
-  value->parse(arg);
-}
-
-void
-Options::checked_parse_arg
-(
-  int argc,
-  char* argv[],
-  int& current,
-  std::shared_ptr<OptionDetails> value,
-  const std::string& name
-)
-{
-  if (current + 1 >= argc)
-  {
-    if (value->value().has_implicit())
-    {
-      parse_option(value, name, "");
-    }
-    else
-    {
-      throw missing_argument_exception(name);
-    }
-  }
-  else
-  {
-    if (argv[current + 1][0] == '-' && value->value().has_implicit())
-    {
-      parse_option(value, name, "");
-    }
-    else
-    {
-      parse_option(value, name, argv[current + 1]);
-      ++current;
-    }
-  }
-}
-
-void
-Options::add_to_option(const std::string& option, const std::string& arg)
-{
-  auto iter = m_options.find(option);
-
-  if (iter == m_options.end())
-  {
-    throw option_not_exists_exception(option);
-  }
-
-  parse_option(iter->second, option, arg);
-}
-
-bool
-Options::consume_positional(std::string a)
-{
-  if (m_positional.size() > 0)
-  {
-    add_to_option(m_positional, a);
-    return true;
-  }
-  else
-  {
-    return false;
-  }
-}
-
-void
-Options::parse_positional(std::string option)
-{
-  m_positional = std::move(option);
-}
-
-void
-Options::parse(int& argc, char**& argv)
-{
-  int current = 1;
-
-  int nextKeep = 1;
-
-  while (current != argc)
-  {
-    std::match_results<const char*> result;
-    std::regex_match(argv[current], result, option_matcher);
-
-    if (result.empty())
-    {
-      //not a flag
-
-      //if true is returned here then it was consumed, otherwise it is
-      //ignored
-      if (consume_positional(argv[current]))
-      {
-      }
-      else
-      {
-        argv[nextKeep] = argv[current];
-        ++nextKeep;
-      }
-      //if we return from here then it was parsed successfully, so continue
-    }
-    else
-    {
-      //short or long option?
-      if (result[4].length() != 0)
-      {
-        const std::string& s = result[4];
-
-        for (std::size_t i = 0; i != s.size(); ++i)
-        {
-          std::string name(1, s[i]);
-          auto iter = m_options.find(name);
-
-          if (iter == m_options.end())
-          {
-            throw option_not_exists_exception(name);
-          }
-
-          auto value = iter->second;
-
-          //if no argument then just add it
-          if (!value->has_arg())
-          {
-            parse_option(value, name);
-          }
-          else
-          {
-            //it must be the last argument
-            if (i + 1 == s.size())
-            {
-              checked_parse_arg(argc, argv, current, value, name);
-            }
-            else if (value->value().has_implicit())
-            {
-              parse_option(value, name, "");
-            }
-            else
-            {
-              //error
-              throw option_requires_argument_exception(name);
-            }
-          }
-        }
-      }
-      else if (result[1].length() != 0)
-      {
-        const std::string& name = result[1];
-
-        auto iter = m_options.find(name);
-
-        if (iter == m_options.end())
-        {
-          throw option_not_exists_exception(name);
-        }
-
-        auto opt = iter->second;
-
-        //equals provided for long option?
-        if (result[3].length() != 0)
-        {
-          //parse the option given
-
-          //but if it doesn't take an argument, this is an error
-          if (!opt->has_arg())
-          {
-            throw option_not_has_argument_exception(name, result[3]);
-          }
-
-          parse_option(opt, name, result[3]);
-        }
-        else
-        {
-          if (opt->has_arg())
-          {
-            //parse the next argument
-            checked_parse_arg(argc, argv, current, opt, name);
-          }
-          else
-          {
-            //parse with empty argument
-            parse_option(opt, name);
-          }
-        }
-      }
-
-    }
-
-    ++current;
-  }
-
-  for (auto& opt : m_options)
-  {
-    auto& detail = opt.second;
-    auto& value = detail->value();
-
-    if(!detail->count() && value.has_default()){
-      detail->parse_default();
-    }
-  }
-
-  argc = nextKeep;
-}
-
-void
-Options::add_option
-(
-  const std::string& group,
-  const std::string& s,
-  const std::string& l,
-  std::string desc,
-  std::shared_ptr<const Value> value,
-  std::string arg_help
-)
-{
-  auto stringDesc = toLocalString(std::move(desc));
-  auto option = std::make_shared<OptionDetails>(stringDesc, value);
-
-  if (s.size() > 0)
-  {
-    add_one_option(s, option);
-  }
-
-  if (l.size() > 0)
-  {
-    add_one_option(l, option);
-  }
-
-  //add the help details
-  auto& options = m_help[group];
-
-  options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
-      value->has_arg(),
-      value->has_default(), value->get_default_value(),
-      value->has_implicit(), value->get_implicit_value(),
-      std::move(arg_help)});
-}
-
-void
-Options::add_one_option
-(
-  const std::string& option,
-  std::shared_ptr<OptionDetails> details
-)
-{
-  auto in = m_options.emplace(option, details);
-
-  if (!in.second)
-  {
-    throw option_exists_error(option);
-  }
-}
-
-String
-Options::help_one_group(const std::string& g) const
-{
-  typedef std::vector<std::pair<String, String>> OptionHelp;
-
-  auto group = m_help.find(g);
-  if (group == m_help.end())
-  {
-    return "";
-  }
-
-  OptionHelp format;
-
-  size_t longest = 0;
-
-  String result;
-
-  if (!g.empty())
-  {
-    result += toLocalString(" " + g + " options:\n\n");
-  }
-
-  for (const auto& o : group->second.options)
-  {
-    auto s = format_option(o);
-    longest = std::max(longest, stringLength(s));
-    format.push_back(std::make_pair(s, String()));
-  }
-
-  longest = std::min(longest, static_cast<size_t>(OPTION_LONGEST));
-
-  //widest allowed description
-  auto allowed = size_t{76} - longest - OPTION_DESC_GAP;
-
-  auto fiter = format.begin();
-  for (const auto& o : group->second.options)
-  {
-    auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
-
-    result += fiter->first;
-    if (stringLength(fiter->first) > longest)
-    {
-      result += "\n";
-      result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
-    }
-    else
-    {
-      result += toLocalString(std::string(longest + OPTION_DESC_GAP -
-        stringLength(fiter->first),
-        ' '));
-    }
-    result += d;
-    result += "\n";
-
-    ++fiter;
-  }
-
-  return result;
-}
-
-std::string
-Options::help(const std::vector<std::string>& groups) const
-{
-  String result = "Usage:\n  " + toLocalString(m_program) + " [OPTION...]"
-    + m_help_string + "\n\n";
-
-  for (std::size_t i = 0; i < groups.size(); ++i)
-  {
-    result += help_one_group(groups[i]);
-    if (i < groups.size() - 1)
-    {
-      result += "\n";
-    }
-  }
-
-  return toUTF8String(result);
-}
-
-const std::vector<std::string>
-Options::groups() const
-{
-  std::vector<std::string> g;
-
-  std::transform(
-    m_help.begin(),
-    m_help.end(),
-    std::back_inserter(g),
-    [] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
-    {
-      return pair.first;
-    }
-  );
-
-  return g;
-}
-
-const HelpGroupDetails&
-Options::group_help(const std::string& group) const
-{
-  return m_help.at(group);
-}
-
-}
-
-#if defined(__GNU__)
-#pragma GCC diagnostic pop
-#endif
-
-#endif //CXX_OPTS_HPP
diff --git a/entitiesBenchmark.h b/entitiesBenchmark.h
deleted file mode 100644 (file)
index dcf22f1..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-#ifndef ENTITIESBENCHMARK_H_
-#define ENTITIESBENCHMARK_H_
-
-#include <string>
-#include <vector>
-#include <memory>
-#include <random>
-#include <numeric>
-#include <functional>
-
-#include "entities.hpp"
-
-class EntitiesBenchmark {
-    public:
-
-    struct PositionComponent : public Component {
-        float x = 0.0f;
-        float y = 0.0f;
-    };
-
-    struct VelocityComponent : public Component {
-        float x = 0.0f;
-        float y = 0.0f;
-    };
-
-    struct ComflabulationComponent : public Component {
-        float thingy = 0.0;
-        int dingy = 0;
-        bool mingy = false;
-        std::string stringy;
-    };
-
-    class MovementSystem : public System {
-        public:
-        MovementSystem() = default;
-
-        void update(EntityManager &es, DeltaTime dt) {
-                       es.each<PositionComponent, VelocityComponent>(
-                               [dt](Entity e) {
-                                       auto& pos = *e.component<PositionComponent>();
-                                       auto& vel = *e.component<VelocityComponent>();
-                                       pos.x = vel.x * dt;
-                                       pos.y = vel.y * dt;
-                               }
-                       );
-        }
-    };
-
-    class ComflabSystem : public System {
-        public:
-        ComflabSystem() = default;
-
-        void update(EntityManager &es, DeltaTime dt) {
-                       es.each<ComflabulationComponent>(
-                               [dt](Entity e) {
-                                       auto comflab = e.component<ComflabulationComponent>();
-                       comflab->thingy *= 1.000001f;
-                       comflab->mingy = !comflab->mingy;
-                       comflab->dingy++;
-                       //comflab.stringy = std::to_string(comflab.dingy);
-                   }
-                       );
-        }
-    };
-
-    #ifdef USE_MORECOMPLEX_SYSTEM
-    class MoreComplexSystem : public System {
-        private:
-        int random(int min, int max){
-            // Seed with a real random value, if available
-            static std::random_device r;
-        
-            // Choose a random mean between min and max
-            static std::default_random_engine e1(r());
-
-            std::uniform_int_distribution<int> uniform_dist(min, max);
-
-            return uniform_dist(e1);
-        }
-
-        public:
-        MoreComplexSystem() = default;
-
-        void update(EntityManager &es, DeltaTime dt) {
-                       es.each<PositionComponent, DirectionComponent, ComflabulationComponent>(
-                               [dt](Entity e) {
-                                       auto comflab = e.component<ComflabulationComponent>();
-                                       if(comflab) {
-                                               std::vector<double> vec;
-                                               for(size_t i = 0; i < comflab->dingy && i < 100; i++)
-                                                       vec.push_back(i * comflab0>thingy);
-                                               int sum = std::accumulate(vec.begin(), vec.end(), 0);
-                                               int product = std::accumulate(vec.begin(), vec.end(),
-                                                       1, std::multiplies<double>());
-                                               comflab->stringy = std::to_string(comflab->dingy);
-                                               
-                                               auto pos = e.component<PositionComponent>();
-                                               auto vel = e.component<VelocityComponent>();
-                                               if (pos && vel && comflab->dingy % 10000 == 0) {
-                                                       if (pos->x > pos->y) {
-                                                               vel->x = random(0, 5);
-                                                               vel->y = random(0, 10);
-                                                       } else {
-                                                               vel->x = random(0, 10);
-                                                               vel->y = random(0, 5);
-                                                       }
-                                               }
-                                       }
-                               }
-                       );
-        }
-    };
-    #endif
-
-    class Application {
-        public:
-               EntityManager em;
-               SystemManager sm;
-
-        Application() : sm(em) {
-            sm.add<MovementSystem>();
-            sm.add<ComflabSystem>();
-            #ifdef USE_MORECOMPLEX_SYSTEM
-            sm.add<MoreComplexSystem>();
-            #endif
-        }
-
-        void update(DeltaTime dt) {
-            sm.update<MovementSystem>(dt);
-            sm.update<ComflabSystem>(dt);
-            #ifdef USE_MORECOMPLEX_SYSTEM
-            sm.update<MoreComplexSystem>(dt);
-            #endif
-        }
-    };
-
-    static constexpr double fakeDeltaTime = 1.0 / 60;
-};
-
-#endif // ENTITIESBENCHMARK_H_
diff --git a/main.cpp b/main.cpp
deleted file mode 100644 (file)
index 0d77c8d..0000000
--- a/main.cpp
+++ /dev/null
@@ -1,207 +0,0 @@
-#include <iostream>
-#include <string>
-#include <ctime>
-#include <functional>
-#include <iomanip>
-
-#include "entities.hpp"
-
-#include <entityx/entityx.h>
-#include <entityx/deps/Dependencies.h>
-
-using TestFunc = std::function<void(void)>;
-constexpr unsigned int testCount = 100;
-
-void test(const std::string& t, TestFunc ours, TestFunc theirs)
-{
-       auto run = [](auto f) {
-               clock_t avg = 0;
-               for (unsigned int i = 0; i < testCount; i++) {
-                       auto start = clock();
-                       f();
-                       auto diff = clock() - start;
-                       avg += diff;
-               }
-               auto diff = avg / testCount;
-               std::cout << "Test took " <<
-                       diff << " ticks (" << diff / static_cast<double>(CLOCKS_PER_SEC) <<
-                       "ms).\n";
-               return diff;
-       };
-
-       std::cout << std::fixed << std::setprecision(6);
-       std::cout << "==============================================\n";
-       std::cout << "Running test: " << t << "\n";
-
-       std::cout << "Ours:\n";
-       auto o = run(ours);
-
-       std::cout << "Theirs:\n";
-       auto p = run(theirs);
-
-       auto percent = (o - p) / static_cast<float>(p);
-       std::cout << "\nDIFFERENCE: " << o - p << " ticks (" <<
-               std::setprecision(2) << percent << "%).\n\n";
-}
-
-struct Position : public Component {
-       float x, y;
-
-       Position(float _x = 0, float _y = 0)
-               : x(_x), y(_y) {}
-};
-
-struct Velocity : public Component {
-       float x, y;
-       Velocity(float _x = 0, float _y = 0)
-               : x(_x), y(_y) {}
-};
-
-int main(void)
-{
-       EntityManager em;
-
-       entityx::EventManager ev;
-       entityx::EntityManager xm (ev);
-       entityx::SystemManager xsm (xm, ev);
-
-       test("Creation500",
-       [&em](){
-               for (int i = 0; i < 500; i++)
-                       em.create();
-       },
-       [&xm](){
-               for (int i = 0; i < 500; i++)
-                       xm.create();
-       });
-
-       test("Reset",
-       [&em](){
-               em.reset();
-       },
-       [&xm](){
-               xm.reset();
-       });
-
-       test("Assign",
-       [&em](){
-               auto e = em.create();
-               auto& pos = *e.assign<Position>(12, 34);
-
-               //std::cout << pos.x << ' ' << pos.y << '\n';
-               pos.x = 56, pos.y = 16;
-               //std::cout << pos.x << ' ' << pos.y << '\n';
-       },
-       [&xm](){
-               auto e = xm.create();
-               auto pos = e.assign<Position>(12, 34);
-
-               //std::cout << pos->x << ' ' << pos->y << '\n';
-               pos->x = 56, pos->y = 16;
-               //std::cout << pos->x << ' ' << pos->y << '\n';
-
-       });
-
-       test("HasComponent",
-       [&em](){
-               auto e = em.create();
-               //std::cout << e.hasComponent<Position>() << '\n';
-               e.assign<Position>();
-               //std::cout << e.hasComponent<Position>() << '\n';
-       },
-       [&xm](){
-               auto e = xm.create();
-               //std::cout << e.has_component<Position>() << '\n';
-               e.assign<Position>();
-               //std::cout << e.has_component<Position>() << '\n';
-       });
-
-       test("Remove",
-       [&em](){
-               auto e = em.create();   
-               //std::cout << e.hasComponent<Position>() << '\n';
-               e.assign<Position>();
-               //std::cout << e.hasComponent<Position>() << '\n';
-               e.remove<Position>();
-               //std::cout << e.hasComponent<Position>() << '\n';
-       },
-       [&xm](){
-               auto e = xm.create();   
-               //std::cout << e.has_component<Position>() << '\n';
-               e.assign<Position>();
-               //std::cout << e.has_component<Position>() << '\n';
-               e.remove<Position>();
-               //std::cout << e.has_component<Position>() << '\n';
-       });
-
-       /*test("EachAll", [&em](){
-               em.reset();
-               em.create(), em.create(), em.create();
-               int i = 0;
-               em.each([&i](Entity e) {
-                       std::cout << ++i << '\n';
-               });
-       });*/
-
-       test("EachSpec",
-       [&em](){
-               em.reset();
-               em.create(), em.create();
-               auto e1 = em.create(); e1.assign<Position>(1, 3);
-               em.create();
-               auto e2 = em.create(); e2.assign<Position>(99);
-
-               em.each<Position>([](Entity e) {
-                       auto& pos = *e.component<Position>();
-                       //std::cout << pos.x << ' ' << pos.y << '\n';
-               });
-       },
-       [&xm](){
-               xm.reset();
-               xm.create(), xm.create();
-               auto e1 = xm.create(); e1.assign<Position>(1, 3);
-               xm.create();
-               auto e2 = xm.create(); e2.assign<Position>(99);
-
-               xm.each<Position>([](entityx::Entity e, Position& pos) {
-                       //std::cout << pos.x << ' ' << pos.y << '\n';
-               });
-       });
-
-       test("Loop100",
-       [&em](){
-               for (int i = 100; i--;) {
-                       auto e = em.create();
-                       e.assign<Position>(i, i);
-               }
-               em.each<Position>([](Entity e) {
-                       auto& pos = * e.component<Position>();
-                       pos.x += 5, pos.y -= 5;
-               });
-       },
-       [&xm](){
-               for (int i = 100; i--;) {
-                       auto e = xm.create();
-                       e.assign<Position>(i, i);
-               }
-               xm.each<Position>([](entityx::Entity e, Position pos) {
-                       pos.x += 5, pos.y -= 5;
-               });
-       });
-
-       /*test("Dependency",
-       [&em](){
-               em.addDependency<Velocity, Position>();
-               auto e = em.create();
-               e.assign<Velocity>();
-               std::cout << e.hasComponent<Position>() << e.hasComponent<Velocity>() << '\n';
-       },
-       [&xm, &xsm](){
-               xsm.add<entityx::deps::Dependency<Velocity, Position>>();
-               auto e = xm.create();
-               e.assign<Velocity>();
-               std::cout << e.has_component<Position>() << e.has_component<Velocity>() << '\n';
-       });*/
-
-       return 0;
-}
diff --git a/tests/EntityXBenchmark.h b/tests/EntityXBenchmark.h
new file mode 100644 (file)
index 0000000..defa37c
--- /dev/null
@@ -0,0 +1,155 @@
+#ifndef ENTITYXBENCHMARK_H_
+#define ENTITYXBENCHMARK_H_
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <random>
+#include <numeric>
+#include <functional>
+
+#include "entityx/entityx.h"
+
+class EntityXBenchmark {
+    public:
+
+    struct PositionComponent {
+        float x = 0.0f;
+        float y = 0.0f;
+    };
+
+    struct DirectionComponent {
+        float x = 0.0f;
+        float y = 0.0f;
+    };
+
+    struct ComflabulationComponent {
+        float thingy = 0.0;
+        int dingy = 0;
+        bool mingy = false;
+        std::string stringy;
+    };
+
+
+    // Convenience types for our entity system.
+    using Entity = entityx::Entity;
+    using EntityManager = entityx::EventManager;
+    using EventManager = entityx::EventManager;
+
+    template <typename C>
+    using Component = entityx::ComponentHandle<C>;
+
+    using TimeDelta = entityx::TimeDelta;
+
+
+
+    template<class S>
+    using System = entityx::System<S>;
+
+
+    class MovementSystem : public System<MovementSystem> {
+        public:
+        MovementSystem() = default;
+
+        void update(entityx::EntityManager &es, entityx::EventManager &events, entityx::TimeDelta dt) override {
+            Component<PositionComponent> position;
+            Component<DirectionComponent> direction;
+
+            for (auto entity : es.entities_with_components(position, direction)) {
+                position->x += direction->x * dt;
+                position->y += direction->y * dt;
+            }
+        }
+    };
+
+    class ComflabSystem : public System<ComflabSystem> {
+        public:
+        ComflabSystem() = default;
+
+        void update(entityx::EntityManager &es, entityx::EventManager &events, entityx::TimeDelta dt) override {
+            Component<ComflabulationComponent> comflab;
+
+            for (auto entity : es.entities_with_components(comflab)) {
+                comflab->thingy *= 1.000001f;
+                comflab->mingy = !comflab->mingy;
+                comflab->dingy++;
+                //comflab.stringy = std::to_string(comflab.dingy);
+            }
+        }
+    };
+
+    #ifdef USE_MORECOMPLEX_SYSTEM
+    class MoreComplexSystem : public System<MoreComplexSystem> {
+        private:
+        int random(int min, int max){
+            // Seed with a real random value, if available
+            static std::random_device r;
+        
+            // Choose a random mean between min and max
+            static std::default_random_engine e1(r());
+
+            std::uniform_int_distribution<int> uniform_dist(min, max);
+
+            return uniform_dist(e1);
+        }
+
+        public:
+        MoreComplexSystem() = default;
+
+        void update(entityx::EntityManager &es, entityx::EventManager &events, entityx::TimeDelta dt) override {
+            Component<PositionComponent> position;
+            Component<DirectionComponent> direction;
+            Component<ComflabulationComponent> comflab;
+
+            for (auto entity : es.entities_with_components(comflab, direction, comflab)) {
+                if(comflab) {
+                    std::vector<double> vec;
+                    for(size_t i = 0;i < comflab->dingy && i < 100;i++){
+                        vec.push_back(i * comflab->thingy);
+                    }
+
+                    int sum = std::accumulate(std::begin(vec), std::end(vec), 0.0);
+                    int product = std::accumulate(std::begin(vec), std::end(vec), 1, std::multiplies<double>());
+
+                    comflab->stringy = std::to_string(comflab->dingy);
+
+                    if(position && direction && comflab->dingy % 10000 == 0) {
+                        if(position->x > position->y) {
+                            direction->x = random(0, 5);
+                            direction->y = random(0, 10);
+                        } else {
+                            direction->x = random(0, 10);
+                            direction->y = random(0, 5);
+                        }
+                    }
+                }
+            }
+        }
+    };
+    #endif
+
+    class Application : public entityx::EntityX {
+        public:
+        Application() {
+            this->systems.add<MovementSystem>();
+            this->systems.add<ComflabSystem>();
+            #ifdef USE_MORECOMPLEX_SYSTEM
+            this->systems.add<MoreComplexSystem>();
+            #endif
+
+            this->systems.configure();
+        }
+
+        void update(TimeDelta dt) {
+            this->systems.update<MovementSystem>(dt);
+            this->systems.update<ComflabSystem>(dt);
+            #ifdef USE_MORECOMPLEX_SYSTEM
+            this->systems.update<MoreComplexSystem>(dt);
+            #endif
+        }
+    };
+
+    static constexpr double fakeDeltaTime = 1.0 / 60;
+};
+
+#endif // ENTITYXBENCHMARK_H_
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644 (file)
index 0000000..bf69813
--- /dev/null
@@ -0,0 +1,4 @@
+all:
+       g++ -std=c++17 -Wall -Wextra entitiesTests.cpp -o entitiesTests -O1 
+       g++ -std=c++17 -Wall -Wextra entityXTests.cpp  -o entityXTests  -O1 -lentityx
+       
diff --git a/tests/benchpress.hpp b/tests/benchpress.hpp
new file mode 100644 (file)
index 0000000..cb1bff4
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+* Copyright (C) 2015 Christopher Gilbert.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+#ifndef BENCHPRESS_HPP
+#define BENCHPRESS_HPP
+
+#include <algorithm>   // max, min
+#include <atomic>      // atomic_intmax_t
+#include <chrono>      // high_resolution_timer, duration
+#include <functional>  // function
+#include <iomanip>     // setw
+#include <iostream>    // cout
+#include <regex>       // regex, regex_match
+#include <sstream>     // stringstream
+#include <string>      // string
+#include <thread>      // thread
+#include <vector>      // vector
+
+namespace benchpress {
+
+/*
+ * The options class encapsulates all options for running benchmarks.
+ *
+ * When including benchpress, a main function can be emitted which includes a command-line parser for building an
+ * options object. However from time-to-time it may be necessary for the developer to have to build their own main
+ * stub and construct the options object manually.
+ *
+ * options opts;
+ * opts
+ *     .bench(".*")
+ *     .benchtime(1)
+ *     .cpu(4);
+ */
+class options {
+    std::string d_bench;
+    size_t      d_benchtime;
+    size_t      d_cpu;
+public:
+    options()
+        : d_bench(".*")
+        , d_benchtime(1)
+        , d_cpu(std::thread::hardware_concurrency())
+    {}
+    options& bench(const std::string& bench) {
+        d_bench = bench;
+        return *this;
+    }
+    options& benchtime(size_t benchtime) {
+        d_benchtime = benchtime;
+        return *this;
+    }
+    options& cpu(size_t cpu) {
+        d_cpu = cpu;
+        return *this;
+    }
+    std::string get_bench() const {
+        return d_bench;
+    }
+    size_t get_benchtime() const {
+        return d_benchtime;
+    }
+    size_t get_cpu() const {
+        return d_cpu;
+    }
+};
+
+class context;
+
+/*
+ * The benchmark_info class is used to store a function name / pointer pair.
+ *
+ * benchmark_info bi("example", [](benchpress::context* b) {
+ *     // benchmark function
+ * });
+ */
+class benchmark_info {
+    std::string                   d_name;
+    std::function<void(context*)> d_func;
+
+public:
+    benchmark_info(std::string name, std::function<void(context*)> func)
+        : d_name(name)
+        , d_func(func)
+    {}
+
+    std::string                   get_name() const { return d_name; }
+    std::function<void(context*)> get_func() const { return d_func; }
+};
+
+/*
+ * The registration class is responsible for providing a single global point of reference for registering
+ * benchmark functions.
+ *
+ * registration::get_ptr()->register_benchmark(info);
+ */
+class registration {
+    static registration*        d_this;
+    std::vector<benchmark_info> d_benchmarks;
+
+public:
+    static registration* get_ptr() {
+        if (nullptr == d_this) {
+            d_this = new registration();
+        }
+        return d_this;
+    }
+
+    void register_benchmark(benchmark_info& info) {
+        d_benchmarks.push_back(info);
+    }
+
+    std::vector<benchmark_info> get_benchmarks() { return d_benchmarks; }
+};
+
+/*
+ * The auto_register class is a helper used to register benchmarks.
+ */
+class auto_register {
+public:
+    auto_register(const std::string& name, std::function<void(context*)> func) {
+        benchmark_info info(name, func);
+        registration::get_ptr()->register_benchmark(info);
+    }
+};
+
+#define CONCAT(x, y) x ## y
+#define CONCAT2(x, y) CONCAT(x, y)
+
+// The BENCHMARK macro is a helper for creating benchmark functions and automatically registering them with the
+// registration class.
+#define BENCHMARK(x, f) benchpress::auto_register CONCAT2(register_, __LINE__)((x), (f));
+
+/*
+ * This function can be used to keep variables on the stack that would normally be optimised away
+ * by the compiler, without introducing any additional instructions or changing the behaviour of
+ * the program.
+ * 
+ * This function uses the Extended Asm syntax of GCC. The volatile keyword indicates that the 
+ * following instructions have some unknowable side-effect, and ensures that the code will neither 
+ * be moved, nor optimised away.
+ *
+ * AssemblerTemplate: No operands.
+ *
+ * OutputOperands: None.
+ *
+ * InputOperands: The "g" is a wildcard constraint which tells the compiler that it may choose what 
+ * to use for p (eg. a register OR a memory reference).
+ *
+ * Clobbers: The "memory" clobber tells the compiler that the assembly code performs reads or writes
+ * to the memory pointed to by one of the input parameters.
+ *
+ * Example usage:
+ *  std::vector<int> v;
+ *  v.reserve(10);
+ *  escape(v.data());
+ */
+void escape(void *p) {
+    asm volatile("" : : "g"(p) : "memory");
+}
+
+/*
+ * This function can be used to disable the optimiser. It has the effect of creating a read / write
+ * memory barrier for the compiler, meaning it does not assume that any values read from memory before
+ * the asm remain unchanged after that asm; it reloads them as needed.
+ *
+ * Example usage:
+ *  std::vector<int> v;
+ *  v.reserve(10);
+ *  escape(v.data());
+ *  v.push_back(42);
+ *  clobber(); // Ensure the integer pushed is read
+ */
+void clobber() {
+    asm volatile("" : : : "memory");
+}
+
+/*
+ * The result class is responsible for producing a printable string representation of a benchmark run.
+ */
+class result {
+    size_t                   d_num_iterations;
+    std::chrono::nanoseconds d_duration;
+    size_t                   d_num_bytes;
+
+public:
+    result(size_t num_iterations, std::chrono::nanoseconds duration, size_t num_bytes)
+        : d_num_iterations(num_iterations)
+        , d_duration(duration)
+        , d_num_bytes(num_bytes)
+    {}
+
+    size_t get_ns_per_op() const {
+        if (d_num_iterations <= 0) {
+            return 0;
+        }
+        return d_duration.count() / d_num_iterations;
+    }
+
+    double get_mb_per_s() const {
+        if (d_num_iterations <= 0 || d_duration.count() <= 0 || d_num_bytes <= 0) {
+            return 0;
+        }
+        return ((double(d_num_bytes) * double(d_num_iterations) / double(1e6)) /
+                double(std::chrono::duration_cast<std::chrono::seconds>(d_duration).count()));
+    }
+
+    std::string to_string() const {
+        std::stringstream tmp;
+        tmp << std::setw(12) << std::right << d_num_iterations;
+        size_t npo = get_ns_per_op();
+        tmp << std::setw(12) << std::right << npo << std::setw(0) << " ns/op";
+        double mbs = get_mb_per_s();
+        if (mbs > 0.0) {
+            tmp << std::setw(12) << std::right << mbs << std::setw(0) << " MB/s";
+        }
+        return std::string(tmp.str());
+    }
+};
+
+/*
+ * The parallel_context class is responsible for providing a thread-safe context for parallel benchmark code.
+ */
+class parallel_context {
+    std::atomic_intmax_t d_num_iterations;
+public:
+    parallel_context(size_t num_iterations)
+        : d_num_iterations(num_iterations)
+    {}
+
+    bool next() {
+        return (d_num_iterations.fetch_sub(1) > 0);
+    }
+};
+
+/*
+ * The context class is responsible for providing an interface for capturing benchmark metrics to benchmark functions.
+ */
+class context {
+    bool                                           d_timer_on;
+    std::chrono::high_resolution_clock::time_point d_start;
+    std::chrono::nanoseconds                       d_duration;
+    std::chrono::seconds                           d_benchtime;
+    size_t                                         d_num_iterations;
+    size_t                                         d_num_threads;
+    size_t                                         d_num_bytes;
+    benchmark_info                                 d_benchmark;
+
+public:
+    context(const benchmark_info& info, const options& opts)
+        : d_timer_on(false)
+        , d_start()
+        , d_duration()
+        , d_benchtime(std::chrono::seconds(opts.get_benchtime()))
+        , d_num_iterations(1)
+        , d_num_threads(opts.get_cpu())
+        , d_num_bytes(0)
+        , d_benchmark(info)
+    {}
+
+    size_t num_iterations() const { return d_num_iterations; }
+
+    void set_num_threads(size_t n) { d_num_threads = n; }
+    size_t num_threads() const { return d_num_threads; }
+
+    void start_timer() {
+        if (!d_timer_on) {
+            d_start = std::chrono::high_resolution_clock::now();
+            d_timer_on = true;
+        }
+    }
+    void stop_timer() {
+        if (d_timer_on) {
+            d_duration += std::chrono::high_resolution_clock::now() - d_start;
+            d_timer_on = false;
+        }
+    }
+    void reset_timer() {
+        if (d_timer_on) {
+            d_start = std::chrono::high_resolution_clock::now();
+        }
+        d_duration = std::chrono::nanoseconds::zero();
+    }
+
+    void set_bytes(int64_t bytes) { d_num_bytes = bytes; }
+
+    size_t get_ns_per_op() {
+        if (d_num_iterations <= 0) {
+            return 0;
+        }
+        return d_duration.count() / d_num_iterations;
+    }
+
+    void run_n(size_t n) {
+        d_num_iterations = n;
+        reset_timer();
+        start_timer();
+        d_benchmark.get_func()(this);
+        stop_timer();
+    }
+
+    void run_parallel(std::function<void(parallel_context*)> f) {
+        parallel_context pc(d_num_iterations);
+        std::vector<std::thread> threads;
+        for (size_t i = 0; i < d_num_threads; ++i) {
+            threads.push_back(std::thread([&pc,&f]() -> void {
+                f(&pc);
+            }));
+        }
+        for(auto& thread : threads){
+            thread.join();
+        }
+    }
+
+    result run() {
+        size_t n = 1;
+        run_n(n);
+        while (d_duration < d_benchtime && n < 1e9) {
+            size_t last = n;
+            if (get_ns_per_op() == 0) {
+                n = 1e9;
+            } else {
+                n = d_duration.count() / get_ns_per_op();
+            }
+            n = std::max(std::min(n+n/2, 100*last), last+1);
+            n = round_up(n);
+            run_n(n);
+        }
+        return result(n, d_duration, d_num_bytes);
+    }
+
+private:
+    template<typename T>
+    T round_down_10(T n) {
+        int tens = 0;
+        while (n > 10) {
+            n /= 10;
+            tens++;
+        }
+        int result = 1;
+        for (int i = 0; i < tens; ++i) {
+            result *= 10;
+        }
+        return result;
+    }
+
+    template<typename T>
+    T round_up(T n) {
+        T base = round_down_10(n);
+        if (n < (2 * base)) {
+            return 2 * base;
+        }
+        if (n < (5 * base)) {
+            return 5 * base;
+        }
+        return 10 * base;
+    }
+};
+
+/*
+ * The run_benchmarks function will run the registered benchmarks.
+ */
+void run_benchmarks(const options& opts) {
+    std::regex match_r(opts.get_bench());
+    auto benchmarks = registration::get_ptr()->get_benchmarks();
+    for (auto& info : benchmarks) {
+        if (std::regex_match(info.get_name(), match_r)) {
+            context c(info, opts);
+            auto r = c.run();
+            std::cout << std::setw(35) << std::left << info.get_name() << r.to_string() << std::endl;
+        }
+    }
+}
+
+} // namespace benchpress
+
+/*
+ * If BENCHPRESS_CONFIG_MAIN is defined when the file is included then a main function will be emitted which provides a
+ * command-line parser and then executes run_benchmarks.
+ */
+#ifdef BENCHPRESS_CONFIG_MAIN
+#include "cxxopts.hpp"
+benchpress::registration* benchpress::registration::d_this;
+int main(int argc, char** argv) {
+    std::chrono::high_resolution_clock::time_point bp_start = std::chrono::high_resolution_clock::now();
+    benchpress::options bench_opts;
+    try {
+        cxxopts::Options cmd_opts(argv[0], " - command line options");
+        cmd_opts.add_options()
+            ("bench", "run benchmarks matching the regular expression", cxxopts::value<std::string>()
+                ->default_value(".*"))
+            ("benchtime", "run enough iterations of each benchmark to take t seconds", cxxopts::value<size_t>()
+                ->default_value("1"))
+            ("cpu", "specify the number of threads to use for parallel benchmarks", cxxopts::value<size_t>()
+                ->default_value(std::to_string(std::thread::hardware_concurrency())))
+            ("list", "list all available benchmarks")
+            ("help", "print help")
+        ;
+        cmd_opts.parse(argc, argv);
+        if (cmd_opts.count("help")) {
+            std::cout << cmd_opts.help({""}) << std::endl;
+            exit(0);
+        }
+        if (cmd_opts.count("bench")) {
+            bench_opts.bench(cmd_opts["bench"].as<std::string>());
+        }
+        if (cmd_opts.count("benchtime")) {
+            bench_opts.benchtime(cmd_opts["benchtime"].as<size_t>());
+        }
+        if (cmd_opts.count("cpu")) {
+            bench_opts.cpu(cmd_opts["cpu"].as<size_t>());
+        }
+        if (cmd_opts.count("list")) {
+            auto benchmarks = benchpress::registration::get_ptr()->get_benchmarks();
+            for (auto& info : benchmarks) {
+                std::cout << info.get_name() << std::endl;
+            }
+            exit(EXIT_SUCCESS);
+        }
+    } catch (const cxxopts::OptionException& e) {
+        std::cout << "error parsing options: " << e.what() << std::endl;
+        exit(1);
+    }
+    benchpress::run_benchmarks(bench_opts);
+    float duration = std::chrono::duration_cast<std::chrono::milliseconds>(
+            std::chrono::high_resolution_clock::now() - bp_start
+    ).count() / 1000.f;
+    std::cout << argv[0] << " " << duration << "s" << std::endl;
+    return 0;
+}
+#endif
+
+#endif // BENCHPRESS_HPP
diff --git a/tests/cxxopts.hpp b/tests/cxxopts.hpp
new file mode 100644 (file)
index 0000000..047190e
--- /dev/null
@@ -0,0 +1,1312 @@
+/*
+
+Copyright (c) 2014 Jarryd Beck
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+#ifndef CXX_OPTS_HPP
+#define CXX_OPTS_HPP
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+#include <exception>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <regex>
+#include <sstream>
+#include <string>
+#include <vector>
+
+//when we ask cxxopts to use Unicode, help strings are processed using ICU,
+//which results in the correct lengths being computed for strings when they
+//are formatted for the help output
+//it is necessary to make sure that <unicode/unistr.h> can be found by the
+//compiler, and that icu-uc is linked in to the binary.
+
+#ifdef CXXOPTS_USE_UNICODE
+#include <unicode/unistr.h>
+
+namespace cxxopts
+{
+  typedef icu::UnicodeString String;
+
+  inline
+  String
+  toLocalString(std::string s)
+  {
+    return icu::UnicodeString::fromUTF8(s);
+  }
+
+  class UnicodeStringIterator : public
+    std::iterator<std::forward_iterator_tag, int32_t>
+  {
+    public:
+
+    UnicodeStringIterator(const icu::UnicodeString* s, int32_t pos)
+    : s(s)
+    , i(pos)
+    {
+    }
+
+    value_type
+    operator*() const
+    {
+      return s->char32At(i);
+    }
+
+    bool
+    operator==(const UnicodeStringIterator& rhs) const
+    {
+      return s == rhs.s && i == rhs.i;
+    }
+
+    bool
+    operator!=(const UnicodeStringIterator& rhs) const
+    {
+      return !(*this == rhs);
+    }
+
+    UnicodeStringIterator&
+    operator++()
+    {
+      ++i;
+      return *this;
+    }
+
+    UnicodeStringIterator
+    operator+(int32_t v)
+    {
+      return UnicodeStringIterator(s, i + v);
+    }
+
+    private:
+    const icu::UnicodeString* s;
+    int32_t i;
+  };
+
+  inline
+  String&
+  stringAppend(String&s, String a)
+  {
+    return s.append(std::move(a));
+  }
+
+  inline
+  String&
+  stringAppend(String& s, int n, UChar32 c)
+  {
+    for (int i = 0; i != n; ++i)
+    {
+      s.append(c);
+    }
+
+    return s;
+  }
+
+  template <typename Iterator>
+  String&
+  stringAppend(String& s, Iterator begin, Iterator end)
+  {
+    while (begin != end)
+    {
+      s.append(*begin);
+      ++begin;
+    }
+
+    return s;
+  }
+
+  inline
+  size_t
+  stringLength(const String& s)
+  {
+    return s.length();
+  }
+
+  inline
+  std::string
+  toUTF8String(const String& s)
+  {
+    std::string result;
+    s.toUTF8String(result);
+
+    return result;
+  }
+}
+
+namespace std
+{
+  cxxopts::UnicodeStringIterator
+  begin(const icu::UnicodeString& s)
+  {
+    return cxxopts::UnicodeStringIterator(&s, 0);
+  }
+
+  cxxopts::UnicodeStringIterator
+  end(const icu::UnicodeString& s)
+  {
+    return cxxopts::UnicodeStringIterator(&s, s.length());
+  }
+}
+
+//ifdef CXXOPTS_USE_UNICODE
+#else
+
+namespace cxxopts
+{
+  typedef std::string String;
+
+  template <typename T>
+  T
+  toLocalString(T&& t)
+  {
+    return t;
+  }
+
+  inline
+  size_t
+  stringLength(const String& s)
+  {
+    return s.length();
+  }
+
+  inline
+  String&
+  stringAppend(String&s, String a)
+  {
+    return s.append(std::move(a));
+  }
+
+  inline
+  String&
+  stringAppend(String& s, size_t n, char c)
+  {
+    return s.append(n, c);
+  }
+
+  template <typename Iterator>
+  String&
+  stringAppend(String& s, Iterator begin, Iterator end)
+  {
+    return s.append(begin, end);
+  }
+
+  template <typename T>
+  std::string
+  toUTF8String(T&& t)
+  {
+    return std::forward<T>(t);
+  }
+
+}
+
+//ifdef CXXOPTS_USE_UNICODE
+#endif
+
+namespace cxxopts
+{
+  class Value : public std::enable_shared_from_this<Value>
+  {
+    public:
+
+    virtual void
+    parse(const std::string& text) const = 0;
+
+    virtual void
+    parse() const = 0;
+
+    virtual bool
+    has_arg() const = 0;
+
+    virtual bool
+    has_default() const = 0;
+
+    virtual bool
+    has_implicit() const = 0;
+
+    virtual std::string
+    get_default_value() const = 0;
+
+    virtual std::string
+    get_implicit_value() const = 0;
+
+    virtual std::shared_ptr<Value>
+    default_value(const std::string& value) = 0;
+
+    virtual std::shared_ptr<Value>
+    implicit_value(const std::string& value) = 0;
+  };
+
+  class OptionException : public std::exception
+  {
+    public:
+    OptionException(const std::string& message)
+    : m_message(message)
+    {
+    }
+
+    virtual const char*
+    what() const noexcept
+    {
+      return m_message.c_str();
+    }
+
+    private:
+    std::string m_message;
+  };
+
+  class OptionSpecException : public OptionException
+  {
+    public:
+
+    OptionSpecException(const std::string& message)
+    : OptionException(message)
+    {
+    }
+  };
+
+  class OptionParseException : public OptionException
+  {
+    public:
+    OptionParseException(const std::string& message)
+    : OptionException(message)
+    {
+    }
+  };
+
+  class option_exists_error : public OptionSpecException
+  {
+    public:
+    option_exists_error(const std::string& option)
+    : OptionSpecException(u8"Option ?" + option + u8"? already exists")
+    {
+    }
+  };
+
+  class invalid_option_format_error : public OptionSpecException
+  {
+    public:
+    invalid_option_format_error(const std::string& format)
+    : OptionSpecException(u8"Invalid option format ?" + format + u8"?")
+    {
+    }
+  };
+
+  class option_not_exists_exception : public OptionParseException
+  {
+    public:
+    option_not_exists_exception(const std::string& option)
+    : OptionParseException(u8"Option ?" + option + u8"? does not exist")
+    {
+    }
+  };
+
+  class missing_argument_exception : public OptionParseException
+  {
+    public:
+    missing_argument_exception(const std::string& option)
+    : OptionParseException(u8"Option ?" + option + u8"? is missing an argument")
+    {
+    }
+  };
+
+  class option_requires_argument_exception : public OptionParseException
+  {
+    public:
+    option_requires_argument_exception(const std::string& option)
+    : OptionParseException(u8"Option ?" + option + u8"? requires an argument")
+    {
+    }
+  };
+
+  class option_not_has_argument_exception : public OptionParseException
+  {
+    public:
+    option_not_has_argument_exception
+    (
+      const std::string& option,
+      const std::string& arg
+    )
+    : OptionParseException(
+        u8"Option ?" + option + u8"? does not take an argument, but argument?"
+        + arg + "? given")
+    {
+    }
+  };
+
+  class option_not_present_exception : public OptionParseException
+  {
+    public:
+    option_not_present_exception(const std::string& option)
+    : OptionParseException(u8"Option ?" + option + u8"? not present")
+    {
+    }
+  };
+
+  class argument_incorrect_type : public OptionParseException
+  {
+    public:
+    argument_incorrect_type
+    (
+      const std::string& arg
+    )
+    : OptionParseException(
+      u8"Argument ?" + arg + u8"? failed to parse"
+    )
+    {
+    }
+  };
+
+  namespace values
+  {
+    template <typename T>
+    void
+    parse_value(const std::string& text, T& value)
+    {
+      std::istringstream is(text);
+      if (!(is >> value))
+      {
+        std::cerr << "cannot parse empty value" << std::endl;
+        throw argument_incorrect_type(text);
+      }
+
+      if (is.rdbuf()->in_avail() != 0)
+      {
+        throw argument_incorrect_type(text);
+      }
+    }
+
+    template <typename T>
+    void
+    parse_value(const std::string& text, std::vector<T>& value)
+    {
+      T v;
+      parse_value(text, v);
+      value.push_back(v);
+    }
+
+    inline
+    void
+    parse_value(const std::string& /*text*/, bool& value)
+    {
+      //TODO recognise on, off, yes, no, enable, disable
+      //so that we can write --long=yes explicitly
+      value = true;
+    }
+
+    inline
+    void
+    parse_value(const std::string& text, std::string& value)
+    {
+      value = text;
+    }
+
+    template <typename T>
+    struct value_has_arg
+    {
+      static constexpr bool value = true;
+    };
+
+    template <>
+    struct value_has_arg<bool>
+    {
+      static constexpr bool value = false;
+    };
+
+    template <typename T>
+    class standard_value : public Value
+    {
+      public:
+      standard_value()
+      : m_result(std::make_shared<T>())
+      , m_store(m_result.get())
+      {
+      }
+
+      standard_value(T* t)
+      : m_store(t)
+      {
+      }
+
+      void
+      parse(const std::string& text) const
+      {
+        if (m_implicit && text.empty())
+        {
+          parse_value(m_implicit_value, *m_store);
+        }
+        else
+        {
+          parse_value(text, *m_store);
+        }
+      }
+
+      void
+      parse() const
+      {
+        parse_value(m_default_value, *m_store);
+      }
+
+      bool
+      has_arg() const
+      {
+        return value_has_arg<T>::value;
+      }
+
+      bool
+      has_default() const
+      {
+        return m_default;
+      }
+
+      bool
+      has_implicit() const
+      {
+        return m_implicit;
+      }
+
+      virtual std::shared_ptr<Value>
+      default_value(const std::string& value){
+        m_default = true;
+        m_default_value = value;
+        return shared_from_this();
+      }
+
+      virtual std::shared_ptr<Value>
+      implicit_value(const std::string& value){
+        m_implicit = true;
+        m_implicit_value = value;
+        return shared_from_this();
+      }
+
+      std::string
+      get_default_value() const
+      {
+        return m_default_value;
+      }
+
+      std::string
+      get_implicit_value() const
+      {
+        return m_implicit_value;
+      }
+
+      const T&
+      get() const
+      {
+        if (m_store == nullptr)
+        {
+          return *m_result;
+        }
+        else
+        {
+          return *m_store;
+        }
+      }
+
+      protected:
+      std::shared_ptr<T> m_result;
+      T* m_store;
+      bool m_default = false;
+      std::string m_default_value;
+      bool m_implicit = false;
+      std::string m_implicit_value;
+    };
+  }
+
+  template <typename T>
+  std::shared_ptr<Value>
+  value()
+  {
+    return std::make_shared<values::standard_value<T>>();
+  }
+
+  template <typename T>
+  std::shared_ptr<Value>
+  value(T& t)
+  {
+    return std::make_shared<values::standard_value<T>>(&t);
+  }
+
+  class OptionAdder;
+
+  class OptionDetails
+  {
+    public:
+    OptionDetails
+    (
+      const String& description,
+      std::shared_ptr<const Value> value
+    )
+    : m_desc(description)
+    , m_value(value)
+    , m_count(0)
+    {
+    }
+
+    const String&
+    description() const
+    {
+      return m_desc;
+    }
+
+    bool
+    has_arg() const
+    {
+      return m_value->has_arg();
+    }
+
+    void
+    parse(const std::string& text)
+    {
+      m_value->parse(text);
+      ++m_count;
+    }
+
+    void
+    parse_default()
+    {
+      m_value->parse();
+      ++m_count;
+    }
+
+    int
+    count() const
+    {
+      return m_count;
+    }
+
+    const Value& value() const {
+        return *m_value;
+    }
+
+    template <typename T>
+    const T&
+    as() const
+    {
+#ifdef CXXOPTS_NO_RTTI
+      return static_cast<const values::standard_value<T>&>(*m_value).get();
+#else
+      return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
+#endif
+    }
+
+    private:
+    String m_desc;
+    std::shared_ptr<const Value> m_value;
+    int m_count;
+  };
+
+  struct HelpOptionDetails
+  {
+    std::string s;
+    std::string l;
+    String desc;
+    bool has_arg;
+    bool has_default;
+    std::string default_value;
+    bool has_implicit;
+    std::string implicit_value;
+    std::string arg_help;
+  };
+
+  struct HelpGroupDetails
+  {
+    std::string name;
+    std::string description;
+    std::vector<HelpOptionDetails> options;
+  };
+
+  class Options
+  {
+    public:
+
+    Options(std::string program, std::string help_string = "")
+    : m_program(std::move(program))
+    , m_help_string(toLocalString(std::move(help_string)))
+    {
+    }
+
+    inline
+    void
+    parse(int& argc, char**& argv);
+
+    inline
+    OptionAdder
+    add_options(std::string group = "");
+
+    inline
+    void
+    add_option
+    (
+      const std::string& group,
+      const std::string& s,
+      const std::string& l,
+      std::string desc,
+      std::shared_ptr<const Value> value,
+      std::string arg_help
+    );
+
+    int
+    count(const std::string& o) const
+    {
+      auto iter = m_options.find(o);
+      if (iter == m_options.end())
+      {
+        return 0;
+      }
+
+      return iter->second->count();
+    }
+
+    const OptionDetails&
+    operator[](const std::string& option) const
+    {
+      auto iter = m_options.find(option);
+
+      if (iter == m_options.end())
+      {
+        throw option_not_present_exception(option);
+      }
+
+      return *iter->second;
+    }
+
+    //parse positional arguments into the given option
+    inline
+    void
+    parse_positional(std::string option);
+
+    inline
+    std::string
+    help(const std::vector<std::string>& groups = {""}) const;
+
+    inline
+    const std::vector<std::string>
+    groups() const;
+
+    inline
+    const HelpGroupDetails&
+    group_help(const std::string& group) const;
+
+    private:
+
+    inline
+    void
+    add_one_option
+    (
+      const std::string& option,
+      std::shared_ptr<OptionDetails> details
+    );
+
+    inline
+    bool
+    consume_positional(std::string a);
+
+    inline
+    void
+    add_to_option(const std::string& option, const std::string& arg);
+
+    inline
+    void
+    parse_option
+    (
+      std::shared_ptr<OptionDetails> value,
+      const std::string& name,
+      const std::string& arg = ""
+    );
+
+    inline
+    void
+    checked_parse_arg
+    (
+      int argc,
+      char* argv[],
+      int& current,
+      std::shared_ptr<OptionDetails> value,
+      const std::string& name
+    );
+
+    inline
+    String
+    help_one_group(const std::string& group) const;
+
+    std::string m_program;
+    String m_help_string;
+
+    std::map<std::string, std::shared_ptr<OptionDetails>> m_options;
+    std::string m_positional;
+
+    //mapping from groups to help options
+    std::map<std::string, HelpGroupDetails> m_help;
+  };
+
+  class OptionAdder
+  {
+    public:
+
+    OptionAdder(Options& options, std::string group)
+    : m_options(options), m_group(std::move(group))
+    {
+    }
+
+    inline
+    OptionAdder&
+    operator()
+    (
+      const std::string& opts,
+      const std::string& desc,
+      std::shared_ptr<const Value> value
+        = ::cxxopts::value<bool>(),
+      std::string arg_help = ""
+    );
+
+    private:
+    Options& m_options;
+    std::string m_group;
+  };
+
+}
+
+namespace cxxopts
+{
+
+  namespace
+  {
+
+    constexpr int OPTION_LONGEST = 30;
+    constexpr int OPTION_DESC_GAP = 2;
+
+    std::basic_regex<char> option_matcher
+      ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([a-zA-Z]+)");
+
+    std::basic_regex<char> option_specifier
+      ("(([a-zA-Z]),)?([a-zA-Z0-9][-_a-zA-Z0-9]+)");
+
+    String
+    format_option
+    (
+      const HelpOptionDetails& o
+    )
+    {
+      auto& s = o.s;
+      auto& l = o.l;
+
+      String result = "  ";
+
+      if (s.size() > 0)
+      {
+        result += "-" + toLocalString(s) + ",";
+      }
+      else
+      {
+        result += "   ";
+      }
+
+      if (l.size() > 0)
+      {
+        result += " --" + toLocalString(l);
+      }
+
+      if (o.has_arg)
+      {
+        auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";
+
+        if (o.has_implicit)
+        {
+          result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
+        }
+        else
+        {
+          result += " " + arg;
+        }
+      }
+
+      return result;
+    }
+
+    String
+    format_description
+    (
+      const HelpOptionDetails& o,
+      size_t start,
+      size_t width
+    )
+    {
+      auto desc = o.desc;
+
+      if (o.has_default)
+      {
+        desc += toLocalString(" (default:" + o.default_value + ")");
+      }
+
+      String result;
+
+      auto current = std::begin(desc);
+      auto startLine = current;
+      auto lastSpace = current;
+
+      auto size = size_t{};
+
+      while (current != std::end(desc))
+      {
+        if (*current == ' ')
+        {
+          lastSpace = current;
+        }
+
+        if (size > width)
+        {
+          if (lastSpace == startLine)
+          {
+            stringAppend(result, startLine, current + 1);
+            stringAppend(result, "\n");
+            stringAppend(result, start, ' ');
+            startLine = current + 1;
+            lastSpace = startLine;
+          }
+          else
+          {
+            stringAppend(result, startLine, lastSpace);
+            stringAppend(result, "\n");
+            stringAppend(result, start, ' ');
+            startLine = lastSpace + 1;
+          }
+          size = 0;
+        }
+        else
+        {
+          ++size;
+        }
+
+        ++current;
+      }
+
+      //append whatever is left
+      stringAppend(result, startLine, current);
+
+      return result;
+    }
+  }
+
+OptionAdder
+Options::add_options(std::string group)
+{
+  return OptionAdder(*this, std::move(group));
+}
+
+OptionAdder&
+OptionAdder::operator()
+(
+  const std::string& opts,
+  const std::string& desc,
+  std::shared_ptr<const Value> value,
+  std::string arg_help
+)
+{
+  std::match_results<const char*> result;
+  std::regex_match(opts.c_str(), result, option_specifier);
+
+  if (result.empty())
+  {
+    throw invalid_option_format_error(opts);
+  }
+
+  const auto& s = result[2];
+  const auto& l = result[3];
+
+  m_options.add_option(m_group, s.str(), l.str(), desc, value,
+    std::move(arg_help));
+
+  return *this;
+}
+
+void
+Options::parse_option
+(
+  std::shared_ptr<OptionDetails> value,
+  const std::string& /*name*/,
+  const std::string& arg
+)
+{
+  value->parse(arg);
+}
+
+void
+Options::checked_parse_arg
+(
+  int argc,
+  char* argv[],
+  int& current,
+  std::shared_ptr<OptionDetails> value,
+  const std::string& name
+)
+{
+  if (current + 1 >= argc)
+  {
+    if (value->value().has_implicit())
+    {
+      parse_option(value, name, "");
+    }
+    else
+    {
+      throw missing_argument_exception(name);
+    }
+  }
+  else
+  {
+    if (argv[current + 1][0] == '-' && value->value().has_implicit())
+    {
+      parse_option(value, name, "");
+    }
+    else
+    {
+      parse_option(value, name, argv[current + 1]);
+      ++current;
+    }
+  }
+}
+
+void
+Options::add_to_option(const std::string& option, const std::string& arg)
+{
+  auto iter = m_options.find(option);
+
+  if (iter == m_options.end())
+  {
+    throw option_not_exists_exception(option);
+  }
+
+  parse_option(iter->second, option, arg);
+}
+
+bool
+Options::consume_positional(std::string a)
+{
+  if (m_positional.size() > 0)
+  {
+    add_to_option(m_positional, a);
+    return true;
+  }
+  else
+  {
+    return false;
+  }
+}
+
+void
+Options::parse_positional(std::string option)
+{
+  m_positional = std::move(option);
+}
+
+void
+Options::parse(int& argc, char**& argv)
+{
+  int current = 1;
+
+  int nextKeep = 1;
+
+  while (current != argc)
+  {
+    std::match_results<const char*> result;
+    std::regex_match(argv[current], result, option_matcher);
+
+    if (result.empty())
+    {
+      //not a flag
+
+      //if true is returned here then it was consumed, otherwise it is
+      //ignored
+      if (consume_positional(argv[current]))
+      {
+      }
+      else
+      {
+        argv[nextKeep] = argv[current];
+        ++nextKeep;
+      }
+      //if we return from here then it was parsed successfully, so continue
+    }
+    else
+    {
+      //short or long option?
+      if (result[4].length() != 0)
+      {
+        const std::string& s = result[4];
+
+        for (std::size_t i = 0; i != s.size(); ++i)
+        {
+          std::string name(1, s[i]);
+          auto iter = m_options.find(name);
+
+          if (iter == m_options.end())
+          {
+            throw option_not_exists_exception(name);
+          }
+
+          auto value = iter->second;
+
+          //if no argument then just add it
+          if (!value->has_arg())
+          {
+            parse_option(value, name);
+          }
+          else
+          {
+            //it must be the last argument
+            if (i + 1 == s.size())
+            {
+              checked_parse_arg(argc, argv, current, value, name);
+            }
+            else if (value->value().has_implicit())
+            {
+              parse_option(value, name, "");
+            }
+            else
+            {
+              //error
+              throw option_requires_argument_exception(name);
+            }
+          }
+        }
+      }
+      else if (result[1].length() != 0)
+      {
+        const std::string& name = result[1];
+
+        auto iter = m_options.find(name);
+
+        if (iter == m_options.end())
+        {
+          throw option_not_exists_exception(name);
+        }
+
+        auto opt = iter->second;
+
+        //equals provided for long option?
+        if (result[3].length() != 0)
+        {
+          //parse the option given
+
+          //but if it doesn't take an argument, this is an error
+          if (!opt->has_arg())
+          {
+            throw option_not_has_argument_exception(name, result[3]);
+          }
+
+          parse_option(opt, name, result[3]);
+        }
+        else
+        {
+          if (opt->has_arg())
+          {
+            //parse the next argument
+            checked_parse_arg(argc, argv, current, opt, name);
+          }
+          else
+          {
+            //parse with empty argument
+            parse_option(opt, name);
+          }
+        }
+      }
+
+    }
+
+    ++current;
+  }
+
+  for (auto& opt : m_options)
+  {
+    auto& detail = opt.second;
+    auto& value = detail->value();
+
+    if(!detail->count() && value.has_default()){
+      detail->parse_default();
+    }
+  }
+
+  argc = nextKeep;
+}
+
+void
+Options::add_option
+(
+  const std::string& group,
+  const std::string& s,
+  const std::string& l,
+  std::string desc,
+  std::shared_ptr<const Value> value,
+  std::string arg_help
+)
+{
+  auto stringDesc = toLocalString(std::move(desc));
+  auto option = std::make_shared<OptionDetails>(stringDesc, value);
+
+  if (s.size() > 0)
+  {
+    add_one_option(s, option);
+  }
+
+  if (l.size() > 0)
+  {
+    add_one_option(l, option);
+  }
+
+  //add the help details
+  auto& options = m_help[group];
+
+  options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
+      value->has_arg(),
+      value->has_default(), value->get_default_value(),
+      value->has_implicit(), value->get_implicit_value(),
+      std::move(arg_help)});
+}
+
+void
+Options::add_one_option
+(
+  const std::string& option,
+  std::shared_ptr<OptionDetails> details
+)
+{
+  auto in = m_options.emplace(option, details);
+
+  if (!in.second)
+  {
+    throw option_exists_error(option);
+  }
+}
+
+String
+Options::help_one_group(const std::string& g) const
+{
+  typedef std::vector<std::pair<String, String>> OptionHelp;
+
+  auto group = m_help.find(g);
+  if (group == m_help.end())
+  {
+    return "";
+  }
+
+  OptionHelp format;
+
+  size_t longest = 0;
+
+  String result;
+
+  if (!g.empty())
+  {
+    result += toLocalString(" " + g + " options:\n\n");
+  }
+
+  for (const auto& o : group->second.options)
+  {
+    auto s = format_option(o);
+    longest = std::max(longest, stringLength(s));
+    format.push_back(std::make_pair(s, String()));
+  }
+
+  longest = std::min(longest, static_cast<size_t>(OPTION_LONGEST));
+
+  //widest allowed description
+  auto allowed = size_t{76} - longest - OPTION_DESC_GAP;
+
+  auto fiter = format.begin();
+  for (const auto& o : group->second.options)
+  {
+    auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
+
+    result += fiter->first;
+    if (stringLength(fiter->first) > longest)
+    {
+      result += "\n";
+      result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
+    }
+    else
+    {
+      result += toLocalString(std::string(longest + OPTION_DESC_GAP -
+        stringLength(fiter->first),
+        ' '));
+    }
+    result += d;
+    result += "\n";
+
+    ++fiter;
+  }
+
+  return result;
+}
+
+std::string
+Options::help(const std::vector<std::string>& groups) const
+{
+  String result = "Usage:\n  " + toLocalString(m_program) + " [OPTION...]"
+    + m_help_string + "\n\n";
+
+  for (std::size_t i = 0; i < groups.size(); ++i)
+  {
+    result += help_one_group(groups[i]);
+    if (i < groups.size() - 1)
+    {
+      result += "\n";
+    }
+  }
+
+  return toUTF8String(result);
+}
+
+const std::vector<std::string>
+Options::groups() const
+{
+  std::vector<std::string> g;
+
+  std::transform(
+    m_help.begin(),
+    m_help.end(),
+    std::back_inserter(g),
+    [] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
+    {
+      return pair.first;
+    }
+  );
+
+  return g;
+}
+
+const HelpGroupDetails&
+Options::group_help(const std::string& group) const
+{
+  return m_help.at(group);
+}
+
+}
+
+#if defined(__GNU__)
+#pragma GCC diagnostic pop
+#endif
+
+#endif //CXX_OPTS_HPP
diff --git a/tests/entitiesBenchmark.h b/tests/entitiesBenchmark.h
new file mode 100644 (file)
index 0000000..e046fe3
--- /dev/null
@@ -0,0 +1,140 @@
+#ifndef ENTITIESBENCHMARK_H_
+#define ENTITIESBENCHMARK_H_
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <random>
+#include <numeric>
+#include <functional>
+
+#include "../entities.hpp"
+
+class EntitiesBenchmark {
+    public:
+
+    struct PositionComponent : public Component {
+        float x = 0.0f;
+        float y = 0.0f;
+    };
+
+    struct VelocityComponent : public Component {
+        float x = 0.0f;
+        float y = 0.0f;
+    };
+
+    struct ComflabulationComponent : public Component {
+        float thingy = 0.0;
+        int dingy = 0;
+        bool mingy = false;
+        std::string stringy;
+    };
+
+    class MovementSystem : public System {
+        public:
+        MovementSystem() = default;
+
+        void update(EntityManager &es, DeltaTime dt) {
+                       es.each<PositionComponent, VelocityComponent>(
+                               [dt](Entity e) {
+                                       auto& pos = *e.component<PositionComponent>();
+                                       auto& vel = *e.component<VelocityComponent>();
+                                       pos.x = vel.x * dt;
+                                       pos.y = vel.y * dt;
+                               }
+                       );
+        }
+    };
+
+    class ComflabSystem : public System {
+        public:
+        ComflabSystem() = default;
+
+        void update(EntityManager &es, DeltaTime dt) {
+                       es.each<ComflabulationComponent>(
+                               [dt](Entity e) {
+                                       auto comflab = e.component<ComflabulationComponent>();
+                       comflab->thingy *= 1.000001f;
+                       comflab->mingy = !comflab->mingy;
+                       comflab->dingy++;
+                       //comflab.stringy = std::to_string(comflab.dingy);
+                   }
+                       );
+        }
+    };
+
+    #ifdef USE_MORECOMPLEX_SYSTEM
+    class MoreComplexSystem : public System {
+        private:
+        int random(int min, int max){
+            // Seed with a real random value, if available
+            static std::random_device r;
+        
+            // Choose a random mean between min and max
+            static std::default_random_engine e1(r());
+
+            std::uniform_int_distribution<int> uniform_dist(min, max);
+
+            return uniform_dist(e1);
+        }
+
+        public:
+        MoreComplexSystem() = default;
+
+        void update(EntityManager &es, DeltaTime dt) {
+                       es.each<PositionComponent, DirectionComponent, ComflabulationComponent>(
+                               [dt](Entity e) {
+                                       auto comflab = e.component<ComflabulationComponent>();
+                                       if(comflab) {
+                                               std::vector<double> vec;
+                                               for(size_t i = 0; i < comflab->dingy && i < 100; i++)
+                                                       vec.push_back(i * comflab0>thingy);
+                                               int sum = std::accumulate(vec.begin(), vec.end(), 0);
+                                               int product = std::accumulate(vec.begin(), vec.end(),
+                                                       1, std::multiplies<double>());
+                                               comflab->stringy = std::to_string(comflab->dingy);
+                                               
+                                               auto pos = e.component<PositionComponent>();
+                                               auto vel = e.component<VelocityComponent>();
+                                               if (pos && vel && comflab->dingy % 10000 == 0) {
+                                                       if (pos->x > pos->y) {
+                                                               vel->x = random(0, 5);
+                                                               vel->y = random(0, 10);
+                                                       } else {
+                                                               vel->x = random(0, 10);
+                                                               vel->y = random(0, 5);
+                                                       }
+                                               }
+                                       }
+                               }
+                       );
+        }
+    };
+    #endif
+
+    class Application {
+        public:
+               EntityManager em;
+               SystemManager sm;
+
+        Application() : sm(em) {
+            sm.add<MovementSystem>();
+            sm.add<ComflabSystem>();
+            #ifdef USE_MORECOMPLEX_SYSTEM
+            sm.add<MoreComplexSystem>();
+            #endif
+        }
+
+        void update(DeltaTime dt) {
+            sm.update<MovementSystem>(dt);
+            sm.update<ComflabSystem>(dt);
+            #ifdef USE_MORECOMPLEX_SYSTEM
+            sm.update<MoreComplexSystem>(dt);
+            #endif
+        }
+    };
+
+    static constexpr double fakeDeltaTime = 1.0 / 60;
+};
+
+#endif // ENTITIESBENCHMARK_H_
diff --git a/tests/entitiesTests b/tests/entitiesTests
new file mode 100755 (executable)
index 0000000..719c8e1
Binary files /dev/null and b/tests/entitiesTests differ
diff --git a/tests/entitiesTests.cpp b/tests/entitiesTests.cpp
new file mode 100644 (file)
index 0000000..8ea2b92
--- /dev/null
@@ -0,0 +1,173 @@
+#include <string>
+#include <vector>
+#include <thread>
+#include <memory>
+
+#define BENCHPRESS_CONFIG_MAIN
+#include "benchpress.hpp"
+
+#include "../entities.hpp"
+
+#include "entitiesBenchmark.h"
+
+inline void init_entities(EntityManager& entities, size_t nentities){
+    for (size_t i = 0; i < nentities; i++) {
+               auto entity = entities.create();
+
+               entity.assign<EntitiesBenchmark::PositionComponent>();
+               entity.assign<EntitiesBenchmark::VelocityComponent>();
+
+               if (i % 2) {
+                       entity.assign<EntitiesBenchmark::ComflabulationComponent>();
+               }
+       }
+}
+
+inline void runEntitiesSystemsEntitiesBenchmark(benchpress::context* ctx, size_t nentities) {
+    EntitiesBenchmark::Application app;
+    auto& entities = app.em;
+
+    init_entities(entities, nentities);
+
+    ctx->reset_timer();
+    for (size_t i = 0; i < ctx->num_iterations(); ++i) {
+        app.update(EntitiesBenchmark::fakeDeltaTime);
+    }
+}
+
+
+
+
+BENCHMARK("entities create destroy entity with components", [](benchpress::context* ctx) {
+    EntityManager entities;
+
+    ctx->reset_timer();
+    for (size_t i = 0; i < ctx->num_iterations(); ++i) {
+        auto entity = entities.create();
+
+        entity.assign<EntitiesBenchmark::PositionComponent>();
+               entity.assign<EntitiesBenchmark::VelocityComponent>();
+               entity.assign<EntitiesBenchmark::ComflabulationComponent>();
+
+        entities.kill(entity);
+    }
+})
+
+
+
+
+
+class BenchmarksEntities {
+    public:
+    static const std::vector<int> ENTITIES;
+
+    static inline void makeBenchmarks(std::string name) {
+        makeBenchmarks(name, ENTITIES);
+    }
+    
+    static void makeBenchmarks(std::string name, const std::vector<int>& entities) {
+        for(int nentities : entities) {
+            std::string tag = "[" + std::to_string(nentities) + "]";
+
+            std::stringstream ss;
+            ss << std::right << std::setw(10) << tag << ' ';
+            ss << name << ' ';
+            ss << std::right << std::setw(8) << nentities;
+            ss << " entities component systems update";
+
+            std::string benchmark_name = ss.str();
+            BENCHMARK(benchmark_name, [nentities](benchpress::context* ctx) {
+                runEntitiesSystemsEntitiesBenchmark(ctx, nentities);
+            })
+        }
+    }
+
+    BenchmarksEntities(std::string name){
+        makeBenchmarks(name);
+    }
+};
+const std::vector<int> BenchmarksEntities::ENTITIES = {
+    25, 50, 
+    100, 200, 400, 800, 
+    1600, 3200, 5000, 
+    10'000, 30'000, 
+    100'000, 500'000, 
+    1'000'000, 2'000'000
+};
+
+BenchmarksEntities entitiesBenchmarks ("entities");
+
+
+
+
+
+/*
+BENCHMARK("[25]     entityx      25 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 25);
+})
+
+BENCHMARK("[50]     entityx      50 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 50);
+})
+
+BENCHMARK("[100]    entityx     100 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 100);
+})
+
+BENCHMARK("[200]    entityx     200 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 200);
+})
+
+
+BENCHMARK("[400]    entityx     400 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 400);
+})
+
+
+BENCHMARK("[800]    entityx     800 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 800);
+})
+
+
+BENCHMARK("[1600]    entityx   1600 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 1600);
+})
+
+
+
+BENCHMARK("[3200]    entityx   3200 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 3200);
+})
+
+
+BENCHMARK("[5000]    entityx   5000 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 5000);
+})
+
+
+BENCHMARK("[10000]   entityx  10000 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 10'000);
+})
+
+BENCHMARK("[30000]   entityx  30000 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 30'000);
+})
+
+
+BENCHMARK("[100000]  entityx 100000 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 100'000L);
+})
+
+
+BENCHMARK("[500000]  entityx 500000 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 500'000L);
+})
+
+BENCHMARK("[1000000] entityx     1M entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 1'000'000L);
+})
+
+BENCHMARK("[2000000] entityx     2M entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 2'000'000L);
+})
+*/
diff --git a/tests/entityXTests b/tests/entityXTests
new file mode 100755 (executable)
index 0000000..514d5f4
Binary files /dev/null and b/tests/entityXTests differ
diff --git a/tests/entityXTests.cpp b/tests/entityXTests.cpp
new file mode 100644 (file)
index 0000000..2f814c5
--- /dev/null
@@ -0,0 +1,174 @@
+#include <string>
+#include <vector>
+#include <thread>
+#include <memory>
+
+#define BENCHPRESS_CONFIG_MAIN
+#include "benchpress.hpp"
+
+#include <entityx/entityx.h>
+
+#include "EntityXBenchmark.h"
+
+inline void init_entities(entityx::EntityManager& entities, size_t nentities){
+    for (size_t i = 0; i < nentities; i++) {
+               auto entity = entities.create();
+
+               entity.assign<EntityXBenchmark::PositionComponent>();
+               entity.assign<EntityXBenchmark::DirectionComponent>();
+
+               if (i % 2) {
+                       entity.assign<EntityXBenchmark::ComflabulationComponent>();
+               }
+       }
+}
+
+inline void runEntitiesSystemsEntityXBenchmark(benchpress::context* ctx, size_t nentities) {
+    EntityXBenchmark::Application app;
+    auto& entities = app.entities;
+
+    init_entities(entities, nentities);
+
+    ctx->reset_timer();
+    for (size_t i = 0; i < ctx->num_iterations(); ++i) {
+        app.update(EntityXBenchmark::fakeDeltaTime);
+    }
+}
+
+
+
+
+BENCHMARK("entityx create destroy entity with components", [](benchpress::context* ctx) {
+    entityx::EntityX app;
+    auto& entities = app.entities;
+
+    ctx->reset_timer();
+    for (size_t i = 0; i < ctx->num_iterations(); ++i) {
+        auto entity = entities.create();
+
+        entity.assign<EntityXBenchmark::PositionComponent>();
+               entity.assign<EntityXBenchmark::DirectionComponent>();
+               entity.assign<EntityXBenchmark::ComflabulationComponent>();
+
+        entity.destroy();
+    }
+})
+
+
+
+
+
+class BenchmarksEntityX {
+    public:
+    static const std::vector<int> ENTITIES;
+
+    static inline void makeBenchmarks(std::string name) {
+        makeBenchmarks(name, ENTITIES);
+    }
+    
+    static void makeBenchmarks(std::string name, const std::vector<int>& entities) {
+        for(int nentities : entities) {
+            std::string tag = "[" + std::to_string(nentities) + "]";
+
+            std::stringstream ss;
+            ss << std::right << std::setw(10) << tag << ' ';
+            ss << name << ' ';
+            ss << std::right << std::setw(8) << nentities;
+            ss << " entities component systems update";
+
+            std::string benchmark_name = ss.str();
+            BENCHMARK(benchmark_name, [nentities](benchpress::context* ctx) {
+                runEntitiesSystemsEntityXBenchmark(ctx, nentities);
+            })
+        }
+    }
+
+    BenchmarksEntityX(std::string name){
+        makeBenchmarks(name);
+    }
+};
+const std::vector<int> BenchmarksEntityX::ENTITIES = {
+    25, 50, 
+    100, 200, 400, 800, 
+    1600, 3200, 5000, 
+    10'000, 30'000, 
+    100'000, 500'000, 
+    1'000'000, 2'000'000
+};
+
+BenchmarksEntityX entityxbenchmarks ("entityx");
+
+
+
+
+
+/*
+BENCHMARK("[25]     entityx      25 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 25);
+})
+
+BENCHMARK("[50]     entityx      50 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 50);
+})
+
+BENCHMARK("[100]    entityx     100 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 100);
+})
+
+BENCHMARK("[200]    entityx     200 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 200);
+})
+
+
+BENCHMARK("[400]    entityx     400 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 400);
+})
+
+
+BENCHMARK("[800]    entityx     800 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 800);
+})
+
+
+BENCHMARK("[1600]    entityx   1600 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 1600);
+})
+
+
+
+BENCHMARK("[3200]    entityx   3200 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 3200);
+})
+
+
+BENCHMARK("[5000]    entityx   5000 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 5000);
+})
+
+
+BENCHMARK("[10000]   entityx  10000 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 10'000);
+})
+
+BENCHMARK("[30000]   entityx  30000 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 30'000);
+})
+
+
+BENCHMARK("[100000]  entityx 100000 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 100'000L);
+})
+
+
+BENCHMARK("[500000]  entityx 500000 entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 500'000L);
+})
+
+BENCHMARK("[1000000] entityx     1M entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 1'000'000L);
+})
+
+BENCHMARK("[2000000] entityx     2M entities component systems update", [](benchpress::context* ctx) {
+    runEntitiesSystemsEntityXBenchmark(ctx, 2'000'000L);
+})
+*/
diff --git a/xtest.cpp b/xtest.cpp
deleted file mode 100644 (file)
index 2f814c5..0000000
--- a/xtest.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-#include <string>
-#include <vector>
-#include <thread>
-#include <memory>
-
-#define BENCHPRESS_CONFIG_MAIN
-#include "benchpress.hpp"
-
-#include <entityx/entityx.h>
-
-#include "EntityXBenchmark.h"
-
-inline void init_entities(entityx::EntityManager& entities, size_t nentities){
-    for (size_t i = 0; i < nentities; i++) {
-               auto entity = entities.create();
-
-               entity.assign<EntityXBenchmark::PositionComponent>();
-               entity.assign<EntityXBenchmark::DirectionComponent>();
-
-               if (i % 2) {
-                       entity.assign<EntityXBenchmark::ComflabulationComponent>();
-               }
-       }
-}
-
-inline void runEntitiesSystemsEntityXBenchmark(benchpress::context* ctx, size_t nentities) {
-    EntityXBenchmark::Application app;
-    auto& entities = app.entities;
-
-    init_entities(entities, nentities);
-
-    ctx->reset_timer();
-    for (size_t i = 0; i < ctx->num_iterations(); ++i) {
-        app.update(EntityXBenchmark::fakeDeltaTime);
-    }
-}
-
-
-
-
-BENCHMARK("entityx create destroy entity with components", [](benchpress::context* ctx) {
-    entityx::EntityX app;
-    auto& entities = app.entities;
-
-    ctx->reset_timer();
-    for (size_t i = 0; i < ctx->num_iterations(); ++i) {
-        auto entity = entities.create();
-
-        entity.assign<EntityXBenchmark::PositionComponent>();
-               entity.assign<EntityXBenchmark::DirectionComponent>();
-               entity.assign<EntityXBenchmark::ComflabulationComponent>();
-
-        entity.destroy();
-    }
-})
-
-
-
-
-
-class BenchmarksEntityX {
-    public:
-    static const std::vector<int> ENTITIES;
-
-    static inline void makeBenchmarks(std::string name) {
-        makeBenchmarks(name, ENTITIES);
-    }
-    
-    static void makeBenchmarks(std::string name, const std::vector<int>& entities) {
-        for(int nentities : entities) {
-            std::string tag = "[" + std::to_string(nentities) + "]";
-
-            std::stringstream ss;
-            ss << std::right << std::setw(10) << tag << ' ';
-            ss << name << ' ';
-            ss << std::right << std::setw(8) << nentities;
-            ss << " entities component systems update";
-
-            std::string benchmark_name = ss.str();
-            BENCHMARK(benchmark_name, [nentities](benchpress::context* ctx) {
-                runEntitiesSystemsEntityXBenchmark(ctx, nentities);
-            })
-        }
-    }
-
-    BenchmarksEntityX(std::string name){
-        makeBenchmarks(name);
-    }
-};
-const std::vector<int> BenchmarksEntityX::ENTITIES = {
-    25, 50, 
-    100, 200, 400, 800, 
-    1600, 3200, 5000, 
-    10'000, 30'000, 
-    100'000, 500'000, 
-    1'000'000, 2'000'000
-};
-
-BenchmarksEntityX entityxbenchmarks ("entityx");
-
-
-
-
-
-/*
-BENCHMARK("[25]     entityx      25 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 25);
-})
-
-BENCHMARK("[50]     entityx      50 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 50);
-})
-
-BENCHMARK("[100]    entityx     100 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 100);
-})
-
-BENCHMARK("[200]    entityx     200 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 200);
-})
-
-
-BENCHMARK("[400]    entityx     400 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 400);
-})
-
-
-BENCHMARK("[800]    entityx     800 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 800);
-})
-
-
-BENCHMARK("[1600]    entityx   1600 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 1600);
-})
-
-
-
-BENCHMARK("[3200]    entityx   3200 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 3200);
-})
-
-
-BENCHMARK("[5000]    entityx   5000 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 5000);
-})
-
-
-BENCHMARK("[10000]   entityx  10000 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 10'000);
-})
-
-BENCHMARK("[30000]   entityx  30000 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 30'000);
-})
-
-
-BENCHMARK("[100000]  entityx 100000 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 100'000L);
-})
-
-
-BENCHMARK("[500000]  entityx 500000 entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 500'000L);
-})
-
-BENCHMARK("[1000000] entityx     1M entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 1'000'000L);
-})
-
-BENCHMARK("[2000000] entityx     2M entities component systems update", [](benchpress::context* ctx) {
-    runEntitiesSystemsEntityXBenchmark(ctx, 2'000'000L);
-})
-*/